window.initBrilloMapa = function (dotNetHelper) {
    document.addEventListener("keydown", function (e) {
        if (e.key && e.key.toLowerCase() === "m") {
            dotNetHelper.invokeMethodAsync("AlternarBrillo");
        }
    });
};

window.initAltInspector = function (dotNetHelper) {
    document.addEventListener("keydown", function (e) {
        if (e.key === "Z" || e.key === "z") dotNetHelper.invokeMethodAsync("SetInspectorActivo", true);
    }, true);

    document.addEventListener("keyup", function (e) {
        if (e.key === "Z" || e.key === "z") dotNetHelper.invokeMethodAsync("SetInspectorActivo", false);
    }, true);

    window.addEventListener("blur", function () {
        dotNetHelper.invokeMethodAsync("SetInspectorActivo", false);
    });
};






// -----------------------------------------------------------------------------
// Leaflet helpers (sin flechas) para RealTimeMap
// -----------------------------------------------------------------------------
(function () {
    function hookLeaflet(L) {
        if (!L || window.__leafletHooked) return;

        // Hook SUPER temprano: constructor del mapa
        const originalInitialize = L.Map && L.Map.prototype && L.Map.prototype.initialize;
        if (typeof originalInitialize === "function") {
            L.Map.prototype.initialize = function (...args) {
                const res = originalInitialize.apply(this, args);
                window.__rtLeafletMap = this; // guardamos el mapa creado
                return res;
            };
        }

        // Hook adicional: factory L.map (por si acaso)
        const originalMapFactory = L.map;
        if (typeof originalMapFactory === "function") {
            L.map = function (...args) {
                const map = originalMapFactory.apply(this, args);
                window.__rtLeafletMap = map;
                return map;
            };
        }

        window.__leafletHooked = true;
        console.log("[rt] Leaflet hook OK");
    }

    // Si Leaflet ya existe:
    if (window.L) {
        hookLeaflet(window.L);
        return;
    }

    // Si Leaflet se asigna más tarde: definimos setter
    let _L = null;
    Object.defineProperty(window, "L", {
        configurable: true,
        enumerable: true,
        get() { return _L; },
        set(v) {
            _L = v;
            // restauramos propiedad normal
            try {
                delete window.L;
                window.L = v;
            } catch { /* ignore */ }

            hookLeaflet(v);
        }
    });
})();


window.rt = window.rt || {};

window.rt.ensureRouteLayer = function () {
    const map = window.__rtLeafletMap;
    if (!map || !window.L) {
        // ayuda para diagnosticar
        // console.log("[rt] no map yet", { hasL: !!window.L, hasMap: !!map });
        return false;
    }

    if (!window.rt._routeLayer) {
        window.rt._routeLayer = window.L.layerGroup().addTo(map);
    }
    if (!window.rt._routeLines) window.rt._routeLines = {};
    return true;
};

window.rt.clearRoutes = function () {
    if (!window.rt.ensureRouteLayer()) return;
    window.rt._routeLayer.clearLayers();
    window.rt._routeLines = {};

    if (window.rt._routeStopsLayer) {
        window.rt._routeStopsLayer.clearLayers();
        window.rt._routeStopGroups = {};
    }
};
window.rt.drawRoute = function (id, coords, options) {
    if (!window.rt.ensureRouteLayer()) return;

    if (window.rt._routeLines[id]) {
        window.rt._routeLayer.removeLayer(window.rt._routeLines[id]);
        delete window.rt._routeLines[id];
    }

    const arrowId = id + "__arrow";
    if (window.rt._routeLines[arrowId]) {
        window.rt._routeLayer.removeLayer(window.rt._routeLines[arrowId]);
        delete window.rt._routeLines[arrowId];
    }

    const color = (options && options.color) || "#000";
    const weight = (options && options.weight) || 4;
    const opacity = (options && options.opacity) || 0.9;
    const dashArray = options && options.dashArray;
    const lineCap = options && options.lineCap;
    const arrowEnd = !!(options && options.arrowEnd);
    const elevationSegments = Array.isArray(options && options.elevationSegments)
        ? options.elevationSegments
        : [];

    const attachRouteClick = function (line) {
        line.on("click", function (e) {
            try {
                if (e && e.originalEvent) {
                    if (e.originalEvent.preventDefault) e.originalEvent.preventDefault();
                    if (e.originalEvent.stopPropagation) e.originalEvent.stopPropagation();
                    if (e.originalEvent.stopImmediatePropagation) e.originalEvent.stopImmediatePropagation();
                }

                if (window.rt._dotNetHelper) {
                    window.rt._dotNetHelper.invokeMethodAsync("OnPreviewItinerarioClick", id);
                }
            } catch (e) { }
        });
    };

    const buildLine = function (lineCoords, lineOpts) {
        const line = window.L.polyline(lineCoords, lineOpts);
        attachRouteClick(line);
        return line;
    };

    let arrowColor = color;
    let hasElevationPaint = false;

    if (elevationSegments.length > 0 && coords && coords.length >= 2) {
        const group = window.L.layerGroup();

        elevationSegments.forEach(function (segment) {
            const segmentCoords = segment && segment.coords;
            if (!Array.isArray(segmentCoords) || segmentCoords.length < 2) return;

            const segmentColor = (segment && segment.color) || color;
            arrowColor = segmentColor;

            const segmentOpts = {
                color: segmentColor,
                weight,
                opacity,
                lineCap: lineCap || "round"
            };
            if (dashArray) segmentOpts.dashArray = dashArray;

            const segmentLine = buildLine(segmentCoords, segmentOpts);
            const slope = segment && typeof segment.slope === "number" ? segment.slope : null;
            const distance = segment && typeof segment.distance === "number" ? segment.distance : null;
            const elevation = segment && typeof segment.elevation === "number" ? segment.elevation : null;
            const slopeText = slope !== null && Number.isFinite(slope)
                ? ((slope > 0 ? "+" : "") + slope.toFixed(1) + " %")
                : (segment && segment.label ? String(segment.label) : "-");
            const distanceText = distance !== null && Number.isFinite(distance)
                ? distance.toFixed(2) + " m"
                : "-";
            const elevationText = elevation !== null && Number.isFinite(elevation)
                ? elevation.toFixed(2) + " m"
                : "-";
            const label = [
                "Pendiente: " + slopeText,
                "Dist. anterior: " + distanceText,
                "Altura: " + elevationText
            ].join("<br>");

            if (label) {
                segmentLine.bindTooltip(label, {
                    sticky: true,
                    direction: "top",
                    opacity: 0.92,
                    className: "rt-elev-tooltip"
                });
            }

            segmentLine.addTo(group);
            hasElevationPaint = true;
        });

        if (hasElevationPaint) {
            group.addTo(window.rt._routeLayer);
            window.rt._routeLines[id] = group;
        }
    }

    if (!hasElevationPaint) {
        const lineOpts = { color, weight, opacity };
        if (dashArray) lineOpts.dashArray = dashArray;
        if (lineCap) lineOpts.lineCap = lineCap;

        const line = buildLine(coords, lineOpts);
        line.addTo(window.rt._routeLayer);
        window.rt._routeLines[id] = line;
    }

    if (arrowEnd && coords && coords.length >= 2) {
        const a = coords[coords.length - 2];
        const b = coords[coords.length - 1];

        // bearing aprox (en grados)
        const lat1 = a[0] * Math.PI / 180, lon1 = a[1] * Math.PI / 180;
        const lat2 = b[0] * Math.PI / 180, lon2 = b[1] * Math.PI / 180;
        const y = Math.sin(lon2 - lon1) * Math.cos(lat2);
        const x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1);
        let brng = Math.atan2(y, x) * 180 / Math.PI;
        brng = (brng + 360) % 360;

        const size = Math.max(14, Math.min(28, weight * 4)); // tamaño ligado al grosor

        const arrowClass = dashArray ? "rt-arrow rt-arrow--dashed" : "rt-arrow";
        const arrowInnerHtml = dashArray ? '<span class="rt-arrow-dash-head"></span>' : '';

        const icon = window.L.divIcon({
            className: "rt-arrow-icon",
            html: `<div class="${arrowClass}" style="
         width:${size}px;height:${size}px;
         transform: rotate(${brng}deg);
         --arrow-color:${arrowColor};
       ">${arrowInnerHtml}</div>`,
            iconSize: [size, size],
            iconAnchor: [size / 2, size / 2]
        });

        const marker = window.L.marker(b, { icon, interactive: false });
        marker.addTo(window.rt._routeLayer);
        window.rt._routeLines[arrowId] = marker;
    }
};

window.rt.fitRoutes = function () {
    const map = window.__rtLeafletMap;
    if (!map || !window.rt._routeLayer) return;

    const bounds = window.rt._routeLayer.getBounds();
    if (bounds && bounds.isValid()) {
        map.fitBounds(bounds, { padding: [30, 30] });
    }
};

window.rt = window.rt || {};

window.rt.setZoomLimits = function (minZoom, maxZoom) {
    const map = window.__rtLeafletMap;
    if (!map || !window.L) return;

    map.options.minZoom = minZoom;
    map.options.maxZoom = maxZoom;
    map.setMinZoom(minZoom);
    map.setMaxZoom(maxZoom);

    map.eachLayer(l => {
        if (!l) return;

        // límites generales
        if (typeof l.setMaxZoom === "function") l.setMaxZoom(maxZoom);
        if (typeof l.setMinZoom === "function") l.setMinZoom(minZoom);

        // ✅ SOLO TileLayer
        if (l instanceof window.L.TileLayer) {
            l.options.maxNativeZoom = 18;  // tiles reales hasta 18
            l.options.maxZoom = maxZoom;  // permitimos 19
            try { l.redraw(); } catch (e) { }
        }
    });

    try { map.invalidateSize(); } catch (e) { }

    if (window.rt.addZoomIndicator) window.rt.addZoomIndicator();
};


window.rt = window.rt || {};

// guardar referencia a Blazor para callbacks JS -> .NET
window.rt.setDotNetHelper = function (dotNetHelper) {
    window.rt._dotNetHelper = dotNetHelper;
};




// -----------------------------------------------------------------------------
// Elev points layer (puntos de elevación + número)
// -----------------------------------------------------------------------------
window.rt = window.rt || {};

window.rt.ensureElevLayer = function () {
    const map = window.__rtLeafletMap;
    if (!map || !window.L) return false;

    if (!window.rt._elevLayer) {
        window.rt._elevLayer = window.L.layerGroup().addTo(map);
    }
    if (!window.rt._elevGroups) window.rt._elevGroups = {};
    return true;
};


window.rt.clearElevPoints = function (id) {
    if (!window.rt.ensureElevLayer()) return;

    if (id) {
        const g = window.rt._elevGroups && window.rt._elevGroups[id];
        if (g) {
            window.rt._elevLayer.removeLayer(g);
            delete window.rt._elevGroups[id];
        }
        return;
    }

    // sin id => limpiar todo
    window.rt._elevLayer.clearLayers();
    window.rt._elevGroups = {};
};

// points: [{ lat, lon, label }]
// options: { radius, opacity, fillOpacity, showLabels, labelEvery }
window.rt.drawElevPoints = function (id, points, options) {
    if (!window.rt.ensureElevLayer()) return;

    // borrar lo anterior con ese id
    if (window.rt._elevGroups[id]) {
        window.rt._elevLayer.removeLayer(window.rt._elevGroups[id]);
        delete window.rt._elevGroups[id];
    }

    const radius = (options && options.radius) || 3;
    const opacity = (options && options.opacity) || 0.9;
    const fillOpacity = (options && options.fillOpacity) || 0.9;

    const showLabels = options && options.showLabels !== false; // default true
    const every = (options && options.labelEvery) || 1;         // default 1
    const showSlopeTooltips = !!(options && options.showSlopeTooltips);
    const baseClassName = (options && options.className) || "";
    const defaultColor = (options && options.color) || "#facc15";

    const group = window.L.layerGroup();

    if (points && points.length) {
        for (let i = 0; i < points.length; i++) {
            const p = points[i];
            const pointColor = (p && p.color) || defaultColor;
            const category = (p && p.category) ? String(p.category) : "";
            const categoryClass = category ? `${baseClassName}--${category}` : "";
            const className = [baseClassName, categoryClass, p && p.className].filter(Boolean).join(" ");

            const m = window.L.circleMarker([p.lat, p.lon], {
                radius: radius,
                weight: 1,
                opacity: opacity,
                fillOpacity: fillOpacity,
                color: pointColor,
                fillColor: pointColor,
                className: className
            });

            if (showLabels && every > 0 && (i % every === 0)) {
                const txt = (p.label != null ? String(p.label) : String(i));

                // -------------------------------------------------------
                // ✅ Evitar que el número pise la ruta:
                // si el tramo local es "vertical" -> etiqueta a un lado
                // si es "horizontal" -> arriba
                // -------------------------------------------------------
                let dir = "top";
                let off = [0, -3];

                const prev = (i > 0) ? points[i - 1] : null;
                const next = (i < points.length - 1) ? points[i + 1] : null;

                const a = prev || p;
                const b = next || p;

                const dLat = Math.abs((b.lat) - (a.lat));
                const dLon = Math.abs((b.lon) - (a.lon));

                // tramo más vertical => poner a la derecha
                if (dLat > dLon) {
                    dir = "right"; // puedes cambiar a "left" si prefieres
                    off = [5, 0]; // separación lateral
                }

                m.bindTooltip(txt, {
                    permanent: true,
                    direction: dir,
                    offset: off,
                    opacity: 0.95,
                    className: "rt-elev-label"
                });
            }
            else if (showSlopeTooltips && p && typeof p.slope === "number" && Number.isFinite(p.slope)) {
                const label = (p.slope > 0 ? "+" : "") + p.slope.toFixed(1) + "%";
                m.bindTooltip(label, {
                    sticky: true,
                    direction: "top",
                    opacity: 0.92,
                    className: "rt-elev-tooltip"
                });
            }

            m.addTo(group);
        }
    }

    group.addTo(window.rt._elevLayer);
    window.rt._elevGroups[id] = group;
};
window.rt = window.rt || {};

window.rt = window.rt || {};

window.rt.addZoomIndicator = function () {
    const map = window.__rtLeafletMap;
    if (!map || !window.L) return;

    // evitar duplicados
    if (window.__rtZoomIndicator) return;

    const zoomContainer = map.zoomControl && map.zoomControl._container;
    if (!zoomContainer) return;

    // botones nativos
    const btnIn = zoomContainer.querySelector(".leaflet-control-zoom-in");
    const btnOut = zoomContainer.querySelector(".leaflet-control-zoom-out");
    if (!btnIn || !btnOut) return;

    // indicador
    const div = window.L.DomUtil.create("div", "leaflet-control-zoom-indicator-inset");
    div.innerText = map.getZoom();

    // que no robe clicks
    window.L.DomEvent.disableClickPropagation(div);
    window.L.DomEvent.disableScrollPropagation(div);

    // ✅ insertarlo ENTRE + y -
    zoomContainer.insertBefore(div, btnOut);

    map.on("zoomend", () => {
        div.innerText = map.getZoom();
    });

    window.__rtZoomIndicator = div;
};







window.rt = window.rt || {};

window.rt.blockLeafletDoubleClick = function () {
    if (window.__rtDblClickBlocked) return; // evitar duplicados
    window.__rtDblClickBlocked = true;

    document.addEventListener("dblclick", function (e) {
        // si el doble click ocurre dentro de un mapa Leaflet, lo anulamos
        const isOnMap = e.target && e.target.closest && e.target.closest(".leaflet-container");
        if (!isOnMap) return;

        e.preventDefault();
        e.stopPropagation();
        // clave: corta el evento incluso para listeners “antes”
        if (e.stopImmediatePropagation) e.stopImmediatePropagation();
        return false;
    }, true); // CAPTURING: lo pillamos antes que Leaflet
};




// -----------------------------------------------------------------------------
// Preview layer (línea completa) - NO toca la ruta actual
// -----------------------------------------------------------------------------
window.rt = window.rt || {};

window.rt.ensurePreviewLayer = function () {
    const map = window.__rtLeafletMap;
    if (!map || !window.L) return false;

    if (!window.rt._previewLayer) {
        window.rt._previewLayer = window.L.layerGroup().addTo(map);
    }
    if (!window.rt._previewLines) window.rt._previewLines = {};
    return true;
};

window.rt.clearPreview = function () {
    if (!window.rt.ensurePreviewLayer()) return;
    window.rt._previewLayer.clearLayers();
    window.rt._previewLines = {};

    // ✅ también limpiar paradas destacadas
    if (window.rt._itStopsLayer) {
        window.rt._itStopsLayer.clearLayers();
        window.rt._itStopGroups = {};
    }
};

window.rt.drawPreviewLine = function (id, coords, options) {
    if (!window.rt.ensurePreviewLayer()) return;

    const empty = !coords || coords.length === 0;
    const arrowId = id + "__arrow";

    // toggle: si ya existe, lo quitamos (línea + flecha(s))
    if (window.rt._previewLines[id]) {
        window.rt._previewLayer.removeLayer(window.rt._previewLines[id]);
        delete window.rt._previewLines[id];

        if (window.rt._previewLines[arrowId]) {
            window.rt._previewLayer.removeLayer(window.rt._previewLines[arrowId]);
            delete window.rt._previewLines[arrowId];
        }

        return;
    }

    if (empty) return;

    const map = window.__rtLeafletMap;

    const color = (options && options.color) || "#60a5fa";
    const weight = (options && options.weight) || 4;
    const opacity = (options && options.opacity) || 0.6;
    const dashArray = options && options.dashArray;
    const arrowEnd = !!(options && options.arrowEnd);
    const arrowClass = dashArray ? "rt-arrow rt-arrow--dashed" : "rt-arrow";
    const arrowInnerHtml = dashArray ? '<span class="rt-arrow-dash-head"></span>' : '';

    const lineOpts = { color, weight, opacity };
    if (dashArray) lineOpts.dashArray = dashArray;

    const line = window.L.polyline(coords, lineOpts);

    // ✅ Hacer la línea clicable y avisar a Blazor
    line.on("click", function (e) {
        try {
            if (e && e.originalEvent) {
                if (e.originalEvent.preventDefault) e.originalEvent.preventDefault();
                if (e.originalEvent.stopPropagation) e.originalEvent.stopPropagation();
                if (e.originalEvent.stopImmediatePropagation) e.originalEvent.stopImmediatePropagation();
            }

            if (window.rt._dotNetHelper) {
                window.rt._dotNetHelper.invokeMethodAsync("OnPreviewItinerarioClick", id);
            }
        } catch (e) { }
    });

    line.addTo(window.rt._previewLayer);
    window.rt._previewLines[id] = line;

    // ✅ Flecha al FINAL
    if (arrowEnd && coords.length >= 2 && map) {
        // pane por encima de las paradas
        if (!map.getPane("rtArrowPane")) {
            const pane = map.createPane("rtArrowPane");
            pane.style.zIndex = 760;            // > rtStopsPane (750)
            pane.style.pointerEvents = "none";  // no roba clicks
        }

        const size = Math.max(14, Math.min(28, weight * 4));

        function addArrow(from, to, storeId) {
            const lat1 = from[0] * Math.PI / 180, lon1 = from[1] * Math.PI / 180;
            const lat2 = to[0] * Math.PI / 180, lon2 = to[1] * Math.PI / 180;

            const y = Math.sin(lon2 - lon1) * Math.cos(lat2);
            const x = Math.cos(lat1) * Math.sin(lat2) -
                Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1);

            let brng = Math.atan2(y, x) * 180 / Math.PI;
            brng = (brng + 360) % 360;

            const icon = window.L.divIcon({
                className: "rt-arrow-icon",
                html: `<div class="${arrowClass}" style="
                    width:${size}px;height:${size}px;
                    transform: rotate(${brng}deg);
                    --arrow-color:${color};
                ">${arrowInnerHtml}</div>`,
                iconSize: [size, size],
                iconAnchor: [size / 2, size / 2]
            });

            const marker = window.L.marker(to, {
                icon,
                pane: "rtArrowPane",
                interactive: false,
                zIndexOffset: 1000
            });

            marker.addTo(window.rt._previewLayer);
            window.rt._previewLines[storeId] = marker;
        }

        addArrow(coords[coords.length - 2], coords[coords.length - 1], arrowId);
    }
};



window.rt = window.rt || {};

window.rt.getZoom = function () {
    const map = window.__rtLeafletMap;
    return map ? map.getZoom() : 14;
};




window.rt = window.rt || {};

// Devuelve true si el click está dentro del círculo (en píxeles)
window.rt.isClickInsideCirclePx = function (clickLat, clickLon, targetLat, targetLon, radiusPx) {
    const map = window.__rtLeafletMap;
    if (!map || !window.L) return false;

    const pClick = map.latLngToContainerPoint([clickLat, clickLon]);
    const pTarget = map.latLngToContainerPoint([targetLat, targetLon]);

    const dx = pClick.x - pTarget.x;
    const dy = pClick.y - pTarget.y;

    return (dx * dx + dy * dy) <= (radiusPx * radiusPx);
};





// -----------------------------------------------------------------------------
// Itinerario stops layer (paradas destacadas) - toggle por id
// -----------------------------------------------------------------------------
window.rt = window.rt || {};

window.rt.ensureItStopsLayer = function () {
    const map = window.__rtLeafletMap;
    if (!map || !window.L) return false;

    // pane por encima para que queden SOBRE las líneas
    if (!map.getPane("rtStopsPane")) {
        const pane = map.createPane("rtStopsPane");
        pane.style.zIndex = 750;
    }

    if (!window.rt._itStopsLayer) {
        window.rt._itStopsLayer = window.L.layerGroup().addTo(map);
    }
    if (!window.rt._itStopGroups) window.rt._itStopGroups = {};
    return true;
};

// stops: [{lat, lon}]
window.rt.toggleItStops = function (id, stops, options) {
    if (!window.rt.ensureItStopsLayer()) return;

    // toggle: si existe => quitar
    if (window.rt._itStopGroups[id]) {
        window.rt._itStopsLayer.removeLayer(window.rt._itStopGroups[id]);
        delete window.rt._itStopGroups[id];
        return;
    }

    const radius = (options && options.radius) || 7;
    const color = (options && options.color) || "#0f172a";
    const fillColor = (options && options.fillColor) || "#22c55e";
    const weight = (options && options.weight) || 2;
    const opacity = (options && options.opacity) || 1.0;
    const fillOpacity = (options && options.fillOpacity) || 1.0;

    const group = window.L.layerGroup();

    if (stops && stops.length) {
        for (let i = 0; i < stops.length; i++) {
            const s = stops[i];

            const m = window.L.circleMarker([s.lat, s.lon], {
                pane: "rtStopsPane",
                radius,
                color,
                weight,
                opacity,
                fillColor,
                fillOpacity,
                interactive: true
            });

            if (s.code && window.rt._dotNetHelper) {
                m.on("click", function (e) {
                    try {
                        if (e && e.originalEvent) {
                            if (e.originalEvent.preventDefault) e.originalEvent.preventDefault();
                            if (e.originalEvent.stopPropagation) e.originalEvent.stopPropagation();
                            if (e.originalEvent.stopImmediatePropagation) e.originalEvent.stopImmediatePropagation();
                        }

                        window.rt._dotNetHelper.invokeMethodAsync("OnHighlightedStopClick", s.code);
                    } catch (err) { }
                });
            }

            m.addTo(group);
        }
    }

    group.addTo(window.rt._itStopsLayer);
    window.rt._itStopGroups[id] = group;
};

// -----------------------------------------------------------------------------
// Route solution stops layer (paradas destacadas de la solución seleccionada)
// -----------------------------------------------------------------------------
window.rt = window.rt || {};

window.rt.ensureRouteStopsLayer = function () {
    const map = window.__rtLeafletMap;
    if (!map || !window.L) return false;

    if (!map.getPane("rtRouteStopsPane")) {
        const pane = map.createPane("rtRouteStopsPane");
        pane.style.zIndex = 752;
        pane.style.pointerEvents = "none";
    }

    if (!window.rt._routeStopsLayer) {
        window.rt._routeStopsLayer = window.L.layerGroup().addTo(map);
    }
    if (!window.rt._routeStopGroups) window.rt._routeStopGroups = {};
    return true;
};

window.rt.drawRouteStops = function (id, stops, options) {
    if (!window.rt.ensureRouteStopsLayer()) return;

    if (window.rt._routeStopGroups[id]) {
        window.rt._routeStopsLayer.removeLayer(window.rt._routeStopGroups[id]);
        delete window.rt._routeStopGroups[id];
    }

    if (!stops || !stops.length) return;

    const radius = (options && options.radius) || 8;
    const color = (options && options.color) || "#0f172a";
    const fillColor = (options && options.fillColor) || "#22c55e";
    const weight = (options && options.weight) || 2;
    const opacity = (options && options.opacity) || 1.0;
    const fillOpacity = (options && options.fillOpacity) || 1.0;

    const group = window.L.layerGroup();

    for (let i = 0; i < stops.length; i++) {
        const s = stops[i];

        const m = window.L.circleMarker([s.lat, s.lon], {
            pane: "rtRouteStopsPane",
            radius,
            color,
            weight,
            opacity,
            fillColor,
            fillOpacity,
            interactive: true
        });

        if (s.code && window.rt._dotNetHelper) {
            m.on("click", function (e) {
                try {
                    if (e && e.originalEvent) {
                        if (e.originalEvent.preventDefault) e.originalEvent.preventDefault();
                        if (e.originalEvent.stopPropagation) e.originalEvent.stopPropagation();
                        if (e.originalEvent.stopImmediatePropagation) e.originalEvent.stopImmediatePropagation();
                    }

                    window.rt._dotNetHelper.invokeMethodAsync("OnHighlightedStopClick", s.code);
                } catch (err) { }
            });
        }

        m.addTo(group);
    }

    group.addTo(window.rt._routeStopsLayer);
    window.rt._routeStopGroups[id] = group;
};

window.rt.clearRouteStops = function (id) {
    if (!window.rt.ensureRouteStopsLayer()) return;

    if (id) {
        const group = window.rt._routeStopGroups && window.rt._routeStopGroups[id];
        if (group) {
            window.rt._routeStopsLayer.removeLayer(group);
            delete window.rt._routeStopGroups[id];
        }
        return;
    }

    window.rt._routeStopsLayer.clearLayers();
    window.rt._routeStopGroups = {};
};


// -----------------------------------------------------------------------------
// Origin / destination draggable markers
// -----------------------------------------------------------------------------
window.rt = window.rt || {};

window.rt.ensureEndpointLayer = function () {
    const map = window.__rtLeafletMap;
    if (!map || !window.L) return false;

    if (!map.getPane("rtEndpointPane")) {
        const pane = map.createPane("rtEndpointPane");
        pane.style.zIndex = 780;
    }

    if (!window.rt._endpointLayer) {
        window.rt._endpointLayer = window.L.layerGroup().addTo(map);
    }
    if (!window.rt._endpointMarkers) window.rt._endpointMarkers = {};
    return true;
};

window.rt.setEndpointMarker = function (kind, lat, lon, options) {
    if (!window.rt.ensureEndpointLayer()) return;

    if (window.rt._endpointMarkers[kind]) {
        window.rt._endpointLayer.removeLayer(window.rt._endpointMarkers[kind]);
        delete window.rt._endpointMarkers[kind];
    }

    if (typeof lat !== "number" || typeof lon !== "number") return;

    const color = (options && options.color) || "#22c55e";
    const title = (options && options.title) || "";
    const cssKind = kind === "destination" ? "destination" : "origin";
    const size = (options && options.size) || 18;

    const icon = window.L.divIcon({
        className: "rt-endpoint-icon",
        html: `<div class="rt-endpoint-marker rt-endpoint-marker--${cssKind}" style="--endpoint-color:${color}; width:${size}px; height:${size}px;"></div>`,
        iconSize: [size, size],
        iconAnchor: [size / 2, size / 2]
    });

    const marker = window.L.marker([lat, lon], {
        pane: "rtEndpointPane",
        draggable: true,
        autoPan: true,
        icon,
        title
    });

    const stopEvent = function (e) {
        if (!e || !e.originalEvent) return;
        if (e.originalEvent.preventDefault) e.originalEvent.preventDefault();
        if (e.originalEvent.stopPropagation) e.originalEvent.stopPropagation();
        if (e.originalEvent.stopImmediatePropagation) e.originalEvent.stopImmediatePropagation();
    };

    marker.on("click", stopEvent);
    marker.on("mousedown", stopEvent);

    marker.on("dragend", function (e) {
        try {
            const pos = e.target.getLatLng();
            if (window.rt._dotNetHelper) {
                window.rt._dotNetHelper.invokeMethodAsync("OnEndpointDragged", kind, pos.lat, pos.lng);
            }
        } catch (err) { }
    });

    marker.addTo(window.rt._endpointLayer);
    window.rt._endpointMarkers[kind] = marker;
};

window.rt.clearEndpointMarkers = function (kind) {
    if (!window.rt.ensureEndpointLayer()) return;

    if (kind) {
        const marker = window.rt._endpointMarkers && window.rt._endpointMarkers[kind];
        if (marker) {
            window.rt._endpointLayer.removeLayer(marker);
            delete window.rt._endpointMarkers[kind];
        }
        return;
    }

    window.rt._endpointLayer.clearLayers();
    window.rt._endpointMarkers = {};
};


window.rt = window.rt || {};

window.rt.scrollToHorarioProximo = function () {
    const el = document.getElementById("horario-proximo-anchor");
    if (!el) return;

    el.scrollIntoView({
        block: "center",
        behavior: "auto"
    });
};

window.rt.initDraggablePopups = function () {
    if (window.rt._draggablePopupsReady) return;

    window.rt._draggablePopupsReady = true;
    window.rt._draggablePopupPositions = window.rt._draggablePopupPositions || {};
    window.rt._draggablePopupZCounter = Number.isFinite(window.rt._draggablePopupZCounter)
        ? window.rt._draggablePopupZCounter
        : 1200;

    const padding = 8;
    const edgePeekX = 28;
    const edgePeekY = 42;

    function dragKey(popup) {
        return popup && (popup.dataset.dragKey || popup.id || "");
    }

    function clamp(value, min, max) {
        if (max < min) return min;
        return Math.min(Math.max(value, min), max);
    }

    function parseZ(value) {
        const parsed = Number.parseInt(value, 10);
        return Number.isFinite(parsed) ? parsed : null;
    }

    function ensurePopupZIndex(popup) {
        if (!popup) return;

        const inlineZ = parseZ(popup.style.zIndex);
        if (inlineZ != null) {
            window.rt._draggablePopupZCounter = Math.max(window.rt._draggablePopupZCounter, inlineZ);
            return;
        }

        const computedZ = parseZ(window.getComputedStyle(popup).zIndex);
        if (computedZ != null) {
            window.rt._draggablePopupZCounter = Math.max(window.rt._draggablePopupZCounter, computedZ);
        }

        window.rt._draggablePopupZCounter += 1;
        popup.style.zIndex = String(window.rt._draggablePopupZCounter);
    }

    function bringPopupToFront(popup) {
        if (!popup) return;

        ensurePopupZIndex(popup);
        window.rt._draggablePopupZCounter += 1;
        popup.style.zIndex = String(window.rt._draggablePopupZCounter);
    }

    function clampPosition(left, top, width, height) {
        return {
            left: clamp(left, edgePeekX - width, window.innerWidth - edgePeekX),
            top: clamp(top, edgePeekY - height, window.innerHeight - edgePeekY)
        };
    }

    function constrainFreeHeight(popup, height) {
        if (!popup || !popup.classList.contains("route-panel-overlay")) return null;

        const maxHeight = Math.max(260, window.innerHeight - (padding * 2));
        return Math.min(height || maxHeight, maxHeight);
    }

    function isHandleRecoverablyHidden(popup) {
        if (!popup) return false;

        const handle = popup.querySelector(".draggable-popup-handle");
        if (!handle) return false;

        const rect = handle.getBoundingClientRect();
        const visibleTop = Math.max(rect.top, 0);
        const visibleBottom = Math.min(rect.bottom, window.innerHeight);
        const visibleHeight = Math.max(0, visibleBottom - visibleTop);

        return visibleHeight < 18;
    }

    function setFreePosition(popup, left, top, width, height) {
        popup.classList.add("draggable-popup--free");
        popup.style.setProperty("--drag-left", `${left}px`);
        popup.style.setProperty("--drag-top", `${top}px`);
        popup.style.right = "auto";
        popup.style.bottom = "auto";
        popup.style.width = `${width}px`;

        const constrainedHeight = constrainFreeHeight(popup, height);
        if (constrainedHeight != null) {
            popup.style.height = `${constrainedHeight}px`;
            popup.style.maxHeight = `${constrainedHeight}px`;
        } else {
            popup.style.removeProperty("height");
            popup.style.removeProperty("max-height");
        }
    }

    function savePosition(popup) {
        const key = dragKey(popup);
        if (!key) return;

        const rect = popup.getBoundingClientRect();
        window.rt._draggablePopupPositions[key] = {
            left: rect.left,
            top: rect.top,
            width: rect.width,
            height: rect.height
        };
    }

    function applySavedPositions() {
        document.querySelectorAll(".draggable-popup").forEach((popup) => {
            const key = dragKey(popup);
            const saved = key ? window.rt._draggablePopupPositions[key] : null;

            if (saved && !popup.classList.contains("draggable-popup--free")) {
                const rect = popup.getBoundingClientRect();
                const width = saved.width || rect.width;
                const height = saved.height || rect.height;
                const pos = clampPosition(saved.left, saved.top, width, height);
                setFreePosition(popup, pos.left, pos.top, width, height);
            }

            ensurePopupZIndex(popup);
        });
    }

    document.addEventListener("pointerdown", function (event) {
        const popup = event.target && event.target.closest
            ? event.target.closest(".draggable-popup")
            : null;
        if (!popup) return;

        bringPopupToFront(popup);

        if (event.button !== 0) return;
        if (event.target.closest("button,a,input,textarea,select,label")) return;

        const handle = event.target && event.target.closest
            ? event.target.closest(".draggable-popup-handle")
            : null;

        const fallbackWholePopup =
            !handle &&
            popup.classList.contains("draggable-popup--free") &&
            isHandleRecoverablyHidden(popup);

        if (!handle && !fallbackWholePopup) return;

        const dragSurface = handle || popup;

        event.preventDefault();

        const rect = popup.getBoundingClientRect();
        const width = rect.width;
        const height = rect.height;
        const startX = event.clientX;
        const startY = event.clientY;
        const startLeft = rect.left;
        const startTop = rect.top;

        const initial = clampPosition(startLeft, startTop, width, height);
        setFreePosition(popup, initial.left, initial.top, width, height);
        popup.classList.add("draggable-popup--dragging");

        try { dragSurface.setPointerCapture(event.pointerId); } catch { }

        function onMove(moveEvent) {
            const pos = clampPosition(
                startLeft + moveEvent.clientX - startX,
                startTop + moveEvent.clientY - startY,
                width,
                height);

            popup.style.setProperty("--drag-left", `${pos.left}px`);
            popup.style.setProperty("--drag-top", `${pos.top}px`);
        }

        function onUp(upEvent) {
            document.removeEventListener("pointermove", onMove, true);
            document.removeEventListener("pointerup", onUp, true);
            document.removeEventListener("pointercancel", onUp, true);

            popup.classList.remove("draggable-popup--dragging");
            savePosition(popup);

            try { dragSurface.releasePointerCapture(upEvent.pointerId); } catch { }
        }

        document.addEventListener("pointermove", onMove, true);
        document.addEventListener("pointerup", onUp, true);
        document.addEventListener("pointercancel", onUp, true);
    }, true);

    document.addEventListener("focusin", function (event) {
        const popup = event.target && event.target.closest
            ? event.target.closest(".draggable-popup")
            : null;
        if (!popup) return;
        bringPopupToFront(popup);
    }, true);

    window.addEventListener("resize", function () {
        document.querySelectorAll(".draggable-popup--free").forEach((popup) => {
            const rect = popup.getBoundingClientRect();
            const pos = clampPosition(rect.left, rect.top, rect.width, rect.height);
            setFreePosition(popup, pos.left, pos.top, rect.width, rect.height);
            savePosition(popup);
        });
    });

    const observer = new MutationObserver(applySavedPositions);
    observer.observe(document.body, { childList: true, subtree: true });
    applySavedPositions();
};

window.rt.saveLocalValue = function (key, value) {
    try {
        if (!window.localStorage) return;
        window.localStorage.setItem(key, value ?? "");
    } catch { }
};

window.rt.loadLocalValue = function (key) {
    try {
        if (!window.localStorage) return null;
        return window.localStorage.getItem(key);
    } catch {
        return null;
    }
};

window.rt.clearLocalBrowserData = async function () {
    const deleteIndexedDb = function (name) {
        return new Promise((resolve) => {
            try {
                const req = window.indexedDB.deleteDatabase(name);
                req.onsuccess = resolve;
                req.onerror = resolve;
                req.onblocked = resolve;
            } catch {
                resolve();
            }
        });
    };

    try { window.localStorage?.clear(); } catch { }
    try { window.sessionStorage?.clear(); } catch { }

    try {
        if (window.caches?.keys) {
            const keys = await window.caches.keys();
            await Promise.all(keys.map((key) => window.caches.delete(key)));
        }
    } catch { }

    try {
        if (navigator.serviceWorker?.getRegistrations) {
            const registrations = await navigator.serviceWorker.getRegistrations();
            await Promise.all(registrations.map((registration) => registration.unregister()));
        }
    } catch { }

    try {
        if (window.indexedDB?.databases) {
            const databases = await window.indexedDB.databases();
            await Promise.all((databases || [])
                .filter((database) => database && database.name)
                .map((database) => deleteIndexedDb(database.name)));
        }
    } catch { }
};

window.rt.reloadPage = function () {
    window.location.reload();
};
