
        /* ── PASO 1.1 — estilo mínimo y neutral a propósito ──────────
           Todavía NO es la identidad visual "cartel de neón" de Viu Map.
           Cimientos primero: que funcione bien, se viste después. */
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body { font-family: 'Inter', -apple-system, 'PingFang SC', 'Microsoft YaHei', 'Noto Sans SC', Arial, sans-serif; color: #222; }
        :root {
            /* ── ⚠️ --viu-acento (verde) NO SE TOCA — Fase 1 del rediseño
               (Plan de trabajo, Sept. 2026). Este valor sigue siendo el
               color de respaldo de los pines de lugares SIN bucket
               reconocido (ver COLOR_POR_BUCKET, más abajo en el JS) y
               también el borde de los pines .pin-negocio-*. Cambiarlo
               cambiaría color de pines del mapa — algo que el cliente
               pidió explícitamente NO tocar en este rediseño. Todo lo
               demás que antes usaba esta variable para BOTONES/UI (no
               pines) fue migrado a --vm-azul/--vm-dorado, más abajo, para
               que ambos usos queden desacoplados de ahora en más. ── */
            --viu-acento: #2e7d32; --viu-oscuro: #1b1b1b;

            /* ══════════════════════════════════════════════════════════
               PALETA VIU MAP — Fase 1 del rediseño gráfico (Sept. 2026).
               Identidad real de marca: la moneda/logo dorado + el azul
               marino de la interfaz de referencia del cliente. Reemplaza,
               para elementos de interfaz (NO para pines de lugares, que
               se quedan como están), el uso disperso de --viu-acento y
               de docenas de colores sueltos por selector.
               — Dorado: protagonista de marca (logo, Viu Coin, acentos
                 puntuales de marca).
               — Azul marino: color estructural de acción (botones
                 primarios, estados seleccionados, foco de formularios).
               — Blanco/gris: superficies y texto neutro.
               — Rojo: acento mínimo, heredado del anillo del logo — uso
                 puntual, no masivo.
               ══════════════════════════════════════════════════════════ */
            --vm-dorado: #D79109;
            --vm-dorado-suave: #FBEBBE;
            --vm-azul: #3E4F86;
            --vm-azul-oscuro: #2E3C68;
            --vm-azul-suave: #E7EAF4;
            --vm-texto: #1B2430;
            /* Migrado desde --qh-fondo (Sesión 68) — mismo valor exacto,
               solo pasa a la convención de nombres de la Fase 1. Fondo
               cálido de página completa (paneles de buckets, "¿Cómo
               vas?"), distinto a --vm-superficie (blanco puro, para
               tarjetas). */
            --vm-fondo-calido: #FFFDF8;
            --vm-texto-suave: #6B7280;
            --vm-superficie: #FFFFFF;
            --vm-borde: #E2E5EA;
            --vm-acento-rojo: #D32F2F;

            /* Escala tipográfica (Fase 1, extendida en la Fase 7.5) —
               reemplaza los 28 tamaños sueltos originales (36 a esta
               altura, con todo lo agregado después). Se suman acá 3
               escalones que no existían en la Fase 1 porque ese
               contenido (números grandes de clima/mar, texto muy chico
               de metadatos) todavía no se había construido. */
            --vm-fs-micro: 10px;
            --vm-fs-caption: 11px;
            --vm-fs-cuerpo: 13px;
            --vm-fs-cuerpo-destacado: 14px;
            --vm-fs-subtitulo: 16px;
            --vm-fs-titulo: 19px;
            --vm-fs-titulo-grande: 24px;
            --vm-fs-hero: 32px;
            --vm-fs-hero-grande: 42px;

            /* Interlineado (Fase 7.5, continuación) — mismo criterio que
               los tamaños: un solo valor para texto informativo (notas,
               descripciones, subtítulos de contacto), más compacto que
               el 1.5 que predominaba antes. Títulos y subtítulos de
               verdad (nombres de negocio, encabezados de ficha) NO usan
               esta variable a propósito — cada uno mantiene el
               interlineado propio que ya tenía, tal como pidió el
               cliente. */
            --vm-lh-cuerpo: 1.4;

            /* Espaciado de texto informativo (Fase 7.5, revisión
               dedicada aparte del interlineado) — 3 roles reales, no un
               solo número: "pegado" para una etiqueta/valor secundario
               justo debajo de lo principal (mismo bloque visual);
               "texto" para una nota o bloque autocontenido separado de
               lo anterior; "accion" para el texto que antecede a un
               botón o campo interactivo, que necesita más aire para no
               leerse como parte del control. Solo aplica a líneas de
               texto puro — botones, cajas con fondo/padding y
               contenedores de layout quedaron fuera a propósito, ahí el
               margen depende del layout, no de ser "informativo". */
            --vm-esp-pegado: 2px;
            --vm-esp-texto: 8px;
            --vm-esp-accion: 14px;
            /* Espacio entre SECCIONES completas de un modal (Sesión 69,
               revisión dedicada de espacios en blanco) — header→hero,
               hero→chips, fila de etiquetas→datos de contacto, etc. Un
               escalón por encima de --vm-esp-texto (que es para texto
               suelto dentro de una misma sección): lo justo para que se
               note el cambio de sección sin generar un vacío. */
            --vm-esp-seccion: 10px;

            /* Escala de radios y sombras (Fase 1) — mismo criterio: se usa
               ya en las piezas de esta fase, el resto del CSS se remapea
               en el siguiente paso. */
            --vm-radio-chip: 8px;
            --vm-radio-boton: 12px;
            --vm-radio-panel: 16px;
            --vm-sombra-flotante: 0 2px 10px rgba(27,36,48,0.18);
            --vm-sombra-panel: 0 8px 24px rgba(27,36,48,0.16);
        }

        /* ── Fase 8 — tipografía de tablet (720px a 1024px) — la escala
           de la Fase 7.5 se diseñó a propósito solo para escritorio; acá
           se reduce un escalón moderado (no tanto como haría falta en
           celular, que queda pendiente aparte) para que el texto no se
           vea como una versión achicada de escritorio en una pantalla
           de ese tamaño intermedio. Nada de layout ni colores acá,
           solo tamaño de letra — el resto de la Fase 8 va en reglas
           aparte, más abajo. ── */
        @media (min-width: 720px) and (max-width: 1024px) {
            :root {
                --vm-fs-micro: 9px;
                --vm-fs-caption: 10px;
                --vm-fs-cuerpo: 12px;
                --vm-fs-cuerpo-destacado: 13px;
                --vm-fs-subtitulo: 15px;
                --vm-fs-titulo: 17px;
                --vm-fs-titulo-grande: 21px;
                --vm-fs-hero: 27px;
                --vm-fs-hero-grande: 35px;
            }
        }

        /* ── Fase 8 — tipografía de celular (< 720px), el hallazgo que
           quedó pendiente al cerrar la parte de tablet: hoy el celular
           usa exactamente los mismos tamaños que escritorio, sin ningún
           ajuste. Criterio distinto al de tablet, no el mismo achicado
           "un poco más": el texto de lectura (cuerpo, captions, texto
           de botones) baja poco — un celular se sostiene cerca de los
           ojos, no hace falta que sea mucho más chico para leerse bien.
           Los números protagonistas (temperaturas grandes de clima/mar)
           sí bajan bastante más — un --vm-fs-hero-grande de 42px ocupa
           una porción enorme de una pantalla de 360px de ancho, mucho
           más protagonismo relativo del que tiene en un monitor. ── */
        @media (max-width: 719px) {
            :root {
                --vm-fs-micro: 9px;
                --vm-fs-caption: 10px;
                --vm-fs-cuerpo: 12px;
                --vm-fs-cuerpo-destacado: 13px;
                --vm-fs-subtitulo: 14px;
                --vm-fs-titulo: 16px;
                --vm-fs-titulo-grande: 19px;
                --vm-fs-hero: 23px;
                --vm-fs-hero-grande: 30px;
            }
        }

        nav {
            position: fixed; top: 0; left: 0; right: 0; z-index: 2000;
            /* Translúcido (prueba, a pedido del cliente) — solo el canal
               alfa del color de fondo, no la propiedad "opacity" del
               elemento (esa sí habría afectado también a los botones/
               texto de adentro, por herencia visual de opacity en CSS).
               Así se ve el mapa a través del fondo blanco, sin tocar
               nada de lo que vive encima — logo, buscador, menú,
               perfil, idioma quedan exactamente igual de nítidos que
               siempre. Bajado a 0.5 (antes se probó 0.7 y no se notó lo
               suficiente). Sigue sin backdrop-filter, mismo motivo de
               siempre (bug de Chrome/Edge en Windows). */
            background: rgba(255,255,255,0.25);
            box-shadow: 0 2px 20px rgba(0,0,0,0.1);
            padding: 0 24px; display: flex; align-items: center; justify-content: space-between;
            height: 56px; font-weight: 800; font-size: var(--vm-fs-titulo); color: var(--viu-oscuro);
        }
        .marca-viu-nav { display: flex; align-items: center; gap: 10px; }
        /* .visitas-mapa se movió al pie del mapa (Sesión 27) — su CSS
           vive ahora junto a los créditos, más abajo. */

        /* ── Barra de búsqueda del nav (Sesión 32) — oculta por defecto
           (celular sigue con el botón circular bajo Capas, ver más
           abajo), visible desde 720px. placeholder e input traducidos
           por buscador-mapa.js con la misma clave que ya usaba el panel
           de celular (buscador_placeholder), para no duplicar texto. ── */
        .barra-busqueda-nav-wrap {
            display: none; position: relative; flex: 1; max-width: 460px; margin: 0 24px;
        }
        @media (min-width: 720px) { .barra-busqueda-nav-wrap { display: block; } }
        .input-busqueda-nav {
            width: 100%; height: 38px; border: 1.5px solid rgba(179,179,179,0.8); border-radius: 19px;
            background: #f2f2f2; padding: 0 68px 0 20px; font-size: var(--vm-fs-cuerpo-destacado);
            font-family: 'Inter', 'Segoe UI', sans-serif; color: #1b1b1b; outline: none;
        }
        .input-busqueda-nav::placeholder { color: #999; }
        .input-busqueda-nav:focus { background: #ececec; }
        .icono-lupa-busqueda-nav { position: absolute; right: 40px; top: 50%; transform: translateY(-50%); color: #888; pointer-events: none; }
        .btn-limpiar-busqueda-nav {
            position: absolute; right: 12px; top: 50%; transform: translateY(-50%);
            border: none; background: none; color: #999; font-size: var(--vm-fs-cuerpo); cursor: pointer;
            width: 20px; height: 20px; display: none; align-items: center; justify-content: center;
        }
        .btn-limpiar-busqueda-nav.visible { display: flex; }
        .dropdown-resultados-busqueda-nav {
            position: absolute; top: 46px; left: 0; right: 0; background: white;
            border-radius: 14px; box-shadow: 0 10px 30px rgba(0,0,0,0.18);
            max-height: 60vh; overflow-y: auto; display: none;
        }
        .dropdown-resultados-busqueda-nav.abierto { display: block; }

        /* ── Botón de perfil de usuario — a pedido del cliente (Sesión
           28), ocupa el lugar donde antes decía "Viu Map · Cullera". Ícono
           genérico por ahora (imagen propia del cliente, con
           transparencia) — más adelante el usuario va a poder poner su
           propia foto ahí, mismo elemento, sin rehacer nada de esto.
           Sin función todavía, igual que el botón de Puntos Viu Map de al
           lado (mismo criterio, mismo tamaño: 34px). ── */
        .btn-perfil-nav {
            border: none; background: none; cursor: pointer; padding: 0;
            /* Sombra (a pedido del cliente) — el botón queda circular
               por el estilo en línea que le pone pintarAvatarEn() (ver
               más abajo en el JS), así que una sombra acá sí se ve como
               una sombra redonda de verdad, no rectangular. */
            box-shadow: 0 2px 6px rgba(0,0,0,0.25);
        }
        .btn-perfil-nav img { width: 34px; height: 34px; border-radius: 50%; object-fit: cover; display: block; }
        /* ── Nota (Sesión 29): el CSS de perfil y moneda mide lo mismo
           (34px) desde la Sesión 28 — si algún ícono nuevo se ve más
           chico que su vecino aunque el CSS coincida, sospechar del
           margen transparente DENTRO de la imagen en sí, no del CSS.
           Pasó acá: `icono-perfil.png` tenía ~12px de aire alrededor
           del círculo dentro de su lienzo de 200×200, mientras que
           `viu-coin.png` casi no tenía — mismo tamaño de caja, círculo
           visible más chico. Se resolvió recortando la imagen al
           contenido real (con Python/Pillow, midiendo el canal alfa),
           no agrandando el CSS. ── */

        /* ── Botón de Puntos Viu Map — a pedido del cliente (Sesión 27), para
           la presentación del martes. Sin función todavía (el sistema de
           puntos completo sigue sin diseñar, ver Pendientes) — por ahora
           solo visual: la moneda (imagen propia, mismo tamaño que el
           botón de farmacia de guardia, 34px) + un contador a su lado.
           El contador queda en 0 fijo hasta que exista el sistema real
           detrás — con id propio para conectarlo después sin tocar el
           resto. Reubicado (Sesión 28) a la derecha del botón de
           perfil, ambos donde antes decía "Viu Map · Cullera" — ya no está
           junto a la bandera. ── */
        .btn-puntos-nav {
            display: flex; align-items: center; gap: 6px; border: none; background: none;
            cursor: pointer; padding: 0;
        }
        /* `hidden` pierde contra el display:flex de arriba si no se refuerza:
           sin esta línea la moneda seguiría viéndose sin cuenta. */
        .btn-puntos-nav[hidden] { display: none; }
        .btn-puntos-nav img { width: 34px; height: 34px; border-radius: 50%; object-fit: cover; box-shadow: 0 1px 4px rgba(0,0,0,0.25); display: block; }
        .contador-puntos-nav { font-size: var(--vm-fs-cuerpo-destacado); font-weight: 800; color: #8a6d00; font-family: 'Inter', sans-serif; }
        /* Línea de visitas: letras sutiles, solo la cifra en negrita — para
           que un pautante que mira el mapa vea de un vistazo cuánta gente
           lo abre, sin que compita visualmente con la marca "Viu Map · Cullera". */
        /* ── Contador de visitas — sacado del nav, ahora flotante sobre
           el mapa, justo encima de la línea de créditos (Sesión 27, a
           pedido del cliente). Posición y tamaño de letra MEDIDOS por
           JS contra el control de atribución real de Leaflet (misma
           técnica que el botón de clima, Sesión 25/26) — no calculados
           a mano, para que no dependa de la altura real de esa línea en
           cada pantalla. Los valores de acá abajo son solo el respaldo
           mientras el JS no corrió todavía. ── */
        .visitas-mapa-flotante {
            position: absolute; right: 10px; bottom: 34px; z-index: 850;
            font-size: var(--vm-fs-caption); font-weight: 400; color: #666; font-family: 'Inter', sans-serif;
            background: rgba(255,255,255,0.85); padding: 1px 6px; border-radius: 4px;
        }
        .visitas-mapa-flotante b { color: #222; font-weight: 700; }

        /* ── Logo de Viu Map — esquina inferior izquierda, semi-
           transparente (Sesión 27). Tamaño fijo en escritorio; en
           celular un poco más chico para no competir con los botones
           de esa esquina (mi ubicación, farmacia). ── */
        .logo-viumap-flotante {
            position: absolute; left: 10px; bottom: 10px; z-index: 850;
            width: 130px; height: auto; opacity: 0.6; pointer-events: none; user-select: none;
        }
        body.pantalla-retina .logo-viumap-flotante {
            /* Calculado (Sesión 65) contra el peor caso: reloj del tren
               con las 2 tarjetas (Norte y Sur) visibles a la vez, en un
               celular chico (~667px de alto real, tipo iPhone SE) — ahí
               el reloj puede llegar hasta ~568px desde arriba del mapa,
               dejando un techo seguro de ~33px de alto para el logo. No
               pude medir el archivo real (vive en el hosting, no en
               este código) — 70px de ancho calcula bien si la
               proporción es la típica de un logo con ícono + texto
               (3:1 a 3.5:1); si en la práctica es más "cuadrado" de lo
               que supuse, bajale este número directamente, es un solo
               valor. Preferí no combinarlo con un tope de alto aparte
               (max-height) porque, sin saber el ancho real de la
               imagen, podría terminar deformándola en vez de
               achicarla pareja. */
            width: 70px;
        }

        .selector-idioma-wrap { position: relative; }
        /* ── Fase 3 del rediseño gráfico: ahora sí es UN bloque real en
           el HTML (antes eran 3 elementos sueltos que "coincidían" en
           pantalla porque el JS los posicionaba con el mismo right y
           cálculos en cadena) — a pedido del cliente, para que quien
           lo toque en el futuro vea de un vistazo que mover esto es
           mover TODO el bloque, no una pieza suelta. El reloj y el
           botón de clima siguen afuera a propósito (ver notas de cada
           uno): el reloj es un dato de pantalla sin fondo, el clima es
           un control aparte con su propio desplegable. ── */
        .grupo-mando-mapa {
            position: absolute; right: 10px; z-index: 900; width: 44px;
            background: var(--vm-superficie); border-radius: var(--vm-radio-panel);
            box-shadow: var(--vm-sombra-panel);
        }
        /* Botón de clima — un poco más ancho que la bandera de idioma
           (ancho automático según contenido), misma altura. Más separado
           de la bandera que la bandera del menú hamburguesa, a propósito,
           para que se note como su propio grupo. */
        /* ── Botón de clima — antes vivía en el nav, ahora es un control
           flotante del mapa, a la izquierda del control +/- de Leaflet
           (Sesión 23, a pedido del cliente, para liberar el nav).
           Posición calibrada contra la caja real de .leaflet-control-zoom
           — con la corrección real de la Sesión 23 (continuación): el
           control de Leaflet se posiciona relativo a #mapa, que arranca
           en top:56px (por el nav fijo), no en top:0 del viewport. Con
           el margen propio de Leaflet (10px), el control queda en
           top:66px real, no en top:10px como se había calculado la
           primera vez — ese error dejaba el botón tapado detrás del nav
           (mismo bug que el cliente reportó: "el clima desapareció").
           right:44px = 10 (margen del control) + 26 (ancho del botón
           "+") + 8 (espacio) — esa cuenta sí estaba bien, no dependía
           del nav. ── */
        .btn-clima-flotante {
            /* A la mitad del tamaño (Sesión 67) y reordenado lado a lado
               con el mando de zoom/capas, no apilado debajo (Sesión 68,
               ambos a pedido del cliente). El top/right de acá son solo
               el valor de respaldo antes de que corra
               alinearPilaSuperiorDerecha() — esa función recalcula la
               posición real (clima a la izquierda del mando, misma
               altura) apenas carga la página. */
            position: absolute; top: 66px; right: 64px; z-index: 900;
            height: 26px; padding: 0 9px; border-radius: 13px; border: 1px solid #e5e5e5;
            background: #f7f7f7; box-shadow: 0 2px 8px rgba(0,0,0,0.18); cursor: pointer;
            display: flex; align-items: center; gap: 5px;
        }
        .btn-clima-flotante:hover, .btn-clima-flotante:active { background: #eee; }
        /* Colores del estilo oscuro translúcido de móviles y tablets (ver el
           bloque "v57 — ESTILO OSCURO PARA MÓVILES Y TABLETS" al final del
           archivo). Si Jaime da otro tono u opacidad, se cambian AQUÍ. */
        :root {
            /* v58: +15 puntos de opacidad a pedido de Jaime (0,38 → 0,53 y
               0,52 → 0,67): en el móvil se veía demasiado claro. */
            --mando-oscuro-fondo: rgba(13, 35, 64, 0.53);
            --mando-oscuro-fondo-activo: rgba(13, 35, 64, 0.67);
        }
        /* Botón de RENFE Cercanías C1 (Sesión 73) — mismo estilo que el
           de clima (altura, relleno, radio, fondo), a la izquierda de
           ese mismo botón. El ícono es un reemplazo temporal (🚆) hasta
           que se cargue el logo real de Cercanías. */
        .btn-renfe-flotante {
            position: absolute; top: 66px; right: 160px; z-index: 900;
            height: 26px; padding: 0 9px; border-radius: 13px; border: 1px solid #e5e5e5;
            background: #f7f7f7; box-shadow: 0 2px 8px rgba(0,0,0,0.18); cursor: pointer;
            display: flex; align-items: center; gap: 4px; font-family: 'Inter', sans-serif;
        }
        .btn-renfe-flotante:hover, .btn-renfe-flotante:active { background: #eee; }
        .icono-renfe-nav { width: 20px; height: 20px; object-fit: contain; display: block; flex-shrink: 0; }
        /* Flecha + "Valencia"/"Gandía" apilados — la etiqueta arriba de
           la flecha hacia arriba, abajo de la flecha hacia abajo (a
           pedido del cliente). */
        .renfe-direccion { display: flex; flex-direction: column; align-items: center; line-height: 1; gap: 1px; }
        .renfe-etiqueta-destino { font-size: 7px; font-weight: 700; color: #888; text-transform: uppercase; letter-spacing: 0.02em; }
        .renfe-flecha { font-size: 11px; font-weight: 800; color: #35545C; line-height: 1; }
        .renfe-hora { font-size: var(--vm-fs-caption); font-weight: 800; color: #1b3a2f; font-family: 'Inter', sans-serif; }
        /* Botones de clima y trenes, más grandes (a pedido del cliente,
           Sesión 77) — solo en escritorio/tablet, hay espacio de sobra
           ahí. En celular se quedan como están, ya se habían achicado
           a propósito en una sesión anterior. El ícono del clima se
           agranda desde JS (ver actualizarClimaNav más abajo), no acá
           con transform, para no desbordar la fila. */
        @media (min-width: 720px) {
            .btn-clima-flotante, .btn-renfe-flotante {
                height: 38px; padding: 0 14px; border-radius: 19px; gap: 7px;
            }
            .temp-clima-nav { font-size: var(--vm-fs-subtitulo); }
            .icono-renfe-nav { width: 28px; height: 28px; }
            .renfe-etiqueta-destino { font-size: 9px; }
            .renfe-flecha { font-size: 15px; }
            .renfe-hora { font-size: var(--vm-fs-cuerpo-destacado); }
        }
        .icono-clima-nav { display: inline-flex; align-items: center; line-height: 1; }
        .icono-clima-nav svg {
            display: block;
            /* Sombra suave (a pedido del cliente) — el sol/nube quedaba
               un poco perdido contra el fondo gris claro del botón, sin
               nada que le dé profundidad. Desenfoque bajado a 1px
               (antes 2px, se veía muy difuminada) y desplazamiento casi
               nulo, para que se note como una sombra pegada al dibujo,
               no un halo suelto alrededor. */
            filter: drop-shadow(0 0.5px 1px rgba(0,0,0,0.3));
        }
        .temp-clima-nav { font-size: var(--vm-fs-cuerpo); font-weight: 800; line-height: 1; font-family: 'Inter', sans-serif; }
        /* Mismo borde por fuera + sombra que ya usan los números de la
           franja horaria del modal (a pedido del cliente) — solo en
           celular, para que el color de la temperatura se despegue del
           fondo gris claro del botón igual de bien que en el modal. */
        @media (max-width: 719px) {
            .temp-clima-nav {
                text-shadow:
                    0.5px 0 0 rgba(60,60,60,0.7), -0.5px 0 0 rgba(60,60,60,0.7),
                    0 0.5px 0 rgba(60,60,60,0.7), 0 -0.5px 0 rgba(60,60,60,0.7);
            }
        }

        /* ── "Hora de Viu Map" (Sesión 46) — reloj siempre visible con la
           hora oficial de España (Europa/Madrid), sin importar en qué
           huso horario esté configurado el teléfono de quien mira el
           mapa — pensado para alguien de visita con el teléfono todavía
           en la hora de su país de origen, que quiere saber qué hora es
           realmente en Cullera.
           Es el primer elemento de la cadena — ver
           alinearPilaSuperiorDerecha() en el script principal, que fija
           la posición real de toda la pila (reloj, clima, zoom,
           indicador de %, capas) en tiempo real, uno en base al que
           tiene arriba. El top de acá abajo (66px) es tanto el valor de
           arranque antes de que corra el JS como el punto de partida
           real de toda la cadena — no es una coincidencia, es a
           propósito. Sin fondo ni borde, a diferencia del botón de
           clima — es solo un dato fijo de pantalla, no algo para
           tocar. ── */
        .reloj-viu-map {
            /* Retirado de la vista (a pedido del cliente, Sesión 66) —
               sigue existiendo y actualizándose en el sistema tal cual
               (ver el JS que calcula la distancia/tiempo, sin tocar),
               solo que ya no se muestra ni reserva espacio en la
               columna de la derecha. */
            display: none;
            position: absolute; top: 66px; right: 10px; z-index: 900;
            font-family: 'Inter', sans-serif; font-size: var(--vm-fs-subtitulo); font-weight: 800;
            color: #1b1b1b; text-shadow: 0 1px 3px rgba(255,255,255,0.9);
            pointer-events: none;
        }
        .btn-idioma-actual {
            width: 32px; height: 22px; border-radius: 4px; overflow: hidden;
            border: 1px solid #ddd; padding: 0; cursor: pointer; background: white;
            box-shadow: 0 1px 4px rgba(0,0,0,0.2);
        }
        .btn-idioma-actual svg { width: 100%; height: 100%; display: block; }

        .panel-idiomas {
            position: absolute; top: 32px; right: 0; z-index: 2100;
            background: white; border-radius: 12px; box-shadow: 0 8px 24px rgba(0,0,0,0.22);
            padding: 10px; display: none; flex-wrap: wrap; justify-content: flex-end;
            gap: 8px; width: 246px;
        }
        .panel-idiomas.abierto { display: flex; }
        .idioma-opcion {
            display: flex; flex-direction: column; align-items: center; gap: 3px;
            border: none; background: none; cursor: pointer; padding: 4px 2px; border-radius: 6px;
            width: 36px;
        }
        .idioma-opcion:hover, .idioma-opcion:active { background: #f2f2f2; }
        .idioma-opcion.activa { background: #e8f5e9; }
        .bandera-mini { width: 28px; height: 19px; border-radius: 3px; overflow: hidden; border: 1px solid #ddd; }
        .bandera-mini svg { width: 100%; height: 100%; display: block; }
        .etiqueta-mini { font-size: var(--vm-fs-micro); font-weight: 700; color: #555; }

        /* ── Indicador de % de zoom, debajo del control +/- de Leaflet.
           Sienta la base para ajustes de tamaño dinámico según zoom más
           adelante — por ahora solo informa, por eso va chico y tenue.
           El top de acá abajo es solo un valor de arranque, antes de
           que corra alinearPilaSuperiorDerecha() (ver script
           principal) — esa función es la que fija la posición real,
           calculada en cadena junto con el reloj, el clima, el zoom y
           las capas, ya no un número fijo por ancho de pantalla
           (Sesión 46, reescrito de raíz — ver el porqué completo en el
           comentario de esa función). ── */
        .indicador-zoom {
            width: 44px; height: 22px; border-radius: 0; background: transparent;
            box-shadow: none; opacity: 1; border-top: 1px solid var(--vm-borde);
            display: flex; align-items: center; justify-content: center;
            font-size: var(--vm-fs-micro); font-weight: 700; color: var(--vm-texto-suave);
        }
        /* ── Botones propios de zoom (+/-), Fase 3 del rediseño gráfico
           — reemplazan al control nativo de Leaflet. Se probó primero
           vistiendo el control de Leaflet con CSS (.leaflet-control-zoom),
           pero sus propias reglas internas (modo táctil, estados
           disabled, distinta especificidad según el navegador) terminan
           compitiendo con cualquier estilo que se le ponga encima — así
           es como el botón quedó invisible en la primera prueba. La
           solución más sólida es no pelear contra el CSS de Leaflet:
           estos son botones propios, comunes y corrientes, que llaman a
           mapa.zoomIn()/zoomOut() directamente — mismo comportamiento,
           100% nuestro diseño, sin sorpresas de especificidad. ── */
        .grupo-zoom-mapa { }
        /* appearance:none es la parte clave del arreglo: sin esto, varios
           navegadores (sobre todo móvil) le dejan al <button> su fondo y
           esquinas redondeadas nativas del sistema por encima de
           cualquier "background: transparent" — eso era lo que hacía
           que cada botón se viera como su propia píldora suelta, con
           tamaños ligeramente distintos entre sí, en vez de fundirse
           en el panel de fondo compartido. */
        .btn-zoom-mapa {
            appearance: none; -webkit-appearance: none; -moz-appearance: none;
            display: block; width: 44px; height: 30px; border: none; background: transparent;
            font-size: var(--vm-fs-titulo); font-weight: 600; color: var(--vm-texto); cursor: pointer;
            font-family: Arial, Helvetica, sans-serif; line-height: 1; padding: 0; margin: 0;
        }
        .btn-zoom-mas {
            border-radius: var(--vm-radio-panel) var(--vm-radio-panel) 0 0;
            border-bottom: 1px solid var(--vm-borde);
        }
        .btn-zoom-mapa:hover, .btn-zoom-mapa:active { background: var(--vm-azul-suave); }
        .btn-zoom-mapa:disabled { color: var(--vm-borde); cursor: default; }
        .btn-zoom-mapa:disabled:hover { background: transparent; }

        /* ── Botón de capas (Sesión 18, continuación) — mismo tamaño y
           opacidad que el indicador de zoom de arriba, justo debajo de
           él, alineado con el control +/- de Leaflet (mismo right: 10px
           que usan los 2). Pensado para crecer: hoy tiene 2
           interruptores (Señales de Tráfico, Zonas Azules), más
           adelante puede sumar más sin cambiar la estructura. El top de
           acá abajo también es solo un valor de arranque — ver la nota
           de .indicador-zoom arriba, mismo criterio. ── */
        .capas-wrap { position: relative; }
        /* ── Botón del buscador del mapa (Sesión 30) — sigue el mismo
           apilado que capas/ubicación: top medido por JS contra la
           posición real de .capas-wrap (capas.top + capas.height + 10px
           de aire), no calculado a mano — misma lección de las Sesiones
           25/26. El valor de acá abajo es solo el respaldo mientras el
           JS de buscador-mapa.js no corrió todavía. ── */
        .btn-buscador-mapa {
            position: fixed; top: 210px; right: 10px; z-index: 900;
            width: 34px; height: 34px; border-radius: 50%; border: none;
            background: white; box-shadow: 0 2px 8px rgba(0,0,0,0.3); color: #1b3a2f;
            cursor: pointer; display: flex; align-items: center; justify-content: center;
        }
        .btn-buscador-mapa:hover, .btn-buscador-mapa:active { background: #f5f5f5; }
        /* Sesión 32: en escritorio la búsqueda vive en la barra del nav
           (.barra-busqueda-nav-wrap, más arriba) — este botón queda
           reservado para celular, hasta el ajuste de esa versión que el
           cliente va a definir en una sesión aparte. */
        @media (min-width: 720px) { .btn-buscador-mapa { display: none; } }
        .btn-capas {
            appearance: none; -webkit-appearance: none; -moz-appearance: none;
            width: 44px; height: 30px; border-radius: 0 0 var(--vm-radio-panel) var(--vm-radio-panel);
            border: none; border-top: 1px solid var(--vm-borde);
            background: transparent; box-shadow: none; opacity: 1; margin: 0;
            font-size: var(--vm-fs-cuerpo-destacado); cursor: pointer; display: flex; align-items: center; justify-content: center;
        }
        .btn-capas:hover, .btn-capas:active { background: var(--vm-azul-suave); }
        .panel-capas {
            display: none; position: absolute; top: 32px; right: 0;
            background: white; border-radius: 10px; box-shadow: 0 4px 18px rgba(0,0,0,0.2);
            padding: 12px 16px; min-width: 190px; z-index: 1600;
        }
        .panel-capas.abierto { display: block; }
        .fila-capa {
            display: flex; align-items: center; gap: 9px; font-size: var(--vm-fs-cuerpo); color: #333;
            padding: 6px 0; cursor: pointer; white-space: nowrap;
        }
        .fila-capa input[type="checkbox"] { width: 16px; height: 16px; cursor: pointer; }

        /* ── Legibilidad del chino: a tamaños muy chicos (pensados para
           texto latino corto) los trazos de los caracteres CJK se
           amontonan y se vuelven ilegibles. Se agranda puntualmente
           donde hoy el texto es más chico. ── */
        body.idioma-cjk .etiqueta-mini { font-size: var(--vm-fs-micro); }

        /* ── Menú hamburguesa — opciones de prueba, se reemplazan por las
           secciones reales de Viu Map más adelante ── */
        .grupo-nav-derecha { display: flex; align-items: center; gap: 10px; }
        .menu-hamburguesa-wrap {
            position: relative;
            /* Centrada con el mando de zoom de abajo (a pedido del
               cliente, sin mover el mando) — el mando tiene su centro a
               32px del borde derecho (right:10px + mitad de sus 44px de
               ancho); la hamburguesa, siguiendo el flujo normal del nav
               (padding-right:24px + mitad de sus 32px), quedaba con su
               centro a 40px — 8px más lejos del borde. Este margen
               negativo la acerca esos mismos 8px, sin tocar nada del
               resto de la fila (idioma, ancho del nav, etc.). */
            margin-right: -8px;
        }
        .btn-hamburguesa {
            width: 32px; height: 32px; border-radius: 8px; border: none; background: none;
            display: flex; align-items: center; justify-content: center;
            cursor: pointer; color: var(--viu-oscuro);
        }
        .btn-hamburguesa:hover, .btn-hamburguesa:active { background: #f2f2f2; }

        .panel-menu {
            position: absolute; top: 40px; right: 0; z-index: 2100;
            background: white; border-radius: 12px; box-shadow: 0 8px 24px rgba(0,0,0,0.22);
            padding: 8px 0; display: none; flex-direction: column; width: 200px;
        }
        .panel-menu.abierto { display: flex; }
        .opcion-menu {
            padding: 12px 18px; text-align: left; border: none; background: none;
            font-size: var(--vm-fs-cuerpo-destacado); font-weight: 600; color: var(--viu-oscuro); cursor: pointer;
        }
        .opcion-menu:hover, .opcion-menu:active { background: #f5f5f5; }
        /* ── Menú del perfil (Sesión 51) — mismo .panel-menu/.opcion-menu
           que ya usa la hamburguesa (fondo blanco, letras negras, a
           pedido del cliente), solo con su propio contenedor posicionado
           porque cuelga de btnPerfilUsuario en vez de btnMenuHamburguesa.
           "Mis Viu Map Coins" es la única opción con color propio (dorado),
           mismo tono exacto que ya usa .contador-puntos-nav más abajo,
           para que sea el mismo dorado en todo el nav, no uno nuevo. ── */
        .menu-perfil-wrap { position: relative; }
        /* btnPerfilUsuario vive pegado al borde IZQUIERDO del nav (dentro
           de .marca-viu-nav), a diferencia de la hamburguesa y el
           selector de idioma, que viven en .grupo-nav-derecha — por eso
           .panel-menu no le sirve tal cual: su "right: 0" anda bien
           ahí porque ancla el panel contra el borde derecho de la
           pantalla, pero acá lo dibujaba mayormente fuera de pantalla
           hacia la izquierda. Se pisa con left:0 para que abra hacia la
           derecha, donde sí hay espacio. */
        .menu-perfil-wrap .panel-menu { left: 0; right: auto; }
        .opcion-menu-dorada { color: #8a6d00; }
        .opcion-menu-dorada:hover, .opcion-menu-dorada:active { background: rgba(215,145,9,0.12); }
        /* Cerrar Sesión (a pedido del cliente) — texto negro rojizo, y un
           fondo rojizo oscuro traslúcido al pasar el mouse, en vez del
           gris genérico de las demás opciones — para que se lea como
           una acción distinta al resto, no un ítem más de navegación. */
        .opcion-menu-roja { color: #4a1414; }
        .opcion-menu-roja:hover, .opcion-menu-roja:active { background: rgba(139,0,0,0.09); }

        /* ══════════════════════════════════════════════════════════
           "¿Qué quieres hacer hoy?" — el punto de entrada más humano
           del mapa. Mediterráneo (mar), tejas (terracota) y naranjos
           (dorado) como paleta propia de Cullera, no de una app genérica.
           ══════════════════════════════════════════════════════════ */
        .fondo-buckets {
            position: fixed; inset: 0; z-index: 3000;
            /* Sin backdrop-filter (Sesión 76) — se quitó de acá y de
               otros 4 modales más (perfil, incidencia, verificación de
               teléfono, lector de términos), mismo motivo que ya se
               había resuelto en el nav (Sesión 18): bug real de Chrome/
               Edge en Windows con artefactos de renderizado — cajones
               negros apareciendo y desapareciendo sobre el mapa,
               confirmados por el cliente en Chrome. El fondo ya era
               suficientemente oscuro sin el desenfoque (0.55-0.6 de
               opacidad), así que sacarlo no se nota como pérdida
               visual, solo como corrección del bug. */
            background: rgba(14, 34, 38, 0.55);
            display: none; align-items: flex-end; justify-content: center;
        }
        .fondo-buckets.abierto { display: flex; animation: fbFundido 0.2s ease; }
        @keyframes fbFundido { from { opacity: 0; } to { opacity: 1; } }

        .panel-buckets {
            background: linear-gradient(180deg, var(--vm-fondo-calido) 0%, #FFF7EA 100%);
            width: 100%; max-width: 720px; max-height: 88vh;
            border-radius: 26px 26px 0 0; padding: 26px 22px 22px;
            overflow: hidden; box-shadow: 0 -14px 44px rgba(0,0,0,0.28);
            animation: fbSube 0.32s cubic-bezier(.2,.9,.3,1);
            display: flex; flex-direction: column;
        }
        @keyframes fbSube { from { transform: translateY(36px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
        @media (min-width: 720px) {
            .fondo-buckets { align-items: center; }
            .panel-buckets { border-radius: 26px; max-height: 78vh; }
        }

        .cabecera-buckets { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 16px; flex-shrink: 0; }
        .ceja-buckets {
            font-family: 'Inter', sans-serif; font-size: var(--vm-fs-caption); font-weight: 700;
            letter-spacing: 0.14em; color: var(--vm-dorado); text-transform: uppercase;
        }
        .titulo-buckets h2 {
            font-family: 'Inter', sans-serif; font-weight: 600; font-size: var(--vm-fs-hero);
            color: var(--vm-texto); margin-top: 3px; line-height: 1.18;
        }
        .subtitulo-buckets {
            font-family: 'Inter', sans-serif; font-size: var(--vm-fs-cuerpo); color: #5c7d84;
            margin-top: 6px; line-height: var(--vm-lh-cuerpo); display: none;
        }
        .subtitulo-buckets.visible { display: block; }
        .btn-cerrar-buckets {
            width: 36px; height: 36px; border-radius: 50%; border: none; background: white;
            color: var(--vm-texto); font-size: var(--vm-fs-subtitulo); cursor: pointer; flex-shrink: 0; margin-left: 14px;
            box-shadow: 0 2px 8px rgba(14,52,56,0.15);
        }
        .btn-cerrar-buckets:hover, .btn-cerrar-buckets:active { background: var(--vm-azul-suave); }

        /* Cuerpo con scroll propio — el panel entero nunca cambia de
           tamaño entre pasos, solo esto se desplaza si hace falta. */
        .cuerpo-buckets { flex: 1; overflow-y: auto; min-height: 0; }

        .grilla-buckets { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
        @media (max-width: 560px) { .grilla-buckets { grid-template-columns: repeat(2, 1fr); gap: 10px; } }

        /* ── Paso 2: filtro de categoría + especialidad ── */
        .campo-filtro { margin-bottom: 16px; }
        .campo-filtro label {
            display: block; font-family: 'Inter', sans-serif; font-size: var(--vm-fs-cuerpo); font-weight: 700;
            color: var(--vm-texto); margin-bottom: 6px;
        }
        .campo-filtro select {
            width: 100%; padding: 12px 14px; border: 1.5px solid #dce8ea; border-radius: 12px;
            font-family: 'Inter', sans-serif; font-size: var(--vm-fs-cuerpo-destacado); color: var(--vm-texto); background: white;
        }
        .campo-filtro select:focus { outline: none; border-color: var(--vm-azul); }
        .nota-filtro {
            font-family: 'Inter', sans-serif; font-size: var(--vm-fs-cuerpo); color: #6f8890;
            background: var(--vm-azul-suave); border-radius: 10px; padding: 10px 12px; margin: 4px 0 18px;
        }
        .acciones-filtro { display: flex; gap: 10px; }
        .btn-volver-buckets, .btn-continuar-filtro {
            font-family: 'Inter', sans-serif; font-weight: 600; font-size: var(--vm-fs-cuerpo-destacado);
            border: none; border-radius: 12px; padding: 13px; cursor: pointer;
        }
        .btn-volver-buckets { background: white; color: var(--vm-texto); box-shadow: 0 2px 8px rgba(14,52,56,0.1); }
        .btn-volver-buckets:hover, .btn-volver-buckets:active { background: var(--vm-azul-suave); }
        .btn-continuar-filtro { flex: 1; background: var(--vm-azul); color: white; }
        .btn-continuar-filtro:hover, .btn-continuar-filtro:active { background: #0b5a72; }
        .btn-continuar-filtro:disabled { background: #b8cdd1; cursor: default; }

        /* ── Paso 3: resultados ── */
        .lista-resultados { display: flex; flex-direction: column; gap: 10px; }
        .fila-resultado-negocio {
            background: white; border-radius: 16px; padding: 14px 16px;
            box-shadow: 0 2px 8px rgba(14,110,140,0.08); display: flex; flex-direction: column; gap: 10px;
        }
        .info-resultado .nombre-resultado { font-family: 'Inter', sans-serif; font-weight: 600; font-size: var(--vm-fs-subtitulo); color: var(--vm-texto); }
        .nombre-resultado-con-icono { display: flex; align-items: center; gap: 7px; }
        .icono-farmacia-resultado { width: 20px; height: 20px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
        .info-resultado .meta-resultado { font-family: 'Inter', sans-serif; font-size: var(--vm-fs-cuerpo); color: #6f8890; margin-top: var(--vm-esp-pegado); }
        .etiqueta-guardia-resultado { font-size: var(--vm-fs-caption); font-weight: 800; color: #2e7d32; margin-top: var(--vm-esp-pegado); }
        .acciones-resultado { display: flex; gap: 6px; flex-wrap: wrap; }
        .btn-accion-resultado {
            font-family: 'Inter', sans-serif; font-weight: 700; font-size: var(--vm-fs-cuerpo);
            border: none; border-radius: 20px; padding: 7px 12px; cursor: pointer;
            background: var(--vm-azul-suave); color: var(--vm-azul);
        }
        .btn-accion-resultado.btn-ir-resultado { background: var(--vm-azul); color: white; }
        .btn-accion-resultado.btn-ir-resultado:hover, .btn-accion-resultado.btn-ir-resultado:active { background: #0b5a72; }
        /* Reservar y Agendar Cita, con el mismo color que su ficha
           correspondiente (a pedido del cliente) — antes ambos
           compartían el celeste clarito genérico de este botón base, y
           el ícono (plato/calendario) se perdía contra ese fondo tan
           parecido en tono. Vino para Reservar (mismo color que el
           botón sticky de la ficha), azul marino para Cita Previa. */
        .btn-accion-resultado.btn-reservar-resultado { background: #7A2048; color: white; }
        .btn-accion-resultado.btn-reservar-resultado:hover, .btn-accion-resultado.btn-reservar-resultado:active { background: #5E1836; }
        .btn-accion-resultado.btn-cita-resultado { background: var(--vm-azul); color: white; }
        .btn-accion-resultado.btn-cita-resultado:hover, .btn-accion-resultado.btn-cita-resultado:active { background: var(--vm-azul-oscuro); }
        /* Bisel sutil, sin sombra flotante (a pedido del cliente) — mismo
           concepto que ya tienen los botones grandes de la ficha, para
           que estos también se lean como botones con volumen propio.
           Se aplica a la clase base, así alcanza a Ir/Reservar/Cita
           Previa de una sola vez. */
        .btn-accion-resultado {
            box-shadow: inset 0 1px 0 rgba(255,255,255,0.35), inset 0 -2px 3px rgba(0,0,0,0.15);
        }
        /* Excepción a pedido del cliente: el "Ir" del Comparador de
           Gasolineras vuelve al verde de marca — comparte clase con el
           "Ir" de resultados/filtro de negocios, así que se sobreescribe
           acá solo para el contexto del comparador (.acciones-estacion),
           sin tocar el resto de usos de .btn-ir-resultado. Ahora además
           usa la imagen propia ICONO_IR_COCHE en vez de la píldora de
           color con emoji — por eso pierde fondo/padding, solo enmarca
           la imagen. */
        .acciones-estacion .btn-ir-resultado {
            appearance: none; -webkit-appearance: none; -moz-appearance: none;
            background: transparent; padding: 0; margin: 0; border: none; border-radius: 8px;
            display: flex; align-items: center; justify-content: center;
        }
        .acciones-estacion .btn-ir-resultado:hover, .acciones-estacion .btn-ir-resultado:active { background: transparent; opacity: 0.82; }
        .acciones-estacion .btn-ir-resultado img { display: block; width: 44px; height: 29px; }
        /* Mismo criterio que el botón de coche del comparador, pero para
           los botones "Ir caminando"/"Ir en coche" de resultados/filtro
           de negocios (icono propio ICONO_IR_CAMINANDO/ICONO_IR_COCHE en
           vez de la píldora azul con emoji y texto). Necesita repetir
           ".btn-accion-resultado" en el selector: si no, esta regla
           queda con menos peso que ".btn-accion-resultado.btn-ir-resultado"
           (el que pinta todo de azul) y el fondo azul se sigue viendo
           asomado detrás de la imagen, como un "borde" — eso es lo que
           se veía mal. */
        .btn-accion-resultado.btn-ir-imagen-resultado {
            appearance: none; -webkit-appearance: none; -moz-appearance: none;
            background: transparent; padding: 0; margin: 0; border: none; border-radius: 8px;
            display: flex; align-items: center; justify-content: center;
        }
        .btn-accion-resultado.btn-ir-imagen-resultado:hover, .btn-accion-resultado.btn-ir-imagen-resultado:active { background: transparent; opacity: 0.82; }
        .btn-ir-imagen-resultado img { display: block; width: 44px; height: 29px; }
        .btn-accion-resultado:disabled { background: #f0f0f0; color: #bbb; cursor: default; }
        /* Rediseñados como círculo fijo, igual que en la ficha y en los
           botones del mapa (a pedido del cliente) — estos 3 en
           particular nunca llevan texto, solo el ícono, así que tiene
           más sentido un círculo con la imagen al 100% que la píldora
           de antes (que dejaba el ícono chico dentro de un espacio
           pensado para texto+ícono juntos). */
        .btn-accion-resultado.btn-etiqueta-resultado {
            width: 30px; height: 30px; padding: 0; border-radius: 50%;
            display: flex; align-items: center; justify-content: center; flex-shrink: 0;
        }
        /* Marcado "activo" con el color propio de cada acción, no un
           solo tono genérico para las 3 (a pedido del cliente) — mismo
           criterio de color que ya usan los botones del panel del mapa
           (dorado/rojo/azul). El fondo cubre el botón completo (no solo
           un tinte del ícono), para que se note de un vistazo que ese
           lugar quedó marcado. */
        .btn-accion-resultado.btn-etiqueta-resultado.activa[data-tipo="destacado"] { background: #f9a825; color: white; }
        .btn-accion-resultado.btn-etiqueta-resultado.activa[data-tipo="favorito"] { background: #e53935; color: white; }
        .btn-accion-resultado.btn-etiqueta-resultado.activa[data-tipo="porVisitar"] { background: #1a73e8; color: white; }
        /* Mismo motivo que .btn-etiqueta img — !important necesario
           para ganarle al tamaño en línea (1em) que trae el <img> que
           arma icono(), pensado para texto, no para este círculo. */
        .btn-accion-resultado.btn-etiqueta-resultado img { width: 100% !important; height: 100% !important; vertical-align: 0 !important; object-fit: contain; display: block; }
        .estrellas-resultado { display: inline-flex; gap: 2px; vertical-align: middle; }
        .estrella-resultado { font-size: var(--vm-fs-subtitulo); color: #dde5e6; cursor: pointer; transition: color 0.1s; }
        .estrella-resultado:hover, .estrella-resultado:active { color: #f9a825; }
        .fila-resultado-simple { opacity: 0.72; background: #fbfbf9; }
        .fila-resultado-simple .nombre-resultado { color: #5c7d84; }
        .vacio-resultados {
            text-align: center; padding: 30px 10px; font-family: 'Inter', sans-serif;
            color: #8ba3a8; font-size: var(--vm-fs-cuerpo);
        }

        .tarjeta-bucket {
            background: white; border: none; border-radius: 18px; padding: 18px 10px 14px;
            display: flex; flex-direction: column; align-items: center; text-align: center;
            cursor: pointer; box-shadow: 0 2px 10px rgba(14,110,140,0.09);
            transition: transform 0.15s ease, box-shadow 0.15s ease;
            opacity: 0; transform: translateY(10px); animation: fbCard 0.4s ease forwards;
            font-family: inherit;
        }
        .tarjeta-bucket:hover, .tarjeta-bucket:active { transform: translateY(-3px); box-shadow: 0 10px 24px rgba(14,110,140,0.2); }
        .tarjeta-bucket:active { transform: translateY(-1px); }
        @keyframes fbCard { to { opacity: 1; transform: translateY(0); } }
        .tarjeta-bucket:nth-child(1) { animation-delay: .02s; } .tarjeta-bucket:nth-child(2) { animation-delay: .05s; }
        .tarjeta-bucket:nth-child(3) { animation-delay: .08s; } .tarjeta-bucket:nth-child(4) { animation-delay: .11s; }
        .tarjeta-bucket:nth-child(5) { animation-delay: .14s; } .tarjeta-bucket:nth-child(6) { animation-delay: .17s; }
        .tarjeta-bucket:nth-child(7) { animation-delay: .20s; } .tarjeta-bucket:nth-child(8) { animation-delay: .23s; }
        .tarjeta-bucket:nth-child(9) { animation-delay: .26s; }

        .icono-bucket {
            width: 52px; height: 52px; border-radius: 50%; background: var(--vm-azul-suave);
            display: flex; align-items: center; justify-content: center; font-size: var(--vm-fs-hero); margin-bottom: 9px;
        }
        .titulo-bucket { font-family: 'Inter', sans-serif; font-weight: 600; font-size: var(--vm-fs-cuerpo); color: var(--vm-texto); line-height: 1.25; margin-bottom: 4px; }
        .ejemplos-bucket { font-family: 'Inter', sans-serif; font-size: var(--vm-fs-micro); color: #6f8890; line-height: var(--vm-lh-cuerpo); }

        /* Chip de filtro activo, sobre el mapa */
        .chip-filtro-bucket {
            position: fixed; top: 66px; left: 50%; transform: translateX(-50%); z-index: 1500;
            background: var(--vm-texto); color: white; padding: 8px 8px 8px 16px; border-radius: 22px;
            display: none; align-items: center; gap: 10px; box-shadow: 0 6px 18px rgba(0,0,0,0.28);
            font-family: 'Inter', sans-serif;
        }
        .chip-filtro-bucket.visible { display: flex; }
        .chip-filtro-bucket span { font-size: var(--vm-fs-cuerpo); font-weight: 600; }
        .chip-filtro-bucket button {
            width: 23px; height: 23px; border-radius: 50%; border: none; background: rgba(255,255,255,0.18);
            color: white; cursor: pointer; font-size: var(--vm-fs-cuerpo); display: flex; align-items: center; justify-content: center;
        }
        .chip-filtro-bucket button:hover, .chip-filtro-bucket button:active { background: rgba(255,255,255,0.32); }

        /* top:0, no 56px (a pedido del cliente) — el mapa antes ni
           siquiera llegaba a esa zona: empezaba justo donde termina el
           nav, así que por detrás del nav translúcido no había mapa
           real, solo el fondo blanco liso de <body> (sin declarar,
           blanco por defecto del navegador) — por eso ningún valor de
           opacidad se notaba, no había nada que revelar. El nav sigue
           tapándolo con su propio z-index más alto cuando es opaco;
           ahora, al bajarle la opacidad, lo que se empieza a ver
           debajo es el mapa de verdad, no un blanco liso indistinguible
           del propio fondo del nav. */
        #mapa { position: fixed; top: 0; left: 0; right: 0; bottom: 0; }
        /* ── Cursor propio del mapa (Sesión 18, continuación) — imagen
           subida por el cliente (mano de clic con acento rojo), no el
           cursor del sistema operativo. Corrige un bug real de Windows
           (MPO/DWM) que perdía el borde del cursor durante el
           movimiento — al ser un bitmap fijo que ya renderizó el
           navegador, no algo que el sistema dibuja en tiempo real, no
           hereda ese bug. Imagen recortada a su contenido real (sin el
           margen transparente sobrante), redimensionada a 34×40 px
           primero y despues un 30% más chica (24×28 px, a pedido del
           cliente tras verla) — el tamaño original que subió el
           cliente (631×550) es demasiado grande para un cursor. Solo
           para el cursor GENERAL del mapa (arrastre) — los marcadores
           siguen con su cursor normal, sin tocar. El "6 5" es el punto
           exacto que actúa como el "dedo" del cursor — calculado
           automáticamente ubicando el centro del círculo rojo de la
           imagen, no a ojo, y reescalado junto con el resto de la
           imagen. */
        #mapa, #mapa .leaflet-grab {
            cursor: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAcCAYAAAB75n/uAAABJGlDQ1BJQ0MgUHJvZmlsZQAAeJxjYGAycHRxcmUSYGDIzSspCnJ3UoiIjFJgP8/AxsDMAAaJycUFjgEBPiB2Xn5eKgMG+HaNgRFEX9YFmYUpjxdwJRcUlQDpP0BslJJanMzAwGgAZGeXlxQAxRnnANkiSdlg9gYQuygkyBnIPgJk86VD2FdA7CQI+wmIXQT0BJD9BaQ+Hcxm4gCbA2HLgNglqRUgexmc8wsqizLTM0oUDC0tLRUcU/KTUhWCK4tLUnOLFTzzkvOLCvKLEktSU4BqIe4DA0GIQlCIaQA1WmiS6G+CABQPENbnQHD4MoqdQYghQHJpURmUychkTJiPMGOOBAOD/1IGBpY/CDGTXgaGBToMDPxTEWJqhgwMAvoMDPvmAADAxk/9GlU2EAAABW5JREFUeJytVm1IlWcYvp6PczyWLqNQ/ByeijlZ5yytWI2xTTdm/ot0yZyjtWgt/CPGCoYO9m8LoiVGRxdlobOIopXU1sIKBiMSHWpCUbhziBImOU/Lzjnv81z74XsqXbURu+GF9+u5rve67+u57xcABAAFQBMQaVrvmCPl5wTEt0CKAODz+V7E/xAeAIBS70HKEw/vSrlZS3nMvVLPA7zAI2WNAkBADUj55Xda36NSl+8Ksc8r5RBSUhZVuyqfh2CeFuIkpDw2DvxIIXgD4CWABDgmxDiBgAAgnwed7rpmIU46UnIHEHt1+XJWVlWZoMcTuycEE0IMQKl1qUpVucuEe/x7JN+ilL9cA0xRSYnzVyxGkty2ezc/Bsy41szT+vxcn6/GTZVHCJFc/mxh81JS/FLK9Zc9nugFwFauW2dJMhGP8979+1yUk2MmhTAE3gQwVwsBFzz1PynwSfkDlOo+IuXVmBBckp7uXA+HaY0hSe5obuYqwKzw+yeXlZQ8ANAKYHdBQUFYKXXR6/Uuqa6uVgC8mDbBzNQlfUfgHWod+wIwnzY1GZK01jI8Osr0rCxeu3mTJPnK0qXcuHEjo9Eou7u7CeC8Ugpaayj1FBcXT7OjVanOq0JwWXY2/5ictEkVVWvXsmXPHkvShvbtc9avf98haUg6q1atMgDqATQC+KC0tNQzq7zTItK0fgNCDP2cmRkoALr3treTpGOt5cWLFxkMBkmS0WiUixcv5sjIVVpruX//fubk5DAUCnHTpk0E8FNFRUUKposv4OYNQoiv06X8EFICXu9LwUAg4TiONa6KlStXsqOjgyTZ2NjImpoakuTExF0GAgFnYuJugmRsw4YNBLDRTdc/N+ZmwOM+PHn27BmSTBhj2NPTw/b2dlpr+fvoKHfu3ElrLUmyoaGB1dXVJOkMDg4arXUfySdaWADw9Pb2aqVUWUVFBUkax3H4KOzDM2MNrbWMRCJsaWmhq9aUl5cTwGtPKroQQkDKaWKv13tpcHCQJJ14PJ4EoLWGj0gfESYSCZJMdHZ2EkDLbIKknCUAuubPn/8rgOu1tbUkaePx2Aywx8NaS8dxHiq4ffs209PTBx/bBRAkxYIFC9IzMjJuHDlyhOFwmD09PSwsLGRrayvdXD2R4HGi5FcEg8E/AeQ+tKor5/WqqiqSjLs+t5FIZAaJ4ySeqsQYw7jbx8rLyw2AZUkbWZJITU0d7e/vfzA2NubLysoyU1P3ZV5eHvr6+jA6OjqdRzHbGIQ1FgShlIb0emGMgc/nkwB8M2rgFvej4uJip7+/nySdmPtFMwtqZ+edJDk8PMSuri6SZGVlpQMgOLunSymltda+lZaWdnjv3r15dXV1jjFGk4TWGiRhjIHWj/bQqVOnEI1GkZmZid7eXhYVFYn6+vo4ycCThoaWUjrW2lwAHQ0NDeW7du0yAKTjOCIJHI/F8H13N44fPw4hBMrK3oa1xOnTp7Fw4ULj9XrV4cOHv3raVFJCCGOtlUKIb8rKyhoPHjyI/Px8c+vWLXXoUAd6ey8gJycHxcUv486dOxgY+A2FhYXYuvUzlpYuN9HopMrLy489a+xJANRa03Gc2tzc3H1Hjx5NI2kPHDggi4qKMDIygnA4jNWrV2PLli3Mzs42APSZM2cQCoVw7ty588/ABzDdOrRr4aBSaqipqYm1tbVmzZo1bGtro5mucmJqaoptbW1csWJFAsAJAO/+G/jjoaWU8Pv98wBcbQuFDMkESROJRNjc3Ey/3z8OoAVAQEr59OHzjPBqrTFnzpxPtm/fzitXrrCuro4ZGRnDALYByFZKJfuYxHP8pCXb7wtKqWEp5RCADQBStdZJYI1ZLfpvs+GdZNSr0mwAAAAASUVORK5CYII=') 6 5, grab;
        }
        /* Marcadores y elementos clicables del mapa: cursor normal del
           navegador, sin tocar — es lo que ya funcionaba bien. */
        #mapa .leaflet-marker-icon, #mapa .leaflet-interactive { cursor: pointer; }

        /* ── Punto de "estás aquí" del visitante — idéntico a NexIABus ──
           ⚠️ REGLA DE DISEÑO PERMANENTE, a pedido explícito de Jaime
           (Sesión 46) — el color de este punto NO PUEDE cambiar nunca a
           otro que no sea negro/gris oscuro. Los negocios y lugares del
           mapa usan colores de todo tipo (rojo, verde, azul, amarillo,
           magenta, según su categoría) — el negro queda reservado
           exclusivamente para "esta soy yo". Es la única forma de que
           cualquier persona, mirando el mapa, distinga sin dudar su
           propia posición de cualquier negocio, sin importar qué color
           tenga ese negocio en ese momento. Un intento anterior de
           recolorearlo al verde de Viu Map (misma sesión, sin que Jaime lo
           hubiera pedido) se revirtió por esto exacto — que quede
           anotado acá para no repetirlo. ── */
        .contenedor-usuario {
            display: flex; flex-direction: column; align-items: center; justify-content: center;
            width: 100%; height: 100%; gap: 3px; pointer-events: none;
        }
        .pila-usuario { position: relative; width: 18px; height: 18px; flex-shrink: 0; }
        .pila-usuario .icono-usuario { position: relative; z-index: 2; }
        .cono-brujula {
            display: none; position: absolute; z-index: 1;
            left: 50%; top: 50%;
            width: 34px; height: 26px;
            transform-origin: 50% 100%;
            background: linear-gradient(to top, rgba(27,27,27,0.45), rgba(27,27,27,0.04));
            clip-path: polygon(0 0, 100% 0, 50% 100%);
            transition: transform 0.25s ease-out;
            pointer-events: none;
        }
        .icono-usuario {
            width: 18px; height: 18px; border-radius: 50%;
            background: #1b1b1b; border: 3px solid white;
            box-shadow: 0 2px 6px rgba(0,0,0,0.4);
            animation: parpadeoUsuario 1.1s infinite;
        }
        @keyframes parpadeoUsuario {
            0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(27,27,27,0.6), 0 2px 6px rgba(0,0,0,0.4); }
            50% { opacity: 0.35; box-shadow: 0 0 0 10px rgba(27,27,27,0), 0 2px 6px rgba(0,0,0,0.4); }
        }

        /* ── Botón "centrar en mi ubicación" — idéntico a NexIABus ── */
        .btn-mi-ubicacion {
            position: absolute; left: 10px; top: 70px; z-index: 900;
            width: 34px; height: 34px; border-radius: 50%; background: white;
            border: none; box-shadow: 0 2px 10px rgba(0,0,0,0.3);
            display: flex; align-items: center; justify-content: center;
            cursor: pointer; color: #999; font-size: var(--vm-fs-titulo); transition: color 0.2s;
        }
        .btn-mi-ubicacion:hover, .btn-mi-ubicacion:active { background: #f5f5f5; }
        .btn-mi-ubicacion.disponible { color: #1b1b1b; }
        /* Si el GPS ya confirmó una posición y está fuera del cerco de
           Cullera (ej. alguien abre el mapa desde Madrid), el botón se
           oculta por completo — no tiene sentido ofrecer "centrar en mi
           ubicación" a alguien que no está en el pueblo. Mientras el
           permiso todavía está pendiente/denegado, el botón se ve gris,
           para que la persona pueda tocarlo y reintentar. */
        .btn-mi-ubicacion.fuera-de-cullera { display: none; }

        /* ── Botón de farmacia de guardia — antes vivía en el nav, ahora
           agrupado con "centrar en mi ubicación" (Sesión 22, a pedido del
           cliente): las dos cumplen una función parecida de "ubicarme /
           encontrar algo cerca", así que van juntas en la misma esquina.
           Mismo tamaño y sombra que .btn-mi-ubicacion, 10px de espacio
           justo debajo (70+34+10=114). ── */
        .btn-farmacia-flotante {
            position: absolute; left: 10px; top: 114px; z-index: 900;
            width: 34px; height: 34px; padding: 0; border-radius: 50%; border: none;
            cursor: pointer; overflow: hidden; display: flex;
            align-items: center; justify-content: center; background: white;
            box-shadow: 0 2px 10px rgba(0,0,0,0.3);
        }
        .btn-farmacia-flotante img { width: 100%; height: 100%; object-fit: cover; display: block; }
        .btn-farmacia-flotante picture { display: block; width: 100%; height: 100%; }
        .btn-farmacia-flotante:hover, .btn-farmacia-flotante:active { filter: brightness(1.08); }

        /* ── Botón de Incidencias (Fase 3, Sesión 47) — mismo lenguaje
           visual que .btn-farmacia-flotante (34px, sombra, esquina
           izquierda), debajo de los 3 botones de etiqueta personal
           (158+34+10=... en realidad van en 158/202/246, así que este
           arranca en 246+34+10=290). Oculto por defecto — solo se
           muestra si esCuentaReal() Y telefonoVerificado === true (ver
           actualizarBotonIncidencias()). El ícono es una imagen WebP
           embebida en base64 dentro del propio botón (ver el HTML), igual que
           el resto de las imágenes propias del mapa. ── */
        .btn-incidencias-flotante {
            /* Reubicado (Sesión 66) y bajado un poco más (Sesión 67) —
               esquina inferior derecha, arriba del contador de visitas,
               a propósito: es una zona poco usada de la interfaz y no
               conviene incentivar el uso de este botón poniéndolo en
               un lugar más cómodo o visible de lo necesario.
               bottom:54px = 34 (donde arranca el contador de visitas)
               + ~15 (su alto real) + 5 de aire — antes eran 62px, con
               más aire del necesario. */
            position: absolute; right: 10px; bottom: 48px; z-index: 900;
            width: 34px; height: 34px; padding: 0; border: none; background: none;
            cursor: pointer; display: none;
            align-items: center; justify-content: center;
        }
        .btn-incidencias-flotante.visible { display: flex; }
        /* object-fit: contain (no "cover") — a propósito, para no
           recortar la pestañita de la burbuja de diálogo; y sin fondo
           circular ni overflow:hidden detrás, para que se vea la forma
           real e irregular del ícono (como el logo de WhatsApp), no un
           círculo perfecto. Sombra más suave (Sesión 67, a pedido del
           cliente) — antes era demasiado marcada. */
        .btn-incidencias-flotante img { width: 100%; height: 100%; object-fit: contain; display: block; filter: drop-shadow(0 1px 3px rgba(0,0,0,0.18)); }
        .btn-incidencias-flotante:hover, .btn-incidencias-flotante:active { filter: brightness(1.08); }

        /* ── Los 3 botones de etiqueta personal, reemplazan al único
           botón de favoritos: ⭐ destacado, ❤️ favorito, 🎯 por visitar.
           Cada uno filtra el mismo panel compartido a su categoría.
           Van DEBAJO de .btn-farmacia-flotante (10px por debajo, mismo
           criterio que el resto de esta columna: 114+34+10=158) — bug
           real corregido en la Sesión 46: antes empezaban en 114px,
           exactamente encima de farmacia, y las 2 quedaban superpuestas
           apenas alguien tenía algún favorito marcado (recién visible
           entonces, porque .btn-etiqueta arranca oculto). ── */
        .btn-etiqueta {
            position: absolute; left: 10px; z-index: 900;
            width: 34px; height: 34px; border-radius: 50%; background: white;
            border: none; box-shadow: 0 2px 10px rgba(0,0,0,0.3);
            align-items: center; justify-content: center;
            cursor: pointer; font-size: var(--vm-fs-subtitulo);
            display: none;
        }
        .btn-etiqueta.visible { display: flex; }
        .btn-etiqueta:hover, .btn-etiqueta:active { background: #f5f5f5; }
        /* !important a propósito acá — icono() (la función compartida
           que llena estos botones) devuelve un <img> con tamaño en
           línea (1em, pensado para ir junto a texto normal), correcto
           para el resto de sus usos en la app pero no para un botón
           circular grande como este. El estilo en línea le gana a una
           regla normal, así que sin !important esta regla no hacía
           nada — el ícono quedaba chico y corrido dentro del círculo. */
        .btn-etiqueta img { width: 100% !important; height: 100% !important; vertical-align: 0 !important; object-fit: contain; display: block; }
        #btnDestacados { top: 158px; color: #f9a825; }
        #btnFavoritoCorazon { top: 202px; color: #e53935; }
        #btnPorVisitar { top: 246px; color: #1a73e8; }

        .panel-favoritos {
            position: absolute; left: 10px; top: 286px; z-index: 1000;
            background: white; border-radius: 14px;
            box-shadow: 0 8px 28px rgba(0,0,0,0.18);
            /* Ancho dinámico (Fase 6, a pedido del cliente): en vez de un
               ancho fijo que obligaba a cortar nombres largos con "...",
               "width: max-content" hace que el panel se estire solo hasta
               lo que pida la fila más ancha (el nombre más largo de esa
               categoría de favoritos) — sin medir nada a mano en JS. */
            width: max-content; min-width: 220px; max-width: calc(100vw - 20px);
            overflow: hidden; display: none;
        }
        /* En escritorio, el nombre no se envuelve — el ancho dinámico de
           arriba ya se encarga de que quepa completo en una sola línea
           (hasta un tope generoso, para no desbordar en nombres extremos). */
        @media (min-width: 720px) {
            .panel-favoritos { max-width: min(480px, calc(100vw - 20px)); }
            .nombre-item-favorito { white-space: nowrap; }
        }
        .panel-favoritos.abierto { display: block; }
        .panel-favoritos-titulo {
            padding: 10px 14px; font-size: var(--vm-fs-cuerpo); font-weight: 700; color: #999;
            border-bottom: 1px solid #eee; text-transform: uppercase; letter-spacing: 0.04em;
        }
        /* El ícono del título (a pedido del cliente) — se había dejado
           igual de chico que la letra, y quedó demasiado pequeño; ahora
           un poco más grande (1.8x el tamaño del texto), sin llegar al
           tamaño grande que tenía originalmente. */
        .panel-favoritos-titulo img, .panel-favoritos-titulo svg {
            width: calc(var(--vm-fs-cuerpo) * 1.8) !important; height: calc(var(--vm-fs-cuerpo) * 1.8) !important;
            vertical-align: -0.35em; object-fit: contain;
        }
        .item-favorito {
            padding: 10px 14px; border-bottom: 1px solid #eee;
            display: flex; justify-content: space-between; align-items: center; gap: 8px;
            font-size: var(--vm-fs-cuerpo-destacado);
        }
        .item-favorito:hover, .item-favorito:active { background: #f7f7f7; }
        .item-favorito:last-child { border-bottom: none; }
        /* Sin "text-overflow: ellipsis" a propósito — el nombre del
           negocio nunca se corta. En pantallas angostas (móvil) esto
           envuelve a 2 líneas si hace falta; en escritorio, la regla de
           arriba (min-width:720px) lo mantiene en una sola línea gracias
           al ancho dinámico del panel. */
        .nombre-item-favorito { flex: 1; min-width: 0; }
        .acciones-item-favorito { display: flex; gap: 6px; flex-shrink: 0; }
        .sin-favoritos { padding: 14px; color: #999; font-size: var(--vm-fs-cuerpo); }

        /* ══════════════════════════════════════════════════════════
           Perfil de usuario — Fase 1: registro (Google / email / anónimo)
           y datos base. Mismo patrón visual que .fondo-buckets ("¿Qué
           quieres hacer hoy?"): hoja que sube desde abajo en el
           teléfono, modal centrado en escritorio ≥720px — a propósito,
           para no introducir un tercer estilo de ventana en el mapa.
           ══════════════════════════════════════════════════════════ */
        .fondo-perfil {
            position: fixed; inset: 0; z-index: 3100;
            background: rgba(14, 34, 38, 0.55);
            display: none; align-items: flex-end; justify-content: center;
        }
        .fondo-perfil.abierto { display: flex; animation: fbFundido 0.2s ease; }
        .panel-perfil {
            position: relative; background: var(--vm-fondo-calido);
            width: 100%; max-width: 420px; max-height: 88vh;
            border-radius: 26px 26px 0 0; padding: 28px 22px 22px;
            overflow-y: auto; box-shadow: 0 -14px 44px rgba(0,0,0,0.28);
            animation: fbSube 0.32s cubic-bezier(.2,.9,.3,1);
        }
        @media (min-width: 720px) {
            .fondo-perfil { align-items: center; }
            .panel-perfil { border-radius: 26px; max-height: 82vh; }
        }

        /* ── Panel de Incidencias (Fase 3, Sesión 47) — mismo patrón
           exacto de .fondo-perfil/.panel-perfil (hoja deslizante en
           celular, modal centrado en escritorio ≥720px), a propósito
           para no introducir un tercer estilo de ventana en el mapa
           (mismo criterio ya dejado explícito para el modal de
           verificación de teléfono, más abajo). z-index propio (3150),
           entre el perfil (3100) y la verificación de teléfono (3300) —
           nunca se abren dos de estos a la vez en la práctica, pero
           conviene que el orden de apilado tenga sentido igual. ── */
        .fondo-incidencia {
            position: fixed; inset: 0; z-index: 3150;
            background: rgba(14, 34, 38, 0.55);
            display: none; align-items: flex-end; justify-content: center;
        }
        .fondo-incidencia.abierto { display: flex; animation: fbFundido 0.2s ease; }
        .panel-incidencia {
            position: relative; background: var(--vm-fondo-calido);
            width: 100%; max-width: 420px; max-height: 88vh;
            border-radius: 26px 26px 0 0; padding: 28px 22px 22px;
            overflow-y: auto; box-shadow: 0 -14px 44px rgba(0,0,0,0.28);
            animation: fbSube 0.32s cubic-bezier(.2,.9,.3,1);
        }
        @media (min-width: 720px) {
            .fondo-incidencia { align-items: center; }
            .panel-incidencia { border-radius: 26px; max-height: 82vh; }
        }
        .incidencia-ubicacion-info {
            font-size: var(--vm-fs-cuerpo); padding: 9px 12px; border-radius: 10px; background: #f0f4f5; color: #5c7d84;
        }
        .incidencia-ubicacion-info.ok { background: #e8f5e9; color: #2e7d32; }
        .incidencia-ubicacion-info.error { background: #fdecea; color: #c62828; }
        .incidencia-contador { text-align: right; font-size: var(--vm-fs-caption); color: #999; margin-top: var(--vm-esp-pegado); }
        .incidencia-aviso-112 {
            font-size: var(--vm-fs-cuerpo); color: #a34e00; background: #fff3e0;
            border-radius: 10px; padding: 8px 12px; margin-top: 8px; line-height: var(--vm-lh-cuerpo);
        }
        .incidencia-aviso-reporte-unico {
            font-size: var(--vm-fs-cuerpo); color: #7a1f1f; background: #fdecea;
            border-radius: 10px; padding: 10px 14px; margin: 16px 0 6px; line-height: var(--vm-lh-cuerpo);
        }
        .previa-foto-incidencia { display: flex; align-items: center; gap: 12px; margin-top: 8px; }
        .previa-foto-incidencia img { width: 90px; height: 68px; border-radius: 8px; object-fit: cover; border: 1px solid #e2ded2; }
        /* Solo en celular (a pedido del cliente) — letra más chica en
           los 3 avisos de texto largo del panel de incidencias, y
           espacio entre secciones reducido al mínimo, para que no haga
           falta desplazarse tanto hasta el final del formulario. Sin
           tocar escritorio ni tablet — ahí se quedan como estaban.
           Scopeado a #panelIncidencia a propósito: .perfil-subtitulo y
           .campo-reserva se comparten con otros paneles que no deben
           verse afectados. */
        @media (max-width: 719px) {
            #panelIncidencia .perfil-subtitulo,
            #panelIncidencia .incidencia-aviso-112,
            #panelIncidencia .incidencia-aviso-reporte-unico {
                font-size: var(--vm-fs-caption);
            }
            #panelIncidencia .perfil-subtitulo { margin-bottom: var(--vm-esp-texto); }
            #panelIncidencia .incidencia-aviso-112 { margin-top: var(--vm-esp-pegado); padding: 6px 10px; }
            #panelIncidencia .incidencia-aviso-reporte-unico { margin: var(--vm-esp-texto) 0 var(--vm-esp-pegado); padding: 8px 10px; }
            #panelIncidencia .campo-reserva { margin-bottom: var(--vm-esp-texto); }
        }
        .previa-foto-incidencia button {
            background: #fdecea; color: #c62828; border: none; padding: 7px 12px;
            border-radius: 8px; font-size: var(--vm-fs-cuerpo); font-weight: 600; cursor: pointer;
        }
        .incidencia-nota-foto { font-size: var(--vm-fs-caption); color: #999; margin-top: var(--vm-esp-texto); line-height: var(--vm-lh-cuerpo); }
        .incidencia-cargando-limite { text-align: center; padding: 30px 0; color: #999; font-size: var(--vm-fs-cuerpo); }
        .incidencia-limite-alcanzado { text-align: center; padding: 20px 0 8px; }
        .incidencia-limite-icono { font-size: var(--vm-fs-hero-grande); margin-bottom: 12px; }
        .incidencia-limite-alcanzado p { font-size: var(--vm-fs-cuerpo-destacado); color: var(--vm-texto); line-height: var(--vm-lh-cuerpo); margin-bottom: var(--vm-esp-accion); text-align: justify; }
        /* ── Justificado de los textos largos del panel de incidencia
           (Sesión 47, a pedido de Jaime — "no se ven bien organizados")
           — acotado a #panelIncidencia a propósito, porque .perfil-subtitulo
           y .perfil-mensaje son clases compartidas con el panel de perfil,
           que no se toca acá. ── */
        #panelIncidencia .perfil-subtitulo,
        #panelIncidencia .incidencia-aviso-112,
        #panelIncidencia .incidencia-nota-foto,
        #panelIncidencia .incidencia-aviso-reporte-unico,
        #panelIncidencia .perfil-mensaje {
            text-align: justify;
        }
        .perfil-cerrar {
            position: absolute; top: 14px; right: 16px; background: none; border: none;
            font-size: var(--vm-fs-titulo-grande); color: #999; cursor: pointer; line-height: 1; padding: 4px;
        }
        .perfil-titulo {
            font-family: 'Inter', sans-serif; font-weight: 600; font-size: var(--vm-fs-titulo-grande);
            color: var(--vm-texto); margin-bottom: 6px; padding-right: 24px;
        }
        .perfil-subtitulo { font-size: var(--vm-fs-cuerpo); color: #5c7d84; margin-bottom: 18px; line-height: var(--vm-lh-cuerpo); }
        /* Subtítulo de sección dentro de "Configuración" (Sesión 71) —
           separa visualmente "Datos personales" de "Autorizaciones"
           ahora que viven juntas en una sola pantalla, sin que se lea
           como un solo bloque largo de campos sin ninguna pausa. */
        .perfil-seccion-titulo {
            font-family: 'Inter', sans-serif; font-weight: 700; font-size: var(--vm-fs-cuerpo-destacado);
            color: var(--vm-texto); margin: var(--vm-esp-accion) 0 var(--vm-esp-texto);
            padding-top: var(--vm-esp-accion); border-top: 1px solid #eee;
        }
        .perfil-seccion-titulo:first-of-type { border-top: none; padding-top: 0; margin-top: 0; }

        /* ── Mis Reservas / Mis Citas Previas (Sesión 57) ── */
        .tarjeta-mrc {
            background: #f9f9f7; border-radius: 12px; padding: 14px; margin-bottom: 12px; border: 1px solid #eee;
        }
        .tarjeta-mrc .negocio-nombre-mrc { font-weight: 700; color: #1b3a2f; font-size: var(--vm-fs-cuerpo-destacado); margin-bottom: var(--vm-esp-pegado); }
        .tarjeta-mrc .fecha-hora-mrc { font-size: var(--vm-fs-cuerpo); color: #666; margin-bottom: var(--vm-esp-texto); }
        .badge-estado-mrc { display: inline-block; padding: 3px 10px; border-radius: 20px; font-size: var(--vm-fs-caption); font-weight: 700; margin-bottom: 6px; }
        .badge-estado-mrc.pendiente { background: #fff3cd; color: #8a6d00; }
        .badge-estado-mrc.confirmada { background: #e8f5e9; color: #2e7d32; }
        .badge-estado-mrc.ejecutada { background: #e3f2fd; color: #1565c0; }
        .badge-estado-mrc.no_ejecutada { background: #f5f5f5; color: #888; }
        .badge-estado-mrc.anulada { background: #ffebee; color: #c62828; }
        .mensaje-estado-mrc { font-size: var(--vm-fs-cuerpo); color: #555; margin-bottom: var(--vm-esp-texto); line-height: var(--vm-lh-cuerpo); }
        .botonera-mrc { display: flex; gap: 8px; flex-wrap: wrap; }
        .btn-aceptar-mrc {
            background: #2e7d32; color: white; border: none; border-radius: 8px;
            padding: 7px 14px; font-size: var(--vm-fs-cuerpo); font-weight: 600; cursor: pointer;
        }
        .btn-aceptar-mrc:hover, .btn-aceptar-mrc:active { background: #266b2a; }
        .btn-cancelar-mrc {
            background: none; border: 1.5px solid #e57373; color: #c62828; border-radius: 8px;
            padding: 7px 14px; font-size: var(--vm-fs-cuerpo); font-weight: 600; cursor: pointer;
        }
        .btn-cancelar-mrc:hover, .btn-cancelar-mrc:active { background: #ffebee; }

        .perfil-opcion-acceso {
            width: 100%; display: flex; align-items: center; justify-content: center; gap: 10px;
            padding: 12px; border-radius: 12px; border: 1.5px solid #e2ded2; background: white;
            font-size: var(--vm-fs-cuerpo-destacado); font-weight: 600; color: var(--vm-texto); cursor: pointer; margin-bottom: 10px;
            font-family: inherit;
        }
        .perfil-opcion-acceso:hover, .perfil-opcion-acceso:active { border-color: var(--vm-azul); }
        .perfil-opcion-acceso svg, .perfil-opcion-acceso img { width: 18px; height: 18px; flex-shrink: 0; }
        .perfil-opcion-acceso.primaria { background: var(--vm-azul); color: white; border-color: var(--vm-azul); }
        /* Punto 5: el camino "ya tengo cuenta". Mismo cuerpo que su hermano
           de al lado —para que se lean como dos opciones equivalentes— con
           el azul de la Sesión 55 conservado en borde y texto. */
        .perfil-opcion-acceso.acento { border-color: #1565c0; color: #1565c0; }
        .perfil-separador {
            display: flex; align-items: center; gap: 10px; margin: 16px 0;
            color: #b3a999; font-size: var(--vm-fs-caption); text-transform: uppercase; letter-spacing: 0.06em;
        }
        .perfil-separador::before, .perfil-separador::after { content: ''; flex: 1; height: 1px; background: #e2ded2; }
        .perfil-enlace-alternar { text-align: center; font-size: var(--vm-fs-cuerpo); color: var(--vm-azul); cursor: pointer; margin-top: 6px; }
        .perfil-nota-anonimo { font-size: var(--vm-fs-caption); color: #999; text-align: center; margin-top: var(--vm-esp-accion); line-height: var(--vm-lh-cuerpo); }

        /* ── Punto 5 de la auditoría (22 de septiembre de 2026) — las tres
           piezas que ordenan la pantalla de acceso. Ver el comentario de
           renderPerfilAcceso() en mapa-v3.js.

           .perfil-nota-google: la línea que aclara que Google sirve tanto
           para entrar como para registrarse. Va pegada al botón (margen
           negativo arriba para comerse el margin-bottom de 10px del propio
           botón) porque si flota suelta se lee como texto de otra cosa.

           .perfil-grupo-acceso + .perfil-grupo-etiqueta: cada camino de
           correo bajo su rótulo. La etiqueta usa el mismo gris azulado de
           los subtítulos, no el negro del texto, para que rotule sin
           competir con el botón que tiene debajo. */
        .perfil-nota-google {
            font-size: var(--vm-fs-caption); color: #8a8578; text-align: center;
            margin: -4px 0 2px; line-height: var(--vm-lh-cuerpo);
        }
        .perfil-grupo-acceso { margin-bottom: 14px; }
        .perfil-grupo-acceso:last-of-type { margin-bottom: 0; }
        .perfil-grupo-acceso .perfil-opcion-acceso { margin-bottom: 0; }
        .perfil-grupo-etiqueta {
            font-size: var(--vm-fs-caption); font-weight: 700; color: #5c7d84;
            text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 6px;
        }

        .perfil-campo { margin-bottom: 12px; }
        .perfil-campo label { display: block; font-size: var(--vm-fs-cuerpo); font-weight: 600; color: #5c7d84; margin-bottom: var(--vm-esp-pegado); }
        .perfil-campo input, .perfil-campo select {
            width: 100%; padding: 10px 12px; border: 1.5px solid #e2ded2; border-radius: 10px;
            font-size: var(--vm-fs-cuerpo-destacado); font-family: inherit; background: white; color: var(--vm-texto);
        }
        .perfil-campo input:focus, .perfil-campo select:focus { outline: none; border-color: var(--vm-azul); }
        .perfil-fila-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
        .perfil-mensaje { font-size: var(--vm-fs-cuerpo); text-align: center; margin: 6px 0 2px; min-height: 16px; }
        .perfil-mensaje.error { color: #c62828; }
        .perfil-mensaje.ok { color: #2e7d32; }
        .perfil-btn-guardar {
            width: 100%; padding: 13px; border-radius: 12px; border: none; background: var(--viu-acento);
            color: white; font-size: var(--vm-fs-cuerpo-destacado); font-weight: 700; cursor: pointer; margin-top: 8px; font-family: inherit;
            /* Bisel sutil, sin sombra flotante (a pedido del cliente) —
               mismo concepto que ya tienen otros botones grandes de la
               app (Ir/Compartir/Llamar, Reservar). */
            box-shadow: inset 0 1px 0 rgba(255,255,255,0.35), inset 0 -2px 3px rgba(0,0,0,0.15);
        }
        .perfil-btn-guardar:disabled { background: #bbb; cursor: default; }
        /* Pestañas de "Configuración" (Sesión 71-72) — Mis Datos /
           Autorización de Datos, en vez de un solo bloque largo. */
        .selector-pestanas-config {
            display: flex; gap: 4px; background: #f2f2f2; border-radius: 12px; padding: 4px;
            margin-bottom: var(--vm-esp-seccion);
        }
        .pestana-config {
            flex: 1; border: none; background: none; padding: 9px 6px; border-radius: 9px;
            font-family: 'Inter', sans-serif; font-weight: 600; font-size: var(--vm-fs-cuerpo);
            color: #777; cursor: pointer;
        }
        .pestana-config.activa { background: white; color: var(--vm-texto); box-shadow: 0 1px 4px rgba(0,0,0,0.12); }
        .perfil-titulo-tab {
            font-family: 'Inter', sans-serif; font-weight: 700; font-size: var(--vm-fs-cuerpo-destacado);
            color: var(--vm-texto); margin-bottom: var(--vm-esp-texto);
        }
        /* Texto explicativo bajo "Mis Autorizaciones Viu Map" (a pedido
           del cliente) — para que el usuario entienda el para qué antes
           de decidir, no solo vea 2 casillas sueltas. */
        .perfil-autorizaciones-intro {
            font-size: var(--vm-fs-cuerpo); color: #5c7d84; line-height: var(--vm-lh-cuerpo);
            margin-bottom: var(--vm-esp-seccion);
        }
        /* "Mis Rutas Viu" bloqueada (a pedido del cliente) — si la
           movilidad no está activa, en vez del resumen se ve este
           aviso con acceso directo a activarla. */
        .perfil-bloqueo-rutas { text-align: center; padding: 20px 4px; }
        .icono-bloqueo-rutas { font-size: 48px; margin-bottom: var(--vm-esp-seccion); }
        .perfil-bloqueo-rutas p {
            font-size: var(--vm-fs-cuerpo); color: var(--vm-texto); line-height: var(--vm-lh-cuerpo);
            margin-bottom: var(--vm-esp-accion);
        }
        .resumen-rutas-viu {
            font-size: var(--vm-fs-cuerpo); color: var(--vm-texto); line-height: var(--vm-lh-cuerpo);
            background: var(--vm-fondo-calido); border-radius: 12px; padding: 14px;
        }
        /* Confirmación de eliminar cuenta, dentro del propio modal (a
           pedido del cliente) — antes era un confirm() nativo del
           navegador, sin ningún detalle de qué se borra. */
        #confirmacionEliminarCuenta { text-align: center; padding: 10px 4px; }
        .icono-prohibido-eliminar { font-size: 48px; margin-bottom: var(--vm-esp-seccion); }
        #confirmacionEliminarCuenta p {
            font-size: var(--vm-fs-cuerpo); color: var(--vm-texto); line-height: var(--vm-lh-cuerpo);
            margin-bottom: var(--vm-esp-accion); text-align: left;
        }
        /* Sesión 55, a pedido de Jaime — el enlace para iniciar sesión en la
           pantalla inicial de acceso era solo texto con subrayado
           (perfil-enlace-alternar), poco visible como punto de entrada
           real para iniciar sesión. Azul a propósito, distinto del verde
           de acción principal (Guardar, Continuar con Google) — para que
           se lea como "otro camino", no como la acción primaria.

           🔴 22 de septiembre de 2026 (Punto 5): esta clase YA NO SE USA en
           la pantalla de acceso. No se borra porque la petición de Jaime
           que la creó sigue viva —"que iniciar sesión se vea"— y hay que
           poder volver atrás en un minuto si el rediseño no convence.
           Lo que se hizo en su lugar: el botón de iniciar sesión pasó a ser
           un botón entero (no un enlace), SUBIÓ por encima de "crear
           cuenta", lleva su propio rótulo encima, y conserva el azul en
           borde y texto vía `.perfil-opcion-acceso.acento`. Se le quitó
           solo el relleno macizo, que competía con el botón primario de
           Google y dejaba la jerarquía al revés: el camino de vuelta
           pesaba más que la acción principal. */
        .perfil-btn-login {
            width: 100%; padding: 13px; border-radius: 12px; border: none; background: #1565c0;
            color: white; font-size: var(--vm-fs-cuerpo-destacado); font-weight: 700; cursor: pointer; margin-top: 10px; font-family: inherit;
        }
        .perfil-btn-login:hover, .perfil-btn-login:active { background: #0d47a1; }
        .perfil-btn-secundario {
            width: 100%; padding: 11px; border-radius: 12px; border: 1.5px solid #e2ded2; background: none;
            color: #888; font-size: var(--vm-fs-cuerpo); font-weight: 600; cursor: pointer; margin-top: 8px; font-family: inherit;
        }
        /* "Pasar actividades..." (a pedido del cliente) — dorado sólido,
           mismo color que Donar a la Falla, con el mismo concepto de
           bisel que ya tienen Ir/Compartir/Llamar (luz arriba, sombra
           suave hacia adentro abajo) pero SIN sombra flotante — no es
           un botón "pegado"/sticky, vive dentro del flujo normal del
           panel de perfil. */
        .btn-pasar-agenda-falla {
            background: #f9a825; color: #5c3c00; border: none;
            box-shadow: inset 0 1px 0 rgba(255,255,255,0.35), inset 0 -2px 3px rgba(0,0,0,0.15);
        }
        .indicador-activo-mia { display: block; font-size: var(--vm-fs-caption); font-weight: 700; color: #5c3c00; margin-top: var(--vm-esp-pegado); }
        /* Línea de "cómo desactivarlo" — más rojiza a propósito (a
           pedido del cliente), para que se entienda de inmediato que
           hay una alternativa para deshacer lo que se acaba de activar. */
        .aviso-desactivar-mia { color: #b3261e; font-weight: 600; }
        /* El SVG de Google no trae ancho/alto propio (solo viewBox) — sin
           esto, el navegador lo escala a su tamaño por defecto, mucho más
           grande de lo que se ve acá. Apuntado al id, no a la clase
           general, para no afectar otros usos de perfil-btn-secundario. */
        #btnVincularGoogle svg { width: 18px; height: 18px; display: block; }
        .perfil-encabezado-cuenta { display: flex; align-items: center; gap: 12px; margin-bottom: 18px; }
        /* Solo en celular (a pedido del cliente) — espacios reducidos al
           mínimo en toda la pantalla de Configuración, para que se vean
           las 2 pestañas sin desplazarse tanto. Escritorio y tablet se
           quedan exactamente igual que están — por eso el ajuste vive
           en un @media aparte, apuntado a .pantalla-configuracion (el
           envoltorio propio de esta pantalla), no a las clases
           compartidas directamente. */
        @media (max-width: 719px) {
            .pantalla-configuracion .perfil-encabezado-cuenta { margin-bottom: var(--vm-esp-texto); }
            .pantalla-configuracion .perfil-titulo { margin-bottom: var(--vm-esp-pegado); }
            .pantalla-configuracion .perfil-mensaje:empty { margin: 0; }
            .pantalla-configuracion .selector-pestanas-config { margin-bottom: var(--vm-esp-texto); }
            .pantalla-configuracion .perfil-titulo-tab { margin-bottom: var(--vm-esp-pegado); }
            .pantalla-configuracion .perfil-campo { margin-bottom: var(--vm-esp-texto); }
            .pantalla-configuracion .perfil-autorizaciones-intro { margin-bottom: var(--vm-esp-texto); }
            .pantalla-configuracion .perfil-btn-guardar { margin-top: var(--vm-esp-pegado); }
        }
        .perfil-encabezado-cuenta .avatar-cuenta { width: 48px; height: 48px; border-radius: 50%; overflow: hidden; flex-shrink: 0; }
        .perfil-encabezado-cuenta .nombre-cuenta { font-weight: 700; color: var(--vm-texto); font-size: var(--vm-fs-subtitulo); }
        .perfil-encabezado-cuenta .metodo-cuenta { font-size: var(--vm-fs-caption); color: #999; }

        /* ── Mi Falla Favorita (Sesión 52) — tarjeta con imagen principal,
           nombre, lema y descripción de la Escultura de este año. ── */
        .perfil-falla-fav-imagen-wrap {
            width: 100%; aspect-ratio: 4/3; border-radius: 14px; overflow: hidden;
            background: #eee; margin: 10px 0 14px;
        }
        .perfil-falla-fav-imagen-wrap img { width: 100%; height: 100%; object-fit: cover; display: block; }
        .perfil-falla-fav-nombre { font-family: 'Inter', sans-serif; font-weight: 600; font-size: var(--vm-fs-subtitulo); color: #1b3a2f; margin-bottom: 2px; }
        .perfil-falla-fav-lema { font-size: var(--vm-fs-cuerpo); color: #777; font-style: italic; margin-bottom: var(--vm-esp-texto); }
        .perfil-falla-fav-descripcion { font-size: var(--vm-fs-cuerpo); color: #444; line-height: var(--vm-lh-cuerpo); margin-bottom: var(--vm-esp-pegado); }

        /* Avatar de iniciales en el botón del nav — mismo tamaño (34px)
           que el ícono genérico que reemplaza, para no romper el layout. */
        .avatar-iniciales-nav {
            width: 34px; height: 34px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
            color: white; font-weight: 700; font-size: var(--vm-fs-cuerpo); font-family: 'Inter', sans-serif; flex-shrink: 0;
        }

        .perfil-campo-hint { font-size: var(--vm-fs-caption); color: #999; margin-top: var(--vm-esp-pegado); }
        .perfil-campo-hint.ok { color: #2e7d32; }
        .perfil-campo-hint.error { color: #c62828; }
        .perfil-edad-calculada { font-size: var(--vm-fs-caption); color: #5c7d84; margin-top: var(--vm-esp-pegado); }

        /* ── Verificación de teléfono (Fase 2, Incidencias, Sesión 47) ── */
        .fila-telefono-verificacion { display: grid; grid-template-columns: 1fr auto; gap: 8px; align-items: center; }
        .btn-validar-telefono {
            padding: 10px 14px; border-radius: 10px; border: 1.5px solid var(--vm-azul);
            background: white; color: var(--vm-azul); font-size: var(--vm-fs-cuerpo); font-weight: 700;
            cursor: pointer; white-space: nowrap; font-family: inherit;
        }
        .btn-validar-telefono:hover, .btn-validar-telefono:active { background: var(--vm-azul-suave); }
        .btn-validar-telefono:disabled { opacity: 0.6; cursor: default; }
        .btn-validar-telefono.verificado {
            background: #e8f5e9; border-color: #2e7d32; color: #2e7d32; cursor: default;
        }

        /* Modal de verificación — centrado en las 2 resoluciones a
           propósito, a diferencia del panel de perfil (hoja que sube en
           celular): es una interacción corta, de un solo paso, no
           necesita el mismo tratamiento visual que un panel completo. */
        .fondo-verificacion-telefono {
            position: fixed; inset: 0; z-index: 3300;
            background: rgba(14, 34, 38, 0.6);
            display: none; align-items: center; justify-content: center; padding: 20px;
        }
        .fondo-verificacion-telefono.abierto { display: flex; animation: fbFundido 0.2s ease; }
        .caja-verificacion-telefono {
            position: relative; background: var(--vm-fondo-calido); width: 100%; max-width: 360px;
            border-radius: 20px; padding: 26px 22px 22px; box-shadow: 0 14px 44px rgba(0,0,0,0.3);
        }
        .cerrar-verificacion-telefono {
            position: absolute; top: 12px; right: 14px; background: none; border: none;
            font-size: var(--vm-fs-titulo); color: #999; cursor: pointer; line-height: 1; padding: 4px;
        }
        .titulo-verificacion-telefono {
            font-family: 'Inter', sans-serif; font-weight: 600; font-size: var(--vm-fs-titulo);
            color: var(--vm-texto); margin-bottom: 6px; padding-right: 20px;
        }
        .texto-verificacion-telefono { font-size: var(--vm-fs-cuerpo); color: #5c7d84; margin-bottom: var(--vm-esp-accion); line-height: var(--vm-lh-cuerpo); }
        .input-codigo-verificacion {
            width: 100%; padding: 12px; border: 1.5px solid #e2ded2; border-radius: 10px;
            font-size: var(--vm-fs-titulo-grande); font-weight: 700; letter-spacing: 8px; text-align: center;
            font-family: inherit; color: var(--vm-texto);
        }
        .input-codigo-verificacion:focus { outline: none; border-color: var(--vm-azul); }
        .mensaje-verificacion-telefono { font-size: var(--vm-fs-cuerpo); text-align: center; margin: 8px 0 2px; min-height: 16px; }
        .mensaje-verificacion-telefono.error { color: #c62828; }
        .mensaje-verificacion-telefono.ok { color: #2e7d32; }
        .btn-confirmar-codigo {
            width: 100%; padding: 13px; border-radius: 12px; border: none; background: var(--viu-acento);
            color: white; font-size: var(--vm-fs-cuerpo-destacado); font-weight: 700; cursor: pointer; margin-top: 10px; font-family: inherit;
        }
        .btn-confirmar-codigo:disabled { background: #bbb; cursor: default; }
        .btn-reenviar-codigo {
            width: 100%; padding: 10px; border-radius: 12px; border: none; background: none;
            color: var(--vm-azul); font-size: var(--vm-fs-cuerpo); font-weight: 600; cursor: pointer; margin-top: 4px; font-family: inherit;
        }

        .perfil-consentimientos { margin: 16px 0; padding: 12px 14px; background: #FFF7EA; border-radius: 12px; }
        .fila-check-perfil { display: flex; align-items: flex-start; gap: 8px; margin-bottom: 8px; }
        .fila-check-perfil:last-child { margin-bottom: 0; }
        .fila-check-perfil input[type="checkbox"] { margin-top: 2px; flex-shrink: 0; width: 16px; height: 16px; }
        .fila-check-perfil label { font-size: var(--vm-fs-cuerpo); color: var(--vm-texto); line-height: var(--vm-lh-cuerpo); }
        .fila-check-perfil label a { color: var(--vm-azul); font-weight: 600; }
        .perfil-pista-consentimiento { font-size: var(--vm-fs-caption); color: #a34e00; margin: -4px 0 8px 24px; }

        .perfil-btn-eliminar {
            width: 100%; padding: 10px; border-radius: 12px; border: none; background: none;
            color: #c62828; font-size: var(--vm-fs-cuerpo); font-weight: 600; cursor: pointer; margin-top: 14px; font-family: inherit;
        }
        .perfil-btn-eliminar:hover, .perfil-btn-eliminar:active { text-decoration: underline; }

        /* Lector de Términos/Privacidad dentro de la app — mismo criterio
           que ya usa NexIABus: no navega afuera, así el formulario de
           registro (con lo ya escrito) nunca se pierde. */
        .fondo-lector-perfil {
            position: fixed; inset: 0; z-index: 3200;
            background: rgba(14, 34, 38, 0.55);
            display: none; align-items: center; justify-content: center; padding: 20px;
        }
        .fondo-lector-perfil.abierto { display: flex; }
        .panel-lector-perfil {
            background: white; border-radius: 18px; width: 100%; max-width: 640px; max-height: 88vh;
            padding: 20px; box-shadow: 0 14px 44px rgba(0,0,0,0.3); position: relative; display: flex; flex-direction: column;
        }
        .panel-lector-perfil iframe { width: 100%; flex: 1; min-height: 55vh; border: 1px solid #eee; border-radius: 12px; }
        .lector-perfil-pie { display: flex; justify-content: space-between; align-items: center; margin-top: 12px; }
        .lector-perfil-pie a { font-size: var(--vm-fs-cuerpo); color: var(--vm-azul); font-weight: 600; }

        /* Los 3 botones de etiqueta dentro de la ficha ampliada (reemplazan
           a la única estrella de antes) */
        .fila-etiquetas-ficha { display: flex; gap: 6px; justify-content: center; margin-bottom: var(--vm-esp-seccion); }

        /* .btn-footer-falla ahora solo la usa "Volver" dentro del modal
           de Agenda — el botón de Agenda de la ficha ya no vive en una
           fila ancha propia, ni en la Comisión ni en la Escultura (ver
           fila-etiquetas-ficha / fichaRedes más abajo, y armarFichaNegocio
           en el script). */
        .fila-footer-falla { display: flex; gap: 8px; margin-top: 14px; }
        .btn-footer-falla {
            flex: 1; border: 1.5px solid #ddd; background: white; border-radius: 10px;
            padding: 11px; font-size: var(--vm-fs-cuerpo); font-weight: 700; color: #1b3a2f; cursor: pointer;
        }
        .btn-footer-falla:hover, .btn-footer-falla:active { background: #f7f7f7; }
        .btn-etiqueta-ficha {
            border: none; background: #f2f2f2; border-radius: 50%;
            width: 34px; height: 34px; font-size: var(--vm-fs-subtitulo); cursor: pointer;
            display: flex; align-items: center; justify-content: center;
        }
        /* Marcado "activo" con el color propio de cada acción (a pedido
           del cliente) — antes las 3 usaban el mismo rosa clarito sin
           distinguir cuál se activó. El fondo llena el botón completo
           (círculo entero), no solo un tinte del ícono. */
        #btnFichaDestacado.activa, #btnEsculturaDestacado.activa { background: rgba(249,168,37,0.3); }
        #btnFichaFavorito.activa { background: rgba(229,57,53,0.3); }
        #btnFichaPorVisitar.activa, #btnEsculturaPorVisitar.activa { background: rgba(26,115,232,0.3); }
        /* Mismo motivo que .btn-etiqueta img (ver más arriba) —
           !important necesario para ganarle al tamaño en línea que
           trae el <img> que arma icono(). */
        .btn-etiqueta-ficha img { width: 100% !important; height: 100% !important; vertical-align: 0 !important; object-fit: contain; display: block; }
        /* Animación de clic (a pedido del cliente) — mismo achique breve
           en las 3 apariciones de estos botones (panel del mapa, ficha,
           listas de resultados), para que se sienta el toque sin
           importar desde dónde se lo use. */
        .btn-etiqueta, .btn-etiqueta-ficha, .btn-accion-resultado.btn-etiqueta-resultado {
            transition: transform 0.12s ease, background 0.15s ease;
        }
        .btn-etiqueta:active, .btn-etiqueta-ficha:active, .btn-accion-resultado.btn-etiqueta-resultado:active {
            transform: scale(0.88);
        }

        /* Botón "Ir" del panel de Favoritos — círculo compacto con ícono +
           palabra. A pedido del cliente, vuelve al verde de marca
           (var(--viu-acento)) — no lo comparte con el botón "Ir" de la
           ficha de negocio, que es --btn-accion-principal y queda tal
           cual está (gris neutro). */
        .btn-ir {
            display: flex; align-items: center; gap: 4px;
            background: var(--viu-acento); color: white;
            border-radius: 16px; padding: 5px 12px;
            font-size: var(--vm-fs-cuerpo); font-weight: 700; white-space: nowrap;
        }

        /* Botón de estrella dentro del popup de cada negocio */
        .btn-estrella-popup {
            border: none; background: none; font-size: var(--vm-fs-titulo-grande); cursor: pointer;
            padding: 4px; line-height: 1;
        }

        /* ── Ficha ampliada de negocios con Pauta ── */
        .fondo-ficha {
            position: fixed; inset: 0; background: rgba(0,0,0,0.5); z-index: 3000;
            display: none; align-items: center; justify-content: center; padding: 16px;
        }
        .fondo-ficha.abierto { display: flex; }
        .ficha-negocio {
            background: white; border-radius: 16px; width: 100%; max-width: 380px;
            max-height: 85vh; overflow-y: auto; padding: 22px; position: relative;
            box-shadow: 0 20px 50px rgba(0,0,0,0.3);
        }
        .btn-cerrar-ficha {
            position: absolute; top: 14px; right: 14px; border: none; background: #f2f2f2;
            width: 30px; height: 30px; border-radius: 50%; font-size: var(--vm-fs-subtitulo); cursor: pointer; color: #555;
            z-index: 10; box-shadow: 0 1px 6px rgba(0,0,0,0.15);
        }
        .btn-cerrar-ficha:hover, .btn-cerrar-ficha:active { background: #e5e5e5; }
        /* ── Panel de clima — mismo marco visual que la ficha de negocio ── */
        .ficha-clima { padding-top: 26px; }
        /* Modal más ancho en escritorio/tablet (a pedido del cliente,
           Sesión 76) — en esos tamaños hay espacio de sobra, y la
           franja de 6 horas se veía apretada con el mismo ancho fijo
           que usa el resto de las fichas (380px, pensado para celular).
           Nunca afecta a celular (<720px) — la Fase 8 ya definió ese
           corte para todo el mapa, se reutiliza el mismo acá. */
        @media (min-width: 720px) {
            .ficha-clima { max-width: 460px; }
        }
        /* ── Modal de horarios de RENFE (Sesión 79) — diseño provisto
           por el cliente como mockup, adaptado al ancho real de las
           fichas del mapa. Se dejó afuera la foto del tren del mockup
           a propósito: el cliente ofreció pasar una en alta resolución
           y libre de derechos, se suma después sin tocar el resto. ── */
        .ficha-horarios-renfe { max-width: 420px; }
        @media (min-width: 720px) { .ficha-horarios-renfe { max-width: 480px; } }
        /* Título/subtítulo de este modal en particular (a pedido del
           cliente) — subtítulo un escalón más grande que el resto de
           los modales (14px en vez de 13). Título y subtítulo mucho
           más juntos entre sí (2px de margen abajo del título, 1px
           abajo del subtítulo) — ambos ajustes viven acotados a
           .ficha-horarios-renfe, sin tocar el resto de los modales que
           comparten estas mismas clases. */
        .ficha-horarios-renfe .perfil-titulo { margin-bottom: 0px; }
        /* Solo en celular (a pedido del cliente) — el título de este
           modal en particular baja un escalón de tamaño (de
           titulo-grande a titulo), sin afectar tablet ni escritorio. */
        @media (max-width: 719px) {
            .ficha-horarios-renfe .perfil-titulo { font-size: var(--vm-fs-titulo); }
        }
        .ficha-horarios-renfe .perfil-subtitulo {
            font-size: var(--vm-fs-cuerpo-destacado); margin-bottom: 5px;
        }
        /* Foto propia del cliente (Sesión 79-82) — recortada primero al
           área real con contenido (el PNG original traía un margen
           transparente alrededor de la foto, 43-88px según el lado,
           que al aplanarse sobre blanco se convertía en un marco
           blanco alrededor — eso era lo que se veía "más angosta que
           el texto"). Proporción real después del recorte: 1.668:1, no
           la 1.535:1 de antes (esa incluía el margen transparente).
           aspect-ratio fija en esa proporción nueva, con
           object-fit:cover para que nunca se deforme sea cual sea el
           ancho del modal. Sin bordes redondeados a propósito (a
           pedido del cliente): así queda completamente alineada con
           los extremos rectos de las filas de horarios de más abajo,
           en vez de curvarse en las esquinas mientras todo lo demás es
           recto. box-sizing y max-width explícitos (para que el ancho
           coincida exacto con el del texto de abajo, ninguno de los
           dos con ningún margen lateral propio que los desalinee entre
           sí). Casi pegada al texto de abajo (a pedido del cliente,
           "nota al pie de la foto"). */
        .renfe-horario-foto {
            width: 100%; max-width: 100%; box-sizing: border-box;
            aspect-ratio: 1.841; object-fit: cover;
            display: block; margin: 6px 0;
            /* Borde interno blanco, solo arriba y abajo (a pedido del
               cliente, prueba) — con box-sizing:border-box ya puesto,
               el borde se resta del propio ancho/alto declarado (la
               proporción 1.668) en vez de sumarse por fuera, así que
               queda "hacia adentro" de la foto, no agrandando su caja.
               Nada en los lados, a propósito. */
            border-top: 2px solid white; border-bottom: 2px solid white;
        }
        /* Solo en celular (a pedido del cliente) — imagen distinta de
           verdad (no una aproximación con aspect-ratio): un <picture>
           más arriba en el HTML sirve una foto recortada a mano 4px
           arriba y 3px abajo MÁS que la base, con su propia proporción
           real (1.857, no la 1.841 de la base). El aspect-ratio de acá
           tiene que coincidir con esa proporción real — si no, object-
           fit:cover le seguiría recortando de más encima del recorte
           ya hecho a mano. Regla puesta DESPUÉS de la base a propósito
           (la lección de la vez pasada): con la misma especificidad,
           la que va después en el archivo es la que gana. */
        @media (max-width: 719px) {
            .renfe-horario-foto { aspect-ratio: 2.217; }
        }
        .renfe-horario-descripcion {
            /* Un escalón más chico que el resto de los modales (a
               pedido del cliente) — este texto es más largo que un
               subtítulo típico, así que se achica un poco para no
               competir tanto con el título. Justificado y del mismo
               ancho que la foto de arriba. Interlineado apretado (a
               pedido del cliente) — se lee más como una nota al pie de
               la foto que como un párrafo aparte. */
            width: 100%; box-sizing: border-box;
            font-size: var(--vm-fs-caption); color: #5c7d84; line-height: 1.15;
            text-align: justify; margin: 0 0 var(--vm-esp-seccion);
        }
        .renfe-seccion-titulo {
            font-family: 'Inter', sans-serif; font-weight: 800; font-size: var(--vm-fs-cuerpo-destacado);
            color: #14204A; margin: var(--vm-esp-seccion) 0 var(--vm-esp-texto);
        }
        /* 8 por fila, más chicos (a pedido del cliente) — son de solo
           lectura, sin ninguna interacción, así que no hace falta que
           cada uno sea grande como un botón; con 8 por fila el modal
           queda bastante menos largo, y de paso ayuda a la lectura en
           celular (menos scroll para ver todo el horario del día). */
        .grilla-horarios-renfe { display: grid; grid-template-columns: repeat(8, 1fr); gap: 5px; }
        /* Verde por defecto = todavía falta por pasar (a pedido del
           cliente). Roja + tachada = ya pasó esa hora. Dorada + borde =
           la próxima salida, la misma que ya muestra el botón del mapa
           — se calcula comparando cada horario con la hora actual real
           de Cullera (minutosDelDiaEnCullera), no a mano. */
        .chip-horario-renfe {
            text-align: center; padding: 5px 1px; border-radius: 6px; font-weight: 700;
            font-size: var(--vm-fs-caption); font-family: 'Inter', sans-serif;
            background: #DCF2E3; color: #1b5e20; border: 1px solid transparent;
        }
        /* Oscurecimiento al tocar/pasar el puntero (a pedido del
           cliente) — solo en las verdes, que son las únicas
           interactivas (abren el selector de agendar); rojas y dorada
           se quedan igual, no hacen nada al tocarlas. */
        .chip-horario-renfe:not(.renfe-pasado):not(.renfe-proximo) {
            cursor: pointer; transition: background 0.12s;
        }
        .chip-horario-renfe:not(.renfe-pasado):not(.renfe-proximo):hover,
        .chip-horario-renfe:not(.renfe-pasado):not(.renfe-proximo):active {
            background: #b9e0c4;
        }
        .chip-horario-renfe.renfe-pasado { background: #FAE1E1; color: #8a2d2d; text-decoration: line-through; opacity: 0.75; }
        .chip-horario-renfe.renfe-proximo { background: #FFF6D6; color: #8a6d00; border-color: #D79109; font-weight: 800; }
        .renfe-horario-fuente { text-align: center; font-size: var(--vm-fs-caption); color: #999; margin-top: var(--vm-esp-seccion); }
        .clima-hero {
            position: relative; overflow: hidden;
            background: linear-gradient(180deg, #6EC6FF 0%, #BFE8FF 100%);
            border-radius: 14px; padding: 18px 8px; display: flex; align-items: center;
            justify-content: space-between; gap: 6px; margin-bottom: 14px;
            /* Alto fijo (a pedido del cliente) — corregido en la segunda
               vuelta: el número anterior (132px) partía de un supuesto
               equivocado (pensaba que ícono y texto iban lado a lado; en
               realidad van apilados en una sola columna: fecha, ícono,
               temperatura, máx/mín, sensación y, si hay datos, una fila
               de chips de viento/humedad/lluvia). Se confirmó que TODOS
               los íconos (sol, luna, nube, lluvia, nieve, niebla,
               tormenta, día y de noche) usan la misma caja de 84×84 —
               ahí no hay ninguna diferencia real. La única variación de
               verdad es la fila de chips, que aparece o no según si esa
               hora trae esos datos de AEMET (agrega unos 27px cuando
               está). Este número (250px) contempla el caso más alto
               (con chips) más un margen chico — así las 23 escenas
               rinden siempre al mismo tamaño, con o sin chips. */
            min-height: 250px;
        }
        .clima-hero-contenido { flex: 1; text-align: center; position: relative; z-index: 1; }
        /* ── Lluvia/nieve cayendo de fondo (a pedido del cliente) —
           capa propia entre el degradé del cielo (z-index automático,
           siempre atrás) y el contenido (z-index:1, siempre adelante),
           para que la lluvia se vea "detrás" del número grande sin
           taparlo. pointer-events:none para que nunca robe un clic. ── */
        .clima-hero-precipitacion {
            position: absolute; inset: 0; overflow: hidden; z-index: 0;
            border-radius: inherit; pointer-events: none;
        }
        .gota-cayendo {
            position: absolute; top: -10%; width: 2px; height: 14px;
            background: linear-gradient(180deg, rgba(255,255,255,0) 0%, rgba(210,231,247,0.85) 100%);
            border-radius: 2px; animation-name: vm-caer-gota; animation-timing-function: linear; animation-iteration-count: infinite;
        }
        .copo-cayendo {
            position: absolute; top: -6%; width: 4px; height: 4px; border-radius: 50%;
            background: rgba(255,255,255,0.85);
            animation-name: vm-caer-copo; animation-timing-function: linear; animation-iteration-count: infinite;
        }
        /* ── Ángulo/deriva de la lluvia y la nieve según el viento real
           (a pedido del cliente): --deriva-x y --angulo-lluvia los pone
           cada gota/copo por su cuenta (ver htmlPrecipitacionViento() en
           el script) según el nivel de viento de ese momento — sin
           viento caen derechas, con viento fuerte caen bien inclinadas.
           Los valores por defecto (0) son el caso sin viento, para que
           nada se rompa si algún día falta el dato. ── */
        @keyframes vm-caer-gota {
            0% { transform: translate(0, 0) rotate(var(--angulo-lluvia, 0deg)); opacity: 0; }
            8% { opacity: 1; }
            100% { transform: translate(var(--deriva-x, 0px), 320px) rotate(var(--angulo-lluvia, 0deg)); opacity: 0.2; }
        }
        @keyframes vm-caer-copo {
            0% { transform: translate(0, 0); opacity: 0; }
            10% { opacity: 0.9; }
            100% { transform: translate(var(--deriva-x, 14px), 300px); opacity: 0; }
        }
        /* ── Nubes de fondo a la deriva (viento sin lluvia) — mismo
           truco que las olas del mar: el SVG mide el doble del
           contenedor y se anima con translateX(-50%), así el bucle no
           se nota. Vive en su propia capa, separada de la lluvia/nieve,
           porque pueden coexistir (nublado con lluvia Y viento) o no
           (nublado con viento, sin lluvia). ── */
        .nubes-viento-capa {
            position: absolute; top: 6%; left: 0; width: 200%; height: 40px;
            animation-name: vm-nube-deriva; animation-timing-function: linear; animation-iteration-count: infinite;
        }
        @keyframes vm-nube-deriva { from { transform: translateX(0); } to { transform: translateX(-50%); } }

        /* ── 5 animaciones más (a pedido del cliente, fusionando sus
           ideas con las mías): sol con vida propia, luna con brillo,
           niebla con deriva, rayo eléctrico. Todas livianas — solo
           transform/opacity/filter, nunca redibujan la forma en sí, así
           no cargan la placa de video aunque haya varios íconos en
           pantalla a la vez (franja de horas). ── */
        .sol-rayos {
            transform-box: fill-box; transform-origin: center;
            animation: vm-girar-sol 24s linear infinite;
        }
        .sol-nucleo {
            transform-box: fill-box; transform-origin: center;
            animation: vm-pulso-sol 2.6s ease-in-out infinite;
        }
        @keyframes vm-girar-sol { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
        @keyframes vm-pulso-sol { 0%, 100% { opacity: 1; } 50% { opacity: 0.82; } }

        /* Animación de brillo ELIMINADA del todo (a pedido del cliente,
           Sesión 78) — se había dejado solo en el ícono grande (una
           luna a la vez), pero el costo real no viene de "cuántas
           lunas" sino de qué tan cara es la combinación en sí: animar
           filter:brightness() sobre un elemento que YA tiene un filtro
           SVG de desenfoque (feGaussianBlur) obliga a recalcular ese
           desenfoque en cada cuadro, todo el tiempo que el modal está
           abierto — el mismo tipo de costo que causaba los parches
           grises de carga en el mapa, solo que acá en vez de repintar
           un degradado se recalcula un filtro SVG, más caro todavía.
           El halo con desenfoque en sí SIGUE estando (se calcula una
           sola vez, sin animación, costo mínimo) — lo único que se
           saca es el pulso de brillo encima. Visualmente casi no se
           nota la diferencia; en rendimiento, sí. */
        .niebla-linea {
            transform-box: fill-box; transform-origin: center;
            animation-name: vm-derivar-niebla; animation-timing-function: ease-in-out;
            animation-iteration-count: infinite; animation-direction: alternate;
        }
        @keyframes vm-derivar-niebla { from { transform: translateX(-4%); } to { transform: translateX(4%); } }

        .rayo-electrico {
            transform-box: fill-box; transform-origin: center;
            animation: vm-flash-rayo 2.8s steps(1) infinite;
        }
        @keyframes vm-flash-rayo {
            0%, 86%, 92%, 100% { opacity: 0.6; filter: none; }
            88%, 90% { opacity: 1; filter: drop-shadow(0 0 4px #FFF6C4) brightness(1.4); }
        }
        .estrella-titilante {
            position: absolute; border-radius: 50%; background: #fff;
            animation-name: vm-titilar-estrella; animation-timing-function: ease-in-out; animation-iteration-count: infinite;
        }
        @keyframes vm-titilar-estrella { 0%, 100% { opacity: 0.22; } 50% { opacity: 1; } }
        .clima-hero-fecha { font-family: 'Inter', sans-serif; font-weight: 600; font-size: var(--vm-fs-cuerpo-destacado); color: #0E6E8C; }
        .clima-hero-emoji { display: flex; align-items: center; justify-content: center; margin: 4px 0; filter: drop-shadow(0 2px 6px rgba(0,0,0,0.12)); }
        .clima-hero-emoji svg { display: block; }
        .clima-hero-temp {
            font-family: 'Inter', sans-serif; font-weight: 700; font-size: var(--vm-fs-hero-grande); line-height: 1;
            /* ── Sesión 76, segunda vuelta — el borde de 1px con sombras
               apiladas se veía bien en la franja horaria (números
               chicos) pero bajaba la calidad del número grande en
               celular. Se separó en 2 casos, a pedido del cliente:
               - Base (todos los tamaños): solo la sombra tenue de
                 profundidad, sin borde — es lo único que hace falta
                 ahora que el modal de escritorio/tablet creció (ver
                 .ficha-clima más arriba) y ya no hacía falta ahí.
               - Celular (<720px, ver el @media más abajo): se le suma
                 el borde, con el mismo desplazamiento fino (0.5px) que
                 ya funciona bien en la franja horaria — el de 1px
                 pensado para el tamaño de escritorio se veía pesado en
                 el tamaño más chico de celular (30px, no 42px). ── */
            text-shadow: 0 2px 4px rgba(0,0,0,0.15);
        }
        @media (max-width: 719px) {
            .clima-hero-temp {
                text-shadow:
                    0.5px 0 0 rgba(60,60,60,0.7), -0.5px 0 0 rgba(60,60,60,0.7),
                    0 0.5px 0 rgba(60,60,60,0.7), 0 -0.5px 0 rgba(60,60,60,0.7),
                    0 2px 4px rgba(0,0,0,0.15);
            }
        }
        .clima-hero-maxmin { font-family: 'Inter', sans-serif; font-size: var(--vm-fs-cuerpo); color: #6f8890; margin-top: var(--vm-esp-pegado); }
        .clima-hero-sensacion { font-family: 'Inter', sans-serif; font-size: var(--vm-fs-caption); color: #6f8890; margin-top: var(--vm-esp-pegado); }
        .es-de-noche .clima-hero-sensacion { color: #93A9C4; }
        .clima-hero-datos {
            display: flex; justify-content: center; gap: 8px; margin-top: 8px; position: relative; z-index: 1;
        }
        .clima-hero-chip {
            display: flex; align-items: center; gap: 4px;
            background: rgba(255,255,255,0.55); border-radius: 20px; padding: 3px 9px;
            font-family: 'Inter', sans-serif; font-size: var(--vm-fs-caption); font-weight: 700; color: #35545C;
        }
        .es-de-noche .clima-hero-chip { background: rgba(255,255,255,0.14); color: #D7E4F2; }
        /* v48 — iconos de Jaime para probabilidad de lluvia, precipitación
           y humedad (26 de septiembre de 2026). Van como MÁSCARA, no como
           imagen: la forma sale del dibujo y el color lo pone currentColor,
           así siguen el mismo color del texto de día (#35545C) y de noche
           (#D7E4F2) sin tener que guardar dos versiones de cada icono. */
        .clima-chip-icono {
            width: 16px; height: 16px; flex-shrink: 0; display: inline-block;
            background-color: currentColor;
            -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
            -webkit-mask-size: contain; mask-size: contain;
            -webkit-mask-position: center; mask-position: center;
        }
        .clima-chip-icono.icono-prob-lluvia { -webkit-mask-image: url('data:image/webp;base64,UklGRvwCAABXRUJQVlA4TO8CAAAvL8ALEAYHkSQJTfu3DQnwCmYjaEuybdq22uh9Htu2jTfbtm3btn3OB9i2bZvX5rbnmGO0CehthgrWFgOHUvhNj9iiGplvj6NZoIkAIt8RyAhv8N474yDyFQFFXMJ49jrMdSWBvMZQwQqU9BlZDu/Hou+VA/IOoXArq6co7N/lrRyEeYNCimlvvDFayWU5Cvt/2Qj0BQoq2SMOK39I5/gH46QoA+QRBd3FYxzb8EUWzWcETyhoyyLLY7U9E811EIz8xUx+f6AIX+fym2xQlPlDkIXI4pcin10yWwWI/GAE1Bte9MlVsh3yA70hkIJSkP+GE7GYr+oDhaIgmwkeI9/nEyZGbSBAQWPv8FAWfZWLUNBDBrLcGbFoDaS2NBThqfzEKErdRxbP5TJRZ1wHi3inHeVFJy1f/Yw7y2/S8GguX6WeJeJd9CPyR6Hp9qPoIH4SlNKc5w4SZjPkPLLHsOgHeQmjvFhkz+CF+VstIIGmYlAUx3/MxWLeqwUEoqCa23ikGOvkBAoVhXR3zC/SZGzSpV6lLSmSf0pKYltifHXaRMUhArcESnZllFdOeWWValO6UlKJrbYppuhaSioggKIM/KbB50TAwP8jlYABHTRnKGk00Fz7kbbJBLIYpAgzeJOCTNajnygzoJCFKFk+YIqCGh6gNSB4g4IK7iMvXlFgKCjmEiZUCQgQMEQCiux7hYDm/kZhqQ2C1PcjshxqAgIC1mIychPIMwIaSUEWubQARk3/I4si1B9kyiYU8Rz6SoB5NOoVspgI9YK5jsJi0ThIA2R55PK77M8IKCOdHyaawSC///hh7EyCzOZYjM+fcj1bCkrgLqgTyOk3bpgwIyBjkcW4+VnmZ1FwCGWwQvSPAgRsRxmsEKUoDYwSElHIykC7gcJjwijiEZb/tQEBkdtVLIn6AQn0lIjlhryEedaS2RA7zVUaCDBNHzstUiECEVDRHJv0YGC9ua4Eal0JVASu690JNgJXRrARuCXbGPgXAA=='); mask-image: url('data:image/webp;base64,UklGRvwCAABXRUJQVlA4TO8CAAAvL8ALEAYHkSQJTfu3DQnwCmYjaEuybdq22uh9Htu2jTfbtm3btn3OB9i2bZvX5rbnmGO0CehthgrWFgOHUvhNj9iiGplvj6NZoIkAIt8RyAhv8N474yDyFQFFXMJ49jrMdSWBvMZQwQqU9BlZDu/Hou+VA/IOoXArq6co7N/lrRyEeYNCimlvvDFayWU5Cvt/2Qj0BQoq2SMOK39I5/gH46QoA+QRBd3FYxzb8EUWzWcETyhoyyLLY7U9E811EIz8xUx+f6AIX+fym2xQlPlDkIXI4pcin10yWwWI/GAE1Bte9MlVsh3yA70hkIJSkP+GE7GYr+oDhaIgmwkeI9/nEyZGbSBAQWPv8FAWfZWLUNBDBrLcGbFoDaS2NBThqfzEKErdRxbP5TJRZ1wHi3inHeVFJy1f/Yw7y2/S8GguX6WeJeJd9CPyR6Hp9qPoIH4SlNKc5w4SZjPkPLLHsOgHeQmjvFhkz+CF+VstIIGmYlAUx3/MxWLeqwUEoqCa23ikGOvkBAoVhXR3zC/SZGzSpV6lLSmSf0pKYltifHXaRMUhArcESnZllFdOeWWValO6UlKJrbYppuhaSioggKIM/KbB50TAwP8jlYABHTRnKGk00Fz7kbbJBLIYpAgzeJOCTNajnygzoJCFKFk+YIqCGh6gNSB4g4IK7iMvXlFgKCjmEiZUCQgQMEQCiux7hYDm/kZhqQ2C1PcjshxqAgIC1mIychPIMwIaSUEWubQARk3/I4si1B9kyiYU8Rz6SoB5NOoVspgI9YK5jsJi0ThIA2R55PK77M8IKCOdHyaawSC///hh7EyCzOZYjM+fcj1bCkrgLqgTyOk3bpgwIyBjkcW4+VnmZ1FwCGWwQvSPAgRsRxmsEKUoDYwSElHIykC7gcJjwijiEZb/tQEBkdtVLIn6AQn0lIjlhryEedaS2RA7zVUaCDBNHzstUiECEVDRHJv0YGC9ua4Eal0JVASu690JNgJXRrARuCXbGPgXAA=='); }
        .clima-chip-icono.icono-precipitacion { -webkit-mask-image: url('data:image/webp;base64,UklGRogDAABXRUJQVlA4THsDAAAvL8ALEGDQto0g84f913V3jyCCFrVthiTp/eOPUXPstW3btq1L22ZVrW3b3r2ybY5tuyoy4mPgtm0j9upeenu8AnsT4V5PQ8R/jcPxRM4B/40A61qiqFS2fV8EQTiYIPiKVCryjzaBvWFjnsPZJBFJ5NrHgcPWPrINOBh0ma7IRIpskZEQnl6WlUwik42AIMIZpCI6gytuTsjEvHXG9fA1WU4+EzmHD5WgUnzP2fW7gggNkkgiDXBmFMmHQmbpBlu7oU61GRjAYW+SFFIksheYz+QIWfb3WjNmsFGkZReACTqNJZmIZDJaN3M+aR6apL5M59NYRhoAh1tIony5atDpZ1JJkoqMMYixlX1kTUUNArOapbISFNlSq8Nw7xAR+dyaEHARmqQGDveccQzuB2BHZzrXboCAWzDAPEUZFMUcA+J/3LvAG0eQimZTkWMhclHk+7XACnVFKiTFvfNzCH3VlrZBUgc8ThV5avzfrG3kcBGRqzG5egl3RDDAxlZpZzOaFFP0hn2S/ZZph0YIcDtZmLcABu2mEBmrbWB6G6WoSjdKHxbnMUSTyCcQkraPcdpg38BhX5JV2VXkIHCBMwHW0JSVO2qAAGcTyZIkKUTOg8Citpqm8gW74FYAHPb0bYTvHQDOYC17iAg+uwYO2ME5ai6wQ/w/dreYvKdX8Omkj8nBPlGMZVxAhBdJi2wKa7QAuL9XYFzMn4/Dq4ok27LgovAVLdT738nZQRLZAtbIIl56TBuvvUSf6HwCDju72oZI2lYMsJUBEBewvUzmWw867oLp6SUy0SZs0gquJ0vIidCZ7NunOWxPWuReBJuPabhRsUJxTDBOW8NEkXHYU7ZC9kiyRUFWkGOhK+xhN9KkO45bMejpXNcYINzwydCnNQAOtxH5iMPuHw8BXrmha9mFwW8qySKDYY+Ph99pHH4kMteQ4JFxWOdY6Fz5ici8zWXY74aH36lvPNT5uBSN2lrcqoetxe88CHjl8TD17UQeALxMklLCDUG3qUTG1Wm/yYoFhsJeZIXsMcAbshbZEtZSybLPAbcqlitOhF5GyYqJ2uEAxQrZnYAAbxL5Wy9YU0XkMsBNRJJVmW6TibwEOJGI7Ab4jMgvImxGRM48rOkr/9gFHM4y0cu6GdPfOyY6AyLsZ6wvrM4EPT1kmlsFzmzhd//bCRyuNMUL2hgA'); mask-image: url('data:image/webp;base64,UklGRogDAABXRUJQVlA4THsDAAAvL8ALEGDQto0g84f913V3jyCCFrVthiTp/eOPUXPstW3btq1L22ZVrW3b3r2ybY5tuyoy4mPgtm0j9upeenu8AnsT4V5PQ8R/jcPxRM4B/40A61qiqFS2fV8EQTiYIPiKVCryjzaBvWFjnsPZJBFJ5NrHgcPWPrINOBh0ma7IRIpskZEQnl6WlUwik42AIMIZpCI6gytuTsjEvHXG9fA1WU4+EzmHD5WgUnzP2fW7gggNkkgiDXBmFMmHQmbpBlu7oU61GRjAYW+SFFIksheYz+QIWfb3WjNmsFGkZReACTqNJZmIZDJaN3M+aR6apL5M59NYRhoAh1tIony5atDpZ1JJkoqMMYixlX1kTUUNArOapbISFNlSq8Nw7xAR+dyaEHARmqQGDveccQzuB2BHZzrXboCAWzDAPEUZFMUcA+J/3LvAG0eQimZTkWMhclHk+7XACnVFKiTFvfNzCH3VlrZBUgc8ThV5avzfrG3kcBGRqzG5egl3RDDAxlZpZzOaFFP0hn2S/ZZph0YIcDtZmLcABu2mEBmrbWB6G6WoSjdKHxbnMUSTyCcQkraPcdpg38BhX5JV2VXkIHCBMwHW0JSVO2qAAGcTyZIkKUTOg8Citpqm8gW74FYAHPb0bYTvHQDOYC17iAg+uwYO2ME5ai6wQ/w/dreYvKdX8Omkj8nBPlGMZVxAhBdJi2wKa7QAuL9XYFzMn4/Dq4ok27LgovAVLdT738nZQRLZAtbIIl56TBuvvUSf6HwCDju72oZI2lYMsJUBEBewvUzmWw867oLp6SUy0SZs0gquJ0vIidCZ7NunOWxPWuReBJuPabhRsUJxTDBOW8NEkXHYU7ZC9kiyRUFWkGOhK+xhN9KkO45bMejpXNcYINzwydCnNQAOtxH5iMPuHw8BXrmha9mFwW8qySKDYY+Ph99pHH4kMteQ4JFxWOdY6Fz5ici8zWXY74aH36lvPNT5uBSN2lrcqoetxe88CHjl8TD17UQeALxMklLCDUG3qUTG1Wm/yYoFhsJeZIXsMcAbshbZEtZSybLPAbcqlitOhF5GyYqJ2uEAxQrZnYAAbxL5Wy9YU0XkMsBNRJJVmW6TibwEOJGI7Ab4jMgvImxGRM48rOkr/9gFHM4y0cu6GdPfOyY6AyLsZ6wvrM4EPT1kmlsFzmzhd//bCRyuNMUL2hgA'); }
        .clima-chip-icono.icono-humedad { -webkit-mask-image: url('data:image/webp;base64,UklGRgIFAABXRUJQVlA4TPUEAAAvL8ALEAYIbSRJksyfds/07j0AIiMkwLZlSNLL/DlY27Zt27Zt27Zt27Zt297B2uzurHwT0H9KU4orS5HfRMB2+/tNNCWR3/5IQdG/h4CZmOVAgAZNxfkNNCWDP7wJyAuEYrxUjiJfG02iB8VkH7Kh85QgUBzNhu9QhKHgmSagCbIYi7oDsYPzTlxqn9EMVOJIr4BCsksI0ZL6xBvG8fyQTELkZRgwn1AEEkN185z3kQ/jfHFdB3AYWaxYHDjAsT6KQ+2OhqQy3mu8VFfJWHxkUTk5kT8TgFwREGqUr3hyvvkyX3lR9nACL3OcEoasn5JT9AWTvG4gV8It10wOCcRNfFkVECIMuaflo2CnkC+bgbwxaeQ3CqDHOosD76KJuPFZqJPIhqoAeWLSEnkJGCkqRBia2hYDLnHsE4/ziHIDWR7nlRg0dRBQC/kTqTTEUL0y6YP8T8tQsQS4IYvmAlPRlLS+seiZdCCI6nIT3QPk43Hj8SeCGI8CWCynJJDDGWRFSgNM9zTI4gk+ei2D3Hw8DouXR6LSFAPaID+nNDCRjX6iSRQN4pjiBcfz3EjBMnqHvE4BNBsIJZrXPDQdBFGroo910lASBkL19AN5PXLsFIa0xHknFh2kiAF6yQEVlTRKKyUbUAytpYUm2u87xuuVRcepOcFlDAiixLQdi7WY0ISSxhdMJSCUKMKwYjm83BSTzt/4ZKQEgTWYr/5w6QGCwX7MPCCBEmiPC2x4zaLZumKuUTRI7A8rUkqVBVjnKNAYed6KRVMM6MPpbDt+wMsdqzHzQAiogNkAgr1BbwSJ5wMP1QQCTGaiwmZwAnfLF9dKWxAMCrCcsyCxnzx3wFrMJmBqmYXyq4zsJ775jKkNDCVUGPJZ4iiLBisUzyfJBTEUMOnP6SjYZ96ulke+liJAE9CQw8p5cT3lUFuoSKAi2gB6It8Vy1npK6Yo0ECD4ja47YKhYCrmOIivcBIBNbF88U082nbk5y6W6j5gagOBaDgVQtZfSXTxDvPNOIoBk9FkEM0OTIDHbTwO2bkt2DlMe2AqEkxUdzjUTnV81AOIhN7x1ABiqO/4zPGnKljOoYUPtQzDXAIveH6rpgk/6x5NQCWefxpBUhvtms/++ei8MAE0A4g2mLs0ddAgm38sv8wyYU5D7qLP4gGTxgJopdxQiSeXwb6gxUAoKfxAnjwUgSLgHGYSKMShy5CXnJdCgCKgiKuY21YZb7qD/qDvOgNNQfZxaAEQiICOOE9FZUTxivPfCHNYtB4IRICo4QA/Vh4ZLjnQUKmFnF9SUHQgK4cpCoJBczx9lpGioRgiPQiSRw3FpaYgAtUYdzm0EQQRMMQFp3QCAjSo7ax33tosK1AVky7opYVwMjScBNRA/jQDQZ00FA2JKyZEQxGQwQ8WrWOEEJqCVwK2Ib8/CoOg6AichIIIDUVATHeRFS42NbilE99b5PmgMDA0vFOrxiSW08iXykDgngYF/OLmt2YQQ79UFFOyuYP+of7A9CkB5f1EFq2SEqIZQldkNCRUXz+QP6OA6fOliDDkyxcTZYZLyXR1H3lxegCBbzJJZjdWfE4ZopZMUksjsyr62e8bxuWxckDguwTSwmN8FPA/AXz0zUQxgMC3qSWqNk4L4K2nxksFEfhBAiWLDta45KV33gt3xz4jlRQCEQp+dAxVJaq44okFJ0PDVwEA'); mask-image: url('data:image/webp;base64,UklGRgIFAABXRUJQVlA4TPUEAAAvL8ALEAYIbSRJksyfds/07j0AIiMkwLZlSNLL/DlY27Zt27Zt27Zt27Zt297B2uzurHwT0H9KU4orS5HfRMB2+/tNNCWR3/5IQdG/h4CZmOVAgAZNxfkNNCWDP7wJyAuEYrxUjiJfG02iB8VkH7Kh85QgUBzNhu9QhKHgmSagCbIYi7oDsYPzTlxqn9EMVOJIr4BCsksI0ZL6xBvG8fyQTELkZRgwn1AEEkN185z3kQ/jfHFdB3AYWaxYHDjAsT6KQ+2OhqQy3mu8VFfJWHxkUTk5kT8TgFwREGqUr3hyvvkyX3lR9nACL3OcEoasn5JT9AWTvG4gV8It10wOCcRNfFkVECIMuaflo2CnkC+bgbwxaeQ3CqDHOosD76KJuPFZqJPIhqoAeWLSEnkJGCkqRBia2hYDLnHsE4/ziHIDWR7nlRg0dRBQC/kTqTTEUL0y6YP8T8tQsQS4IYvmAlPRlLS+seiZdCCI6nIT3QPk43Hj8SeCGI8CWCynJJDDGWRFSgNM9zTI4gk+ei2D3Hw8DouXR6LSFAPaID+nNDCRjX6iSRQN4pjiBcfz3EjBMnqHvE4BNBsIJZrXPDQdBFGroo910lASBkL19AN5PXLsFIa0xHknFh2kiAF6yQEVlTRKKyUbUAytpYUm2u87xuuVRcepOcFlDAiixLQdi7WY0ISSxhdMJSCUKMKwYjm83BSTzt/4ZKQEgTWYr/5w6QGCwX7MPCCBEmiPC2x4zaLZumKuUTRI7A8rUkqVBVjnKNAYed6KRVMM6MPpbDt+wMsdqzHzQAiogNkAgr1BbwSJ5wMP1QQCTGaiwmZwAnfLF9dKWxAMCrCcsyCxnzx3wFrMJmBqmYXyq4zsJ775jKkNDCVUGPJZ4iiLBisUzyfJBTEUMOnP6SjYZ96ulke+liJAE9CQw8p5cT3lUFuoSKAi2gB6It8Vy1npK6Yo0ECD4ja47YKhYCrmOIivcBIBNbF88U082nbk5y6W6j5gagOBaDgVQtZfSXTxDvPNOIoBk9FkEM0OTIDHbTwO2bkt2DlMe2AqEkxUdzjUTnV81AOIhN7x1ABiqO/4zPGnKljOoYUPtQzDXAIveH6rpgk/6x5NQCWefxpBUhvtms/++ei8MAE0A4g2mLs0ddAgm38sv8wyYU5D7qLP4gGTxgJopdxQiSeXwb6gxUAoKfxAnjwUgSLgHGYSKMShy5CXnJdCgCKgiKuY21YZb7qD/qDvOgNNQfZxaAEQiICOOE9FZUTxivPfCHNYtB4IRICo4QA/Vh4ZLjnQUKmFnF9SUHQgK4cpCoJBczx9lpGioRgiPQiSRw3FpaYgAtUYdzm0EQQRMMQFp3QCAjSo7ax33tosK1AVky7opYVwMjScBNRA/jQDQZ00FA2JKyZEQxGQwQ8WrWOEEJqCVwK2Ib8/CoOg6AichIIIDUVATHeRFS42NbilE99b5PmgMDA0vFOrxiSW08iXykDgngYF/OLmt2YQQ79UFFOyuYP+of7A9CkB5f1EFq2SEqIZQldkNCRUXz+QP6OA6fOliDDkyxcTZYZLyXR1H3lxegCBbzJJZjdWfE4ZopZMUksjsyr62e8bxuWxckDguwTSwmN8FPA/AXz0zUQxgMC3qSWqNk4L4K2nxksFEfhBAiWLDta45KV33gt3xz4jlRQCEQp+dAxVJaq44okFJ0PDVwEA'); }
        .clima-hero-chip[title] { cursor: help; }
        .clima-hero-chip { position: relative; }
        .clima-hero-chip.mostrar-tip::after {
            content: attr(data-tip); position: absolute; bottom: calc(100% + 6px); left: 50%;
            transform: translateX(-50%); width: max-content; max-width: 170px; white-space: normal; text-align: center; z-index: 5; pointer-events: none;
            background: rgba(20,32,40,0.92); color: #fff; font-weight: 600;
            font-size: var(--vm-fs-caption); padding: 4px 8px; border-radius: 8px;
        }
        /* ── Escenas de cielo (Fase 3 del rediseño gráfico) — cada
           categoría de AEMET + día/noche real de Cullera tiene su
           propio fondo, en vez de un único gradiente claro con un
           emoji encima. El campo de estrellas es un truco 100% CSS
           (varios radial-gradient chiquitos apilados) — no agrega
           ningún elemento nuevo al DOM. ── */
        .clima-hero[data-escena="dia-despejado"] { background: linear-gradient(180deg, #6EC6FF 0%, #BFE8FF 100%); }
        /* ── Solo para el mar (idea del hijo del cliente): de tarde, el
           mismo cielo despejado pero un poco menos brillante que el de
           la mañana, para diferenciarlos de un vistazo. Selector
           .mar-hero, no .clima-hero — nunca lo produce el clima, así
           que no le pisa nada. ── */
        .mar-hero[data-escena="dia-despejado-tarde"] { background: linear-gradient(180deg, #54A3D2 0%, #7DBCDF 100%); }
        .clima-hero[data-escena="dia-nuboso"] {
            background:
                radial-gradient(60% 55% at 30% 20%, rgba(255,255,255,0.55), transparent),
                linear-gradient(180deg, #93A9BB 0%, #CBD8E0 100%);
        }
        .clima-hero[data-escena="dia-cubierto"] {
            background:
                radial-gradient(55% 50% at 30% 15%, rgba(255,255,255,0.28), transparent),
                linear-gradient(180deg, #7D8A94 0%, #AEB8C0 100%);
        }
        .clima-hero[data-escena="dia-lluvia"] { background: linear-gradient(180deg, #5B6B78 0%, #8B99A3 100%); }
        .clima-hero[data-escena="dia-tormenta"] { background: linear-gradient(180deg, #3C4A57 0%, #6B7885 100%); }
        .clima-hero[data-escena="dia-nieve"] { background: linear-gradient(180deg, #AEC4D6 0%, #E4EEF5 100%); }
        .clima-hero[data-escena="dia-niebla"] { background: linear-gradient(180deg, #B8BEC2 0%, #DCE0E2 100%); }
        /* ── Franja de amanecer/atardecer (30 min alrededor del orto/
           ocaso real de AEMET) — cielo cálido en vez del salto directo
           de día a noche. "-fuerte" (despejado) usa tonos más intensos;
           "-suave" (poco nuboso) los mismos tonos pero más pastel, como
           si las nubes suavizaran la luz. ── */
        /* ── Amanecer/atardecer, con movimiento (a pedido del cliente,
           corregido en la segunda vuelta) — la primera versión animaba
           background-position, una de las propiedades más caras de
           animar en CSS: obliga al navegador a repintar TODO el
           degradado en cada cuadro, no es algo que la GPU resuelva
           barato como sí hace con opacity/transform. Con esa animación
           corriendo mientras el modal estaba abierto, empezaron a
           aparecer parches grises de carga en el mapa de fondo — el
           navegador no daba abasto para las 2 cosas a la vez. Se
           reemplazó por una técnica mucho más liviana: 2 capas de
           degradado FIJAS (una es el propio "background" del elemento,
           la otra un ::after encima) con los mismos colores pero la
           franja de transición un poco corrida hacia arriba o abajo —
           y en vez de mover nada, se desvanece una capa sobre la otra
           con opacity, que la GPU compone sin repintar. Mismo efecto
           percibido (los tonos parecen fluir), costo real mucho menor.
           Amanecer: la capa de encima tiene el amarillo más arriba (la
           luz "sube"). Atardecer: al revés, el naranja gana terreno
           hacia abajo (la luz "cae"). Naranja siempre arriba, amarillo
           siempre abajo, en la base y en la capa animada — eso no
           cambia entre amanecer y atardecer, solo el sentido. ── */
        .clima-hero[data-escena="atardecer-fuerte"],
        .clima-hero[data-escena="atardecer-suave"],
        .clima-hero[data-escena="amanecer-fuerte"],
        .clima-hero[data-escena="amanecer-suave"] {
            position: relative;
        }
        .clima-hero[data-escena="atardecer-fuerte"]::after,
        .clima-hero[data-escena="atardecer-suave"]::after,
        .clima-hero[data-escena="amanecer-fuerte"]::after,
        .clima-hero[data-escena="amanecer-suave"]::after {
            content: ''; position: absolute; inset: 0; z-index: -1; pointer-events: none;
            animation: vm-cruzar-cielo 8s ease-in-out infinite;
        }
        .clima-hero[data-escena="atardecer-fuerte"] { background: linear-gradient(180deg, #FF8C42 0%, #FFB05A 40%, #FFD966 100%); }
        .clima-hero[data-escena="atardecer-fuerte"]::after { background: linear-gradient(180deg, #FF8C42 0%, #FFB05A 58%, #FFD966 100%); }
        .clima-hero[data-escena="atardecer-suave"] { background: linear-gradient(180deg, #FFAE73 0%, #FFCC94 40%, #FFE9B8 100%); }
        .clima-hero[data-escena="atardecer-suave"]::after { background: linear-gradient(180deg, #FFAE73 0%, #FFCC94 58%, #FFE9B8 100%); }
        .clima-hero[data-escena="amanecer-fuerte"] { background: linear-gradient(180deg, #FF8C42 0%, #FFB05A 40%, #FFD966 100%); }
        .clima-hero[data-escena="amanecer-fuerte"]::after { background: linear-gradient(180deg, #FF8C42 0%, #FFB05A 22%, #FFD966 100%); }
        .clima-hero[data-escena="amanecer-suave"] { background: linear-gradient(180deg, #FFAE73 0%, #FFCC94 40%, #FFE9B8 100%); }
        .clima-hero[data-escena="amanecer-suave"]::after { background: linear-gradient(180deg, #FFAE73 0%, #FFCC94 22%, #FFE9B8 100%); }
        @keyframes vm-cruzar-cielo { 0%, 100% { opacity: 0; } 50% { opacity: 0.65; } }
        .clima-hero[data-escena$="-fuerte"] .clima-hero-fecha,
        .clima-hero[data-escena$="-suave"] .clima-hero-fecha { color: #7A3B1D; }
        .clima-hero[data-escena$="-fuerte"] .clima-hero-maxmin,
        .clima-hero[data-escena$="-suave"] .clima-hero-maxmin { color: #8A4B2E; }
        .clima-hero[data-escena="dia-tormenta"] .clima-hero-fecha,
        .clima-hero[data-escena="dia-lluvia"] .clima-hero-fecha { color: #E8EEF2; }
        .clima-hero[data-escena="dia-tormenta"] .clima-hero-maxmin,
        .clima-hero[data-escena="dia-lluvia"] .clima-hero-maxmin { color: #CBD6DC; }
        .clima-hero[data-escena^="noche"] {
            background: linear-gradient(180deg, #1B2A4A 0%, #2C3E63 100%);
        }
        .clima-hero[data-escena="noche-estrellada"],
        .clima-hero[data-escena="noche-poconuboso"] {
            background: linear-gradient(180deg, #0E1B3A 0%, #24365E 100%);
        }
        /* ── "Nuboso" de noche (sesión del 15 de septiembre de 2026) —
           antes compartía el mismo azul despejado que "noche-estrellada".
           Ahora un azul turbio, mezclado con gris, para que se note que
           el cielo está de otro color por las nubes — distinto también
           de "noche-opaca" (Muy Nuboso/Cubierto/lluvia/etc.), que sigue
           siendo el gris más neutro de siempre. ── */
        .clima-hero[data-escena="noche-nublada"] {
            background: linear-gradient(180deg, #222E42 0%, #3A4756 100%);
        }
        /* "Muy nuboso" — un paso más gris que "Nuboso" (menos azul,
           más denso), pero todavía distinto del gris neutro de
           "Cubierto" — misma lógica de escalón que los íconos. */
        .clima-hero[data-escena="noche-muynuboso"] {
            background: linear-gradient(180deg, #282E38 0%, #40474F 100%);
        }
        .clima-hero[data-escena="noche-opaca"] { background: linear-gradient(180deg, #2E3540 0%, #454E58 100%); }
        /* ── Texto claro para cualquier escena nocturna (Fase 3 del
           rediseño gráfico) — el fondo en sí lo pone data-escena de
           arriba; esto solo ajusta fecha y máx/mín para que se sigan
           leyendo bien sobre un fondo oscuro, sea cual sea la escena
           nocturna puntual (estrellada, nublada u opaca). ── */
        .es-de-noche .clima-hero-fecha { color: #A9C7E8; }
        .es-de-noche .clima-hero-maxmin { color: #93A9C4; }
        .clima-nav {
            width: 34px; height: 34px; border-radius: 50%; border: none; background: white;
            color: #0E6E8C; font-size: var(--vm-fs-titulo); cursor: pointer; flex-shrink: 0; box-shadow: 0 2px 8px rgba(14,110,140,0.15);
        }
        .clima-nav:hover, .clima-nav:active { background: #E8F4F7; }
        .clima-nav:disabled { opacity: 0.35; cursor: default; }
        .clima-aviso {
            border-radius: 10px; padding: 10px 12px; margin-bottom: 14px; font-family: 'Inter', sans-serif;
        }
        .clima-aviso-titulo { font-weight: 800; font-size: var(--vm-fs-cuerpo); text-transform: uppercase; letter-spacing: 0.04em; }
        .clima-aviso-texto { font-size: var(--vm-fs-cuerpo); margin-top: var(--vm-esp-pegado); line-height: var(--vm-lh-cuerpo); }
        .clima-aviso.amarillo { background: #fff3cd; color: #7a5c00; }
        .clima-aviso.naranja { background: #ffe0b2; color: #a34e00; }
        .clima-aviso.rojo { background: #ffcdd2; color: #a30000; }
        /* ── Amanecer/atardecer reales de AEMET (a pedido del cliente)
           — mismo dato que ya viene en la respuesta de siempre
           (dia.orto/dia.ocaso), no una llamada nueva. Se muestra
           siempre que AEMET lo traiga para ese día; si no, la fila
           entera queda oculta (display:none desde el HTML). ── */
        .clima-orto-ocaso {
            display: flex; justify-content: center; align-items: center; gap: 18px;
            margin-bottom: var(--vm-esp-accion); font-family: 'Inter', sans-serif; font-size: var(--vm-fs-cuerpo); color: #6f8890;
        }
        .clima-orto-ocaso-item { display: flex; align-items: center; gap: 5px; }
        .clima-orto-ocaso-item svg { flex-shrink: 0; }
        /* v47 — iconos de salida y puesta del sol diseñados por Jaime (webp). */
        .clima-orto-ocaso-item .clima-icono-orto-ocaso { flex-shrink: 0; width: auto; height: 26px; display: block; }
        .clima-horas { display: flex; gap: 14px; overflow-x: auto; padding-bottom: 6px; }
        .clima-hora { flex-shrink: 0; text-align: center; font-family: 'Inter', sans-serif; padding: 4px 2px; border-radius: 10px; }
        /* Hora exacta del amanecer/atardecer dentro de la franja — más
           transparente que el primer intento (el naranja sólido tapaba
           los propios colores del ícono del sol), y con una transición
           de color en vez de un naranja plano: azul→naranja para el
           amanecer (de noche a día), naranja→azul para el atardecer (de
           día a noche). Mismo padding que .clima-hora de arriba, para
           todas las celdas — si esta celda tuviera su propio padding
           aparte, quedaría "empujada" más abajo que sus vecinas, que es
           justo el desnivel que se veía antes. */
        .clima-hora-amanecer {
            background: linear-gradient(180deg, rgba(179, 213, 255, 0.35) 0%, rgba(255, 209, 140, 0.35) 100%);
        }
        .clima-hora-atardecer {
            background: linear-gradient(180deg, rgba(255, 209, 140, 0.35) 0%, rgba(179, 213, 255, 0.35) 100%);
        }
        .clima-hora-etiqueta { font-size: var(--vm-fs-caption); color: #8ba3a8; font-weight: 700; }
        .clima-hora-emoji { display: flex; align-items: center; justify-content: center; margin: 4px 0; }
        .clima-hora-emoji svg { display: block; }
        /* Borde por fuera del número (a pedido del cliente) — mismo
           criterio que el número grande del clima: sombras apiladas en
           vez de -webkit-text-stroke, para que quede solo por fuera de
           la letra, no mordiendo el relleno. Desplazamiento más chico
           que el del número grande (0.5px, no 1px) para que no se vea
           pesado en un texto tan pequeño. Color más oscuro, al 70% de
           opacidad (antes gris medio al 55%). */
        .clima-hora-temp {
            font-size: var(--vm-fs-cuerpo); font-weight: 700;
            text-shadow:
                0.5px 0 0 rgba(60,60,60,0.7), -0.5px 0 0 rgba(60,60,60,0.7),
                0 0.5px 0 rgba(60,60,60,0.7), 0 -0.5px 0 rgba(60,60,60,0.7);
        }
        .clima-hora-lluvia { font-size: var(--vm-fs-micro); color: #5B9BD5; margin-top: var(--vm-esp-pegado); font-weight: 600; }
        .es-de-noche .clima-hora-lluvia { color: #8FB8DE; }
        .clima-fuente { text-align: center; font-family: 'Inter', sans-serif; font-size: var(--vm-fs-caption); color: #b0bec5; margin-top: var(--vm-esp-texto); }

        /* ── Comparador de Gasolineras — mismo marco que los otros paneles ── */
        .tarjeta-combustible { background: white; border-radius: 14px; padding: 14px 16px; margin-bottom: 12px; box-shadow: 0 2px 8px rgba(14,110,140,0.08); }
        .titulo-combustible { font-family: 'Inter', sans-serif; font-weight: 600; font-size: var(--vm-fs-cuerpo-destacado); color: var(--vm-texto); margin-bottom: 8px; }
        .fila-estacion { display: flex; justify-content: space-between; align-items: center; padding: 7px 0; border-top: 1px solid #f0f4f5; gap: 10px; }
        .fila-estacion:first-child { border-top: none; }
        .nombre-estacion { font-family: 'Inter', sans-serif; font-size: var(--vm-fs-cuerpo); color: #37474f; flex: 1; }
        /* Solo en celular (a pedido del cliente) — el nombre y la
           referencia entre paréntesis quedaban en la misma línea de
           texto, y con pantallas angostas el navegador cortaba justo en
           medio del paréntesis, partiéndolo de forma rara. En celular
           pasa a su propia línea, prolijo; en tablet/escritorio se
           queda igual que antes (inline, junto al nombre). */
        @media (max-width: 719px) {
            .referencia-direccion-estacion { display: block; margin-top: var(--vm-esp-pegado); }
        }
        .nombre-estacion .estrella-mas-barato { margin-right: 4px; }
        .referencia-estacion { font-size: var(--vm-fs-caption); color: #8ba3a8; font-weight: 400; }
        .precio-estacion { font-family: 'Inter', sans-serif; font-weight: 700; font-size: var(--vm-fs-cuerpo-destacado); color: #55575c; white-space: nowrap; }
        /* Jerarquía de color de precios (a pedido del cliente): el más
           barato es el verde MÁS oscuro (resalta más que el segundo),
           el segundo más barato usa el verde de marca normal
           (var(--viu-acento)), el resto se queda gris oscuro neutro. */
        .precio-estacion.precio-mas-barato { color: var(--viu-acento); }
        .precio-estacion.precio-segundo-barato { color: #173e19; }
        .acciones-estacion { display: flex; gap: 4px; }
        .acciones-estacion .btn-accion-resultado { padding: 5px 8px; font-size: var(--vm-fs-caption); }
        .nota-fuente-gasolina { text-align: center; font-family: 'Inter', sans-serif; font-size: var(--vm-fs-caption); color: #b0bec5; margin-top: var(--vm-esp-texto); }

        /* ── Formulario de Reserva — mismo marco que la ficha de negocio,
           con los mismos tokens de tipografía/color de "¿Qué quieres hacer
           hoy?" para que se sienta parte del mismo sistema. ── */
        .ficha-reserva h3 {
            font-family: 'Inter', sans-serif; font-weight: 700; font-size: var(--vm-fs-titulo);
            color: var(--vm-texto); text-align: center; margin: 0 0 var(--vm-esp-pegado);
        }
        /* Insignia circular con el ícono de la acción (a pedido del
           cliente) — antes el ícono iba metido adentro del propio
           título, chico (1em), sin ninguna identidad propia. Mismo
           color de marca que ya usa cada acción en el botón sticky del
           pie (vino para Reservar, azul para Cita Previa), para que se
           reconozca de un vistazo antes incluso de leer el título. */
        .cabecera-ficha-accion {
            width: 56px; height: 56px; border-radius: 50%; margin: 0 auto var(--vm-esp-texto);
            display: flex; align-items: center; justify-content: center;
            box-shadow: inset 0 1px 0 rgba(255,255,255,0.35), inset 0 -2px 3px rgba(0,0,0,0.15);
        }
        .cabecera-ficha-accion-vino { background: #7A2048; }
        .cabecera-ficha-accion-azul { background: var(--vm-azul); }
        .reserva-subtitulo { text-align: center; font-family: 'Inter', sans-serif; font-size: var(--vm-fs-cuerpo); color: #6f8890; margin-bottom: var(--vm-esp-accion); }
        .campo-reserva { margin-bottom: var(--vm-esp-seccion); }
        .campo-bloqueado-cuenta { background: #f4f5f7 !important; color: #777 !important; cursor: not-allowed; }
        .campo-reserva label {
            display: block; font-family: 'Inter', sans-serif; font-size: var(--vm-fs-cuerpo); font-weight: 700;
            color: var(--vm-texto); margin-bottom: 5px;
        }
        .campo-reserva input, .campo-reserva select, .campo-reserva textarea {
            width: 100%; padding: 11px 13px; border: 1.5px solid #dce8ea; border-radius: 12px;
            font-family: 'Inter', sans-serif; font-size: var(--vm-fs-cuerpo-destacado); color: var(--vm-texto); background: white;
        }
        .campo-reserva input:focus, .campo-reserva select:focus, .campo-reserva textarea:focus { outline: none; border-color: var(--vm-azul); }
        /* El ejemplo (placeholder) del campo de comentarios, más chico
           que el texto real que escriba el usuario — a pedido del
           cliente, se veía demasiado protagónico para ser solo un
           ejemplo orientativo. */
        #rComentarios::placeholder { font-size: var(--vm-fs-caption); }
        #cpComentarios::placeholder { font-size: var(--vm-fs-caption); }
        .fila-doble { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
        .fila-telefono { display: grid; grid-template-columns: 90px 1fr; gap: 8px; }
        .aviso-horario-reserva {
            font-family: 'Inter', sans-serif; font-size: var(--vm-fs-caption); color: #a34e00; background: #fff3e0;
            border-radius: 10px; padding: 8px 12px; margin: -6px 0 14px; display: none;
            text-align: justify;
        }
        .aviso-horario-reserva.visible { display: block; }
        /* ── Envío directo, sin WhatsApp (Sesión 50 para Reservas,
           Sesión 62 para Cita Previa) — el sistema guarda directo en
           Firebase por su cuenta (ver btnEnviarReserva/btnEnviarCitaWA
           más abajo), sin depender de ningún chat externo. Mismo azul de
           marca que el resto de los botones de acción de la ficha
           (var(--vm-azul), actualizado en la Fase 1 del rediseño gráfico
           — antes era var(--viu-acento), el verde), no el verde de
           WhatsApp. ── */
        .btn-reserva-directa {
            display: flex; align-items: center; justify-content: center; gap: 10px;
            width: 100%; background: var(--vm-azul); color: white; border: none;
            padding: 13px; border-radius: 12px; font-family: 'Inter', sans-serif;
            font-weight: 600; font-size: var(--vm-fs-subtitulo); cursor: pointer; transition: background 0.15s;
        }
        .btn-reserva-directa:hover, .btn-reserva-directa:active { background: var(--vm-azul-oscuro); }
        .btn-reserva-directa:disabled { background: #c8d6d8; cursor: default; }
        .nota-envio-reserva {
            text-align: center; font-family: 'Inter', sans-serif; font-size: var(--vm-fs-caption); color: #8ba3a8;
            margin-top: var(--vm-esp-accion); line-height: var(--vm-lh-cuerpo);
        }
        .nota-consentimiento-datos {
            text-align: center; font-family: 'Inter', sans-serif; font-size: var(--vm-fs-caption); color: #8ba3a8;
            /* Interlineado propio, más ajustado que --vm-lh-cuerpo
               (a pedido del cliente) — este texto es de los pocos que
               llega a partirse en 2-3 líneas, así que ahí sí se nota el
               interlineado general, que en el resto de los textos
               (todos de una sola línea) pasa desapercibido. */
            margin-top: 10px; line-height: 1.2; padding: 0 6px;
        }
        .confirmacion-reserva { text-align: center; padding: 10px 4px; }
        /* Círculo de fondo + animación de "aparición" (a pedido del
           cliente) — antes era solo el emoji ✅ suelto sobre blanco,
           sin ningún cierre satisfactorio para una acción que sí lo
           amerita (reservar/agendar algo es un momento de cierre, no
           un dato más). Verde siempre, sea cual sea la acción (Reservar
           o Cita Previa) — acá el color no identifica la acción, dice
           "esto salió bien", que es un concepto aparte. */
        .confirmacion-reserva .icono-grande {
            font-size: 40px; width: 90px; height: 90px; border-radius: 50%;
            background: #e8f5e9; display: flex; align-items: center; justify-content: center;
            margin: 0 auto var(--vm-esp-seccion);
            animation: vm-pop-confirmacion 0.45s cubic-bezier(0.34, 1.56, 0.64, 1);
        }
        @keyframes vm-pop-confirmacion {
            0% { transform: scale(0); opacity: 0; }
            100% { transform: scale(1); opacity: 1; }
        }
        .confirmacion-reserva p { font-family: 'Inter', sans-serif; font-size: var(--vm-fs-cuerpo); color: var(--vm-texto); line-height: var(--vm-lh-cuerpo); }

        /* ── Formulario de Cita Previa — mismo marco que el de Reserva,
           con 4 pasos (servicio → día → horario → contacto) porque acá sí
           hace falta calcular disponibilidad real, a diferencia de una mesa. ── */
        .cp-pasos-indicador { display: flex; justify-content: center; gap: 6px; margin-bottom: var(--vm-esp-accion); }
        .cp-paso-punto { width: 8px; height: 8px; border-radius: 50%; background: #dce8ea; }
        .cp-paso-punto.activo { background: var(--vm-azul); }
        .cp-paso-punto.completado { background: var(--vm-azul); opacity: 0.5; }

        .cp-lista-servicios { display: flex; flex-direction: column; gap: 8px; margin-bottom: 6px; }
        .cp-chip-servicio {
            display: flex; justify-content: space-between; align-items: center; width: 100%;
            padding: 12px 14px; border: 1.5px solid #dce8ea; border-radius: 12px; background: white;
            font-family: 'Inter', sans-serif; font-size: var(--vm-fs-cuerpo-destacado); color: var(--vm-texto); cursor: pointer; text-align: left;
        }
        .cp-chip-servicio:hover, .cp-chip-servicio:active { border-color: var(--vm-azul); }
        .cp-chip-servicio .duracion { font-size: var(--vm-fs-cuerpo); color: #8ba3a8; white-space: nowrap; margin-left: 8px; }

        .cp-resumen-eleccion {
            background: #f7fbfc; border-radius: 10px; padding: 10px 12px; margin-bottom: var(--vm-esp-accion);
            font-family: 'Inter', sans-serif; font-size: var(--vm-fs-cuerpo); color: var(--vm-texto);
        }

        .cp-calendario-nav { display: flex; justify-content: space-between; align-items: center; margin-bottom: var(--vm-esp-seccion); }
        .cp-calendario-nav button {
            background: #f2f2f2; border: none; border-radius: 8px; padding: 7px 12px; cursor: pointer; font-size: var(--vm-fs-cuerpo-destacado);
        }
        .cp-calendario-nav button:disabled { opacity: 0.3; cursor: default; }
        .cp-calendario-nav span { font-family: 'Inter', sans-serif; font-weight: 600; color: var(--vm-texto); font-size: var(--vm-fs-cuerpo-destacado); text-transform: capitalize; }
        .cp-grilla-calendario { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; margin-bottom: 14px; }
        .cp-grilla-calendario .cp-nombre-dia { text-align: center; font-size: var(--vm-fs-caption); color: #8ba3a8; font-weight: 700; padding: 4px 0; }
        .cp-dia {
            aspect-ratio: 1; display: flex; align-items: center; justify-content: center;
            border-radius: 8px; font-size: var(--vm-fs-cuerpo); cursor: pointer; background: #f7fbfc; color: var(--vm-texto); border: none;
        }
        .cp-dia:hover:not(:disabled), .cp-dia:active:not(:disabled) { background: #dce8ea; }
        .cp-dia.seleccionado { background: var(--vm-azul); color: white; font-weight: 700; }
        .cp-dia:disabled { color: #ccc; background: none; cursor: default; }
        .cp-dia.cp-vacio { visibility: hidden; }
        /* Marca de "hoy" (a pedido del cliente) — un aro, no relleno, para
           no competir con el fondo sólido de "seleccionado"; si hoy
           también está seleccionado, el aro pasa a blanco para que se
           note contra el azul de fondo en vez de perderse. */
        .cp-dia.hoy { box-shadow: inset 0 0 0 2px var(--vm-azul); font-weight: 700; }
        .cp-dia.hoy.seleccionado { box-shadow: inset 0 0 0 2px white; }
        /* ── "Mi Agenda Viu Map" (Sesión 70) — colores por día, sobre el
           mismo .cp-dia de siempre. Un tipo presente = su propio color
           con opacidad; los 2 (o 3, con la Falla) juntos = gris neutro
           (para no "tapar" un color con el otro) con un punto de cada
           uno adentro.
           El "hoy"/"seleccionado" de ESTE calendario en particular usa
           verde de marca en vez del azul de siempre — acá el azul ya
           significa "Citas Previas", usarlo también para "qué día estoy
           mirando" confundía los dos conceptos (a pedido del cliente).
           Va con selector propio (#agendaMiaGrilla) para no tocar el
           azul del calendario de Cita Previa, que no tiene ese
           problema y se queda igual. ── */
        #agendaMiaGrilla .cp-dia.hoy { box-shadow: inset 0 0 0 2px var(--viu-acento); }
        #agendaMiaGrilla .cp-dia.hoy.seleccionado { box-shadow: inset 0 0 0 2px white; }
        .cp-dia { position: relative; }
        .cp-dia.mia-dia-reservas { background: rgba(122,32,72,0.16); }
        .cp-dia.mia-dia-citas { background: rgba(62,79,134,0.16); }
        .cp-dia.mia-dia-falla { background: rgba(249,168,37,0.22); }
        .cp-dia.mia-dia-tren { background: rgba(198,40,40,0.16); }
        .cp-dia.mia-dia-combinado { background: rgba(0,0,0,0.09); }
        /* Verde al 75% de opacidad, no sólido (a pedido del cliente) —
           el verde sólido junto con los puntos (también oscuros) se
           perdía con la pantalla poco iluminada. Con opacidad se ve
           fuerte igual, pero deja algo de contraste de más. */
        .cp-dia.mia-dia-reservas.seleccionado,
        .cp-dia.mia-dia-citas.seleccionado,
        .cp-dia.mia-dia-falla.seleccionado,
        .cp-dia.mia-dia-tren.seleccionado,
        .cp-dia.mia-dia-combinado.seleccionado,
        #agendaMiaGrilla .cp-dia.seleccionado { background: rgba(46,125,50,0.75); }
        .mia-puntos-dia { position: absolute; bottom: 3px; left: 50%; transform: translateX(-50%); display: flex; gap: 3px; }
        /* Halo blanco alrededor de cada punto, y puntos más grandes (a
           pedido del cliente) — antes 4px, ahora 6px, para que se vean
           mejor incluso contra el verde de "seleccionado". */
        .mia-punto { width: 6px; height: 6px; border-radius: 50%; display: inline-block; box-shadow: 0 0 0 1.5px white; }
        .agenda-mia-leyenda { display: flex; justify-content: center; gap: 16px; margin-bottom: var(--vm-esp-seccion); font-size: var(--vm-fs-caption); color: var(--vm-texto); }
        .agenda-mia-leyenda span { display: inline-flex; align-items: center; gap: 5px; }
        .punto-leyenda-mia { display: inline-block; width: 8px; height: 8px; border-radius: 50%; }

        /* ── Calendario de Agenda de Falla (Sesión 52) — mismo patrón
           visual exacto que el calendario de Cita Previa de arriba
           (cp-*), con su propio prefijo porque es un contexto distinto
           (elegir un día para VER actividades, no para reservar un
           turno), y con el agregado del punto que marca qué días tienen
           algo cargado. ── */
        .agenda-calendario-nav { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }
        .agenda-calendario-nav button {
            background: #f2f2f2; border: none; border-radius: 8px; padding: 7px 12px; cursor: pointer; font-size: var(--vm-fs-cuerpo-destacado);
        }
        .agenda-calendario-nav button:disabled { opacity: 0.3; cursor: default; }
        .agenda-calendario-nav span { font-family: 'Inter', sans-serif; font-weight: 600; color: var(--viu-oscuro); font-size: var(--vm-fs-cuerpo-destacado); text-transform: capitalize; }
        .agenda-grilla-calendario { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; margin-bottom: 14px; }
        .agenda-grilla-calendario .agenda-nombre-dia { text-align: center; font-size: var(--vm-fs-caption); color: #999; font-weight: 700; padding: 4px 0; }
        .agenda-dia {
            aspect-ratio: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
            border-radius: 8px; font-size: var(--vm-fs-cuerpo); cursor: pointer; background: #f7f7f7; color: var(--viu-oscuro); border: none;
        }
        .agenda-dia:hover:not(:disabled), .agenda-dia:active:not(:disabled) { background: #fdecc4; }
        /* Dorado en vez de azul (a pedido del cliente) — todo lo de
           Fallas se marca siempre con el mismo dorado del botón de
           Donar, para que sea reconocible como "esto es de Fallas" de
           un vistazo, sin depender del texto. */
        .agenda-dia.seleccionado { background: #f9a825; color: white; font-weight: 700; }
        .agenda-dia.hoy { box-shadow: inset 0 0 0 2px #f9a825; font-weight: 700; }
        .agenda-dia.hoy.seleccionado { box-shadow: inset 0 0 0 2px white; }
        .agenda-dia:disabled { color: #ccc; background: none; cursor: default; }
        .agenda-dia.agenda-vacio { visibility: hidden; }
        .agenda-dia .punto-evento-dia { width: 4px; height: 4px; border-radius: 50%; background: #f9a825; }
        .agenda-dia.seleccionado .punto-evento-dia { background: white; }
        /* Tarjeta de cada acto (a pedido del cliente) — reemplaza la
           lista de texto plano con una línea gris entre uno y otro. */
        .tarjeta-acto-agenda {
            background: var(--vm-fondo-calido); border-radius: 10px; padding: 10px 12px;
            margin-bottom: var(--vm-esp-pegado);
        }
        .hora-acto-agenda { font-weight: 700; color: #b8860b; font-size: var(--vm-fs-cuerpo); }
        .descripcion-acto-agenda { font-size: var(--vm-fs-cuerpo); color: var(--vm-texto); margin-top: var(--vm-esp-pegado); line-height: var(--vm-lh-cuerpo); }
        /* Actividad de la Falla dentro de "Mi Agenda Viu Map" — mismo
           concepto de tarjeta, en dorado, con una línea aparte que dice
           de qué Falla viene (son actividades de solo lectura, creadas
           desde la Falla, no por el usuario). */
        .tarjeta-acto-falla-mia { background: #fff8e1; }
        .hora-acto-falla-mia { color: #b8860b; }
        .origen-acto-falla-mia { font-size: var(--vm-fs-caption); color: #a3781a; margin-top: var(--vm-esp-pegado); }
        /* Tren agendado dentro de "Mi Agenda Viu Map" (Sesión 83) —
           mismo concepto de tarjeta, en rojo (color de RENFE), con
           botón de quitar propio: a diferencia de la Falla, esto lo
           crea el propio usuario, así que sí se puede borrar. */
        .tarjeta-acto-tren-mia { background: #ffebee; position: relative; padding-right: 66px; }
        .hora-acto-tren-mia { color: #c62828; }
        .origen-acto-tren-mia { font-size: var(--vm-fs-caption); color: #a31c1c; margin-top: var(--vm-esp-pegado); }
        .btn-quitar-tren-mia {
            position: absolute; top: 10px; right: 10px; border: none; background: none;
            color: #c62828; font-size: var(--vm-fs-caption); font-weight: 700; cursor: pointer; padding: 4px;
        }

        .cp-grilla-horarios { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; max-height: 280px; overflow-y: auto; padding: 2px; margin-bottom: 6px; }
        .cp-horario {
            padding: 9px 4px; border-radius: 10px; text-align: center; font-size: var(--vm-fs-cuerpo); font-weight: 600;
            border: none; cursor: pointer; background: #e8f5e9; color: #1b5e20;
        }
        .cp-horario:hover:not(:disabled), .cp-horario:active:not(:disabled) { background: #c8e6c9; }
        .cp-horario.seleccionado { background: var(--vm-azul); color: white; }
        .cp-horario:disabled { background: #eee; color: #bbb; cursor: default; }
        .cp-vacio-horarios { grid-column: 1 / -1; text-align: center; padding: 16px; color: #8ba3a8; font-size: var(--vm-fs-cuerpo); }

        .cp-botones-paso { display: flex; gap: 10px; margin-top: 4px; }
        .cp-botones-paso .btn-atras {
            flex: 0 0 auto; background: #f2f2f2; color: #555; border: none; padding: 11px 16px;
            border-radius: 12px; font-size: var(--vm-fs-cuerpo); font-weight: 600; cursor: pointer;
        }
        .cp-botones-paso .btn-atras:hover, .cp-botones-paso .btn-atras:active { background: #e5e5e5; }

        /* ── Encabezado de la ficha: logo redondo a la izquierda,
           nombre/categoría/valoración a la derecha. Alineado por
           arriba (a pedido del cliente) — el borde superior del logo
           queda a la misma altura que el borde superior del nombre,
           en vez del "estilo Facebook" de antes (logo superpuesto al
           borde inferior de la galería, alineado por abajo con el
           nombre). ── */
        /* margin-bottom bajado de var(--vm-esp-seccion) (10px) a 6px el
           21 de septiembre de 2026, a pedido de Jaime: la línea de
           valoraciones quedaba demasiado separada de la de
           "Abierto ahora"/"Cerrado". Se pone el valor a mano en vez de
           tocar la variable porque --vm-esp-seccion se usa en 16 sitios
           más del CSS y allí el espacio está bien. */
        .ficha-cabecera-fb { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 6px; }
        .ficha-logo {
            width: 76px; height: 76px; border-radius: 50%; object-fit: cover;
            border: 3px solid var(--vm-dorado); background: white; display: block; flex-shrink: 0;
            position: relative; z-index: 2; box-shadow: 0 2px 8px rgba(0,0,0,0.18);
        }
        .ficha-info-cabecera { flex: 1; min-width: 0; padding-bottom: 2px; }
        .ficha-clasificacion { text-align: left; font-size: var(--vm-fs-caption); color: #999; margin-top: var(--vm-esp-pegado); }
        .ficha-vuelvo-en {
            display: none; background: #fff3e0; color: #e65100; font-weight: 700;
            font-size: var(--vm-fs-cuerpo); padding: 8px 12px; border-radius: 8px; margin: 10px 0; text-align: center;
        }
        .ficha-vuelvo-en.visible { display: block; }

        /* ── Galería de fotos: siempre se ve la principal primero;
           las otras 3 (si existen) se navegan con las flechas laterales,
           nunca se muestran solas ni por delante de la principal. ── */
        .ficha-galeria {
            display: none; position: relative; width: calc(100% + 44px);
            margin: -22px -22px 14px; aspect-ratio: 4/3; background: #eee;
            overflow: hidden; border-radius: 16px 16px 0 0;
        }
        .ficha-galeria.visible { display: block; }
        .ficha-galeria img { width: 100%; height: 100%; object-fit: cover; display: block; transition: opacity 0.18s ease; }
        .ficha-galeria img.cambiando-foto { opacity: 0; }
        .btn-galeria-flecha {
            position: absolute; top: 50%; transform: translateY(-50%);
            background: rgba(0,0,0,0.45); color: white; border: none;
            width: 32px; height: 32px; border-radius: 50%; font-size: var(--vm-fs-subtitulo); cursor: pointer;
            align-items: center; justify-content: center; display: none;
        }
        .btn-galeria-flecha.izquierda { left: 10px; }
        .btn-galeria-flecha.derecha { right: 10px; }
        .galeria-puntos { position: absolute; bottom: 8px; left: 0; right: 0; display: flex; justify-content: center; gap: 5px; }
        .punto-galeria { width: 6px; height: 6px; border-radius: 50%; background: rgba(255,255,255,0.55); }
        .punto-galeria.activo { background: white; }
        .ficha-cabecera { display: flex; flex-direction: column; align-items: flex-start; justify-content: center; gap: 0; }
        .ficha-nombre { font-size: var(--vm-fs-subtitulo); font-weight: 800; color: var(--viu-oscuro); text-align: left; line-height: 1.2; }
        .btn-estrella-ficha { border: none; background: none; font-size: var(--vm-fs-titulo-grande); cursor: pointer; }
        .ficha-estado-linea { text-align: center; margin-bottom: 6px; }

        /* ── Valoración general (0-5 estrellas), distinta de las 3
           etiquetas personales — esta es la opinión agregada de todos
           los visitantes, no la del que está mirando en este momento.
           Ahora compacta, dentro de la columna de info junto al logo. ── */
        .ficha-valoracion { text-align: left; margin-top: 3px; }
        .estrellas-valoracion { display: inline-flex; gap: 1px; cursor: pointer; line-height: 1; }
        .estrella-val { font-size: var(--vm-fs-subtitulo); color: #ddd; transition: color 0.15s; }
        .estrella-val.llena { color: #f9a825; }
        .estrella-val-escultura { font-size: var(--vm-fs-hero); color: #ddd; transition: color 0.15s; cursor: pointer; }
        .estrella-val-escultura.llena { color: #f9a825; }
        .mi-valoracion { font-size: var(--vm-fs-caption); color: var(--vm-dorado); font-weight: 600; margin-top: var(--vm-esp-pegado); min-height: 4px; }
        .texto-valoracion { font-size: var(--vm-fs-caption); color: #999; margin-top: var(--vm-esp-pegado); }
        .nota-valoracion { font-size: 11px; color: #6b7280; line-height: 1.35; margin-top: 2px; max-width: 290px; }   /* Etapa 93a */
        .ficha-estado { display: inline-block; font-weight: 700; font-size: var(--vm-fs-cuerpo); padding: 3px 12px; border-radius: 12px; }
        .ficha-estado.abierto { background: #e8f5e9; color: #2e7d32; }
        .ficha-estado.cerrado { background: #ffebee; color: #c62828; }
        .ficha-estado.pronto { background: #fff3e0; color: #e65100; }
        .ficha-tramos { text-align: center; font-size: var(--vm-fs-caption); color: #999; margin-top: var(--vm-esp-pegado); }
        .ficha-contacto { font-size: var(--vm-fs-cuerpo); color: #444; line-height: var(--vm-lh-cuerpo); margin-bottom: var(--vm-esp-accion); }
        .ficha-redes { display: flex; gap: 12px; justify-content: center; align-items: center; margin-bottom: var(--vm-esp-accion); flex-wrap: wrap; }
        .ficha-redes a { display: block; line-height: 0; }
        .ficha-redes img { width: 34px; height: 34px; object-fit: contain; display: block; }
        /* ── Ícono de Agenda de Fallas (antes era la fila ancha
           .fila-footer-falla, para la Comisión) — mismo tamaño (34px) y
           estilo plano que los íconos de redes de al lado (Facebook,
           Instagram), sin fondo ni borde, para que no desentone. Va
           primero en la fila, antes de las redes sociales. ── */
        .ficha-redes .btn-redes-agenda {
            width: 34px; height: 34px; border: none; padding: 0;
            cursor: pointer; display: flex; align-items: center; justify-content: center;
            /* El calendario tiene el cuerpo color crema — sobre el fondo
               blanco de la ficha se perdía casi entero. Un fondo gris
               claro detrás le da el contraste que le faltaba, mismo
               criterio que ya usan los botones de favoritos. */
            background: #f2f2f2; border-radius: 50%;
        }
        .ficha-redes .btn-redes-agenda img { width: 72%; height: 72%; object-fit: contain; display: block; }

        /* ── Fila principal: Ir / Compartir / Llamar — rediseñada a
           pedido del cliente, tomando como concepto una referencia que
           él mismo diseñó (ícono al lado del texto, no arriba; forma
           más baja, gana espacio vertical). No se copió literal: esa
           referencia tenía el texto quemado como imagen (no serviría
           al cambiar de idioma) y colores sueltos (verde/azul/rojo
           genéricos) — acá el texto sigue siendo texto real
           (data-i18n, cambia de idioma sin tocar nada) y los 3 colores
           salen de la paleta propia: verde de marca para "Ir" (mismo
           verde que ya usan los otros botones "Ir" del mapa), azul
           marino para "Compartir", rojo de acento para "Llamar" — un
           uso puntual y justificado del rojo, no masivo. Los íconos
           son de línea (stroke), mismo estilo que ya usan clima/mar,
           no rellenos sólidos como la referencia. ── */
        .fila-acciones-principales { display: flex; gap: 8px; margin-bottom: var(--vm-esp-seccion); }
        .btn-accion-principal {
            flex: 1; display: flex; flex-direction: row; align-items: center; justify-content: center; gap: 6px;
            border: none; border-radius: 10px; color: white;
            padding: 10px 6px; font-size: var(--vm-fs-cuerpo); font-weight: 700; cursor: pointer;
            font-family: 'Inter', sans-serif; line-height: var(--vm-lh-cuerpo); text-align: center;
            /* Mismo bisel sutil que ya tienen Reservar/Cita Previa/Donar/
               Comprar entradas (a pedido del cliente), pero sin la
               sombra flotante de aquellos — estos botones no están
               "pegados" (sticky) como esos, viven dentro del flujo
               normal de la ficha, así que no necesitan esa sombra de
               "levantado sobre la página", solo el volumen propio. */
            box-shadow:
                inset 0 1px 0 rgba(255,255,255,0.35),
                inset 0 -2px 3px rgba(0,0,0,0.15);
        }
        .btn-accion-principal .icono-accion { display: inline-flex; flex-shrink: 0; }
        .btn-accion-principal .icono-accion svg { width: 1.3em; height: 1.3em; display: block; }
        .btn-accion-principal:disabled { opacity: 0.4; cursor: default; }
        .btn-accion-ir { background: var(--viu-acento); }
        .btn-accion-ir:hover, .btn-accion-ir:active { background: #266b2a; }
        .btn-accion-compartir { background: var(--vm-azul); }
        .btn-accion-compartir:hover, .btn-accion-compartir:active { background: var(--vm-azul-oscuro); }
        .btn-accion-llamar { background: var(--vm-acento-rojo); }
        .btn-accion-llamar:hover, .btn-accion-llamar:active { background: #A82525; }

        .fila-compartir-opciones {
            display: none; gap: 5px; margin-bottom: var(--vm-esp-seccion);
            background: #f7f7f7; border-radius: 10px; padding: 6px;
        }
        .fila-compartir-opciones.abierta { display: flex; }
        .btn-compartir-opcion {
            flex: 1; display: flex; flex-direction: column; align-items: center; gap: 4px;
            background: white; border: 1px solid #e0e0e0; border-radius: 8px;
            padding: 8px 4px; font-size: var(--vm-fs-caption); font-weight: 600; color: var(--viu-oscuro); cursor: pointer;
        }
        .btn-compartir-opcion:hover, .btn-compartir-opcion:active { background: #f0f0f0; }
        .btn-compartir-opcion:disabled { opacity: 0.5; cursor: default; }
        .icono-compartir-opcion { width: 20px; height: 20px; object-fit: contain; display: block; }
        .icono-emoji-compartir { font-size: var(--vm-fs-subtitulo); line-height: 20px; width: auto; height: auto; }

        /* .ficha-btn-carta y el contenedor .ficha-acciones que la
           envolvía se retiraron (Sesión 50) — Ver Carta pasó a ser un
           ícono redondo más en .ficha-redes, a la izquierda del botón
           de la página web, en vez de una píldora aparte debajo de las
           redes. Ver REGISTRO_ICONOS y fichaRedes más abajo. */

        /* ── Botón grande de Reservas/Cita Previa — pegado (sticky) abajo
           de la ficha en vez de ser una píldora más entre el resto de
           acciones, para que quede siempre visible mientras se hace
           scroll, superpuesto al contenido que pasa por detrás (fotos
           arriba, datos de contacto más abajo). Ocupa todo el ancho de
           la ficha, con las mismas esquinas redondeadas de abajo que el
           marco de la ficha. ── */
        .ficha-btn-sticky-wrap {
            position: sticky; bottom: -22px; z-index: 5;
            margin: 12px -22px -22px; width: calc(100% + 44px);
        }
        .btn-reserva-sticky {
            display: flex; align-items: center; justify-content: center; gap: 8px;
            width: 100%; border: none; cursor: pointer; text-decoration: none;
            /* text-decoration:none — hace falta porque "Comprar entradas"
               (festivales) es un <a>, no un <button> como los otros 3
               que comparten esta misma clase; los enlaces vienen
               subrayados por defecto en el navegador, con letra Y hasta
               el ícono subrayados. Los <button> nunca tuvieron este
               problema, así que no se nota el cambio ahí. */
            /* Más delgado (Sesión 67, a pedido del cliente) — se bajó
               solo el padding vertical (15px -> 9px), la letra queda
               exactamente del mismo tamaño de siempre. Aplica a los 4
               botones que comparten esta misma clase: Reservar, Cita
               Previa, Donar a la Falla y Comprar entradas (festivales). */
            padding: 9px 15px;
            background: var(--vm-azul); color: white; font-size: var(--vm-fs-subtitulo); font-weight: 700;
            border-radius: 0 0 16px 16px;
            /* Bisel sutil (a pedido del cliente) — un hilo de luz arriba
               y una sombra suave hacia adentro abajo, para que se lea
               como un botón con volumen propio, no un rectángulo de
               color plano. Se suma como más sombras en la misma
               propiedad (separadas por coma), sin tocar la sombra
               flotante que ya tenía (la primera de la lista). Aplica
               igual a los 4 botones que comparten esta clase: Reservar,
               Cita Previa, Donar a la Falla y Comprar entradas. */
            box-shadow:
                0 -4px 14px rgba(0,0,0,0.12),
                inset 0 1px 0 rgba(255,255,255,0.35),
                inset 0 -2px 3px rgba(0,0,0,0.15);
            /* ── Brillo invitando al clic (a pedido del cliente) — barrido
               sutil de izquierda a derecha, cada cierto tiempo, no
               constante. position:relative + isolation:isolate para que
               el ::before (el barrido) quede contenido DENTRO del botón
               y pintado DETRÁS de las letras/ícono — un pseudo-elemento
               posicionado, sin más, pintaría por encima del texto (así
               funciona el orden de pintado de CSS), por eso lleva
               z-index:-1 y el botón necesita su propio contexto de
               apilamiento para que ese -1 no se escape hacia afuera. ── */
            position: relative; overflow: hidden; isolation: isolate;
        }
        .btn-reserva-sticky::before {
            content: ''; position: absolute; top: 0; left: -60%;
            width: 45%; height: 100%; z-index: -1; pointer-events: none;
            background: linear-gradient(115deg, transparent 0%, rgba(255,255,255,0.45) 45%, rgba(255,255,255,0.45) 55%, transparent 100%);
            animation: vm-brillo-boton-cta 3.6s ease-in-out infinite;
        }
        @keyframes vm-brillo-boton-cta {
            0%, 45% { left: -60%; }
            75%, 100% { left: 115%; }
        }
        .btn-reserva-sticky:hover, .btn-reserva-sticky:active { background: var(--vm-azul-oscuro); }
        .btn-reserva-sticky .icono-accion { font-size: var(--vm-fs-hero); }
        /* Donar a la Falla usa el mismo footer sticky, pero con su color
           dorado propio para no
           confundirlo con las acciones propias de Viu Map (Reservas/Cita
           Previa), que siguen en verde de marca. */
        .btn-sticky-donar { background: #f9a825; color: #5c3c00; }
        .btn-sticky-donar:hover, .btn-sticky-donar:active { background: #f57f17; }
        /* Reservar con su propio color (a pedido del cliente) — un vino
           que no se parece a ningún otro color de acción del mapa
           (verde de Ir, azul de Compartir/Cita Previa, rojo de Llamar,
           dorado de Donar/Fallas), para que se reconozca de un vistazo
           en un lugar turístico donde reservar mesa es el pan de cada
           día. */
        .btn-sticky-reservar { background: #7A2048; }
        .btn-sticky-reservar:hover, .btn-sticky-reservar:active { background: #5E1836; }

        /* ── PASO 1.3 — quitar el recorrido peatonal ya trazado ── */
        .btn-quitar-recorrido {
            position: absolute; bottom: 24px; left: 50%; transform: translateX(-50%);
            z-index: 900; background: rgba(233, 30, 78, 0.5); color: #ffffff;
            border: none; border-radius: 20px; padding: 10px 18px;
            font-size: var(--vm-fs-cuerpo); font-weight: 700;
            box-shadow: 0 6px 16px rgba(0,0,0,0.35); cursor: pointer;
            align-items: center; gap: 6px; display: none;
        }
        .btn-quitar-recorrido.visible { display: flex; }

        /* Chip con la distancia real de la ruta trazada (o línea recta si
           OSRM no respondió) — aparece junto con "Quitar recorrido". */
        .distancia-recorrido {
            position: absolute; bottom: 68px; left: 50%; transform: translateX(-50%);
            z-index: 900; background: rgba(27,27,27,0.75); color: #ffffff;
            border-radius: 20px; padding: 6px 14px; font-size: var(--vm-fs-cuerpo); font-weight: 700;
            box-shadow: 0 4px 12px rgba(0,0,0,0.3); display: none;
        }
        .distancia-recorrido.visible { display: block; }

        /* ── PASO 1.1 — marcadores de negocio, versión mínima de prueba ──
           El sistema completo (tamaños por zoom, panel de ajuste editable
           sin tocar código, prospectos comerciales visibles a simple
           vista) llega en el Paso 1.2. Esto es solo para confirmar que
           las 3 variantes descritas en la Sección 6 se ven bien:
           gris sin pauta / logo circular con pauta / emoji con pauta sin logo. */
        /* ── Aro de resaltado del buscador del mapa (Sesión 30) — envuelve
           el pin normal (el que sea: gris, logo, emoji, Sitio de
           Interés...) sin cambiar nada de su estilo propio, solo le
           agrega un aro pulsante alrededor para que salte a la vista
           entre todos los demás. position:relative + el pin de adentro
           en position:absolute con 8px de margen (mitad del aro extra
           de 16px) para quedar centrado. ── */
        .aro-resultado-busqueda {
            position: relative; border-radius: 50%;
            animation: pulsoResultadoBusqueda 1.1s ease-out infinite;
        }
        @keyframes pulsoResultadoBusqueda {
            0%   { box-shadow: 0 0 0 0 rgba(255,171,0,0.55); }
            70%  { box-shadow: 0 0 0 10px rgba(255,171,0,0); }
            100% { box-shadow: 0 0 0 0 rgba(255,171,0,0); }
        }

        .pin-negocio-gris {
            width: 16px; height: 16px; border-radius: 50%;
            background: var(--viu-acento); border: 2px solid white;
            box-shadow: 0 2px 6px rgba(0,0,0,0.35);
        }
        .pin-negocio-logo {
            width: 34px; height: 34px; border-radius: 50%;
            background-size: cover; background-position: center; background-color: white;
            border: 2px solid var(--viu-acento);
            box-shadow: 0 2px 8px rgba(0,0,0,0.35);
        }
        .pin-negocio-emoji {
            width: 30px; height: 30px; border-radius: 50%;
            background: white; border: 2px solid var(--viu-acento);
            box-shadow: 0 2px 8px rgba(0,0,0,0.35);
            display: flex; align-items: center; justify-content: center; font-size: var(--vm-fs-subtitulo);
        }

        /* ── Lugares Especiales (esEspecial): faros del mapa — más
           grandes que un pin de negocio normal, sin el marco redondo
           tipo "botón" (esquinas suaves en vez de círculo completo),
           con borde blanco en vez del acento de color para que
           resalten sobre cualquier fondo. ── */
        .pin-especial-logo {
            width: 46px; height: 46px; border-radius: 10px;
            background-size: cover; background-position: center; background-color: white;
            border: 3px solid white;
            box-shadow: 0 3px 10px rgba(0,0,0,0.45);
        }
        .pin-especial-emoji {
            width: 46px; height: 46px; border-radius: 10px;
            background: white; border: 3px solid white;
            box-shadow: 0 3px 10px rgba(0,0,0,0.45);
            display: flex; align-items: center; justify-content: center;
        }
        /* ── Sombra del pin, apagable por lugar desde lugares_especiales_panel.html
           (campo sombraPin) — activada por defecto, así que esta clase
           solo entra en juego cuando el admin la apaga a propósito. ── */
        .pin-especial-logo.sin-sombra, .pin-especial-emoji.sin-sombra { box-shadow: none; }

        /* ── Pin de la Escultura (Fallas_panel.html) — usa el emoji festivo
           🎉 siempre (no la foto — vive en fotosNegocios/{id}_escultura
           y se carga a demanda, no hay caché disponible al dibujar los
           pines del mapa entero). Borde rojo festivo, distinto del
           verde de marca del pin de la Comisión, para que el ciudadano
           distinga de un vistazo cuál es cuál. ── */
        .pin-escultura-emoji {
            border-radius: 10px; background: white; border: 3px solid #c62828;
            box-shadow: 0 3px 10px rgba(0,0,0,0.45);
            display: flex; align-items: center; justify-content: center;
        }
        .pin-escultura-emoji.sin-sombra { box-shadow: none; }

        /* ── Sitios de Interés (esSitioInteres): el PNG con transparencia
           que carga el admin se ubica tal cual, sin ningún recuadro ni
           fondo alrededor — a diferencia de Lugares Especiales, acá la
           imagen ES el pin. Un drop-shadow (sigue el contorno real del
           PNG, no un cuadro) es lo único que se agrega, para que no se
           pierda contra el mapa. ── */
        .pin-sitio-interes {
            display: block; object-fit: contain;
            filter: drop-shadow(0 2px 4px rgba(0,0,0,0.5));
        }
        .pin-sitio-interes-emoji {
            width: 46px; height: 46px; border-radius: 10px;
            background: white; border: 3px solid white;
            box-shadow: 0 3px 10px rgba(0,0,0,0.45);
            display: flex; align-items: center; justify-content: center;
        }
        /* ── Sombra de Sitios de Interés, apagable por lugar desde
           Sitios_Interes_panel.html (campo sombraPin) — activada por defecto.
           El PNG usa filter (drop-shadow), el emoji de respaldo usa
           box-shadow — cada uno necesita su propia propiedad anulada. ── */
        .pin-sitio-interes.sin-sombra { filter: none; }
        .pin-sitio-interes-emoji.sin-sombra { box-shadow: none; }

        /* ── Estado del Mar: pines fijos en las 3 playas de Cullera,
           siempre visibles — no son "lugares" (sin ficha de negocio),
           es información ambiental. Mismo criterio que Sitios de
           Interés: la imagen es el pin, sin marco. ── */
        .pin-estado-mar {
            display: block; object-fit: contain; cursor: pointer;
            filter: drop-shadow(0 2px 4px rgba(0,0,0,0.5));
        }
        .fondo-ficha-mar {
            position: fixed; inset: 0; background: rgba(0,0,0,0.5); z-index: 3000;
            display: none; align-items: center; justify-content: center; padding: 16px;
        }
        .fondo-ficha-mar.abierto { display: flex; }
        /* El selector "¿Cómo vas?" reutiliza esta misma clase (#fondoModoIr)
           — segunda barrera además del cierre explícito de la ficha: si
           algún llamador nuevo se olvida de cerrar su propio panel antes
           de abrir el selector, este siempre queda arriba de cualquier
           ficha, nunca oculto detrás (bug real, Sesión 18 continuación). */
        #fondoModoIr.abierto { z-index: 3100; }
        /* Mismo motivo que #fondoModoIr, línea de arriba — este selector
           chico se abre desde ARRIBA del modal de horarios de RENFE
           (#fondoHorariosRenfe, .fondo-ficha, mismo z-index:3000), así
           que sin este empuje quedaba dibujado detrás en vez de
           encima, por venir antes en el orden del HTML. */
        #fondoAgendarTren.abierto { z-index: 3100; }
        .ficha-mar {
            background: white; border-radius: 16px; width: 100%; max-width: 380px;
            max-height: 85vh; overflow-y: auto; padding: 22px; position: relative;
            box-shadow: 0 20px 50px rgba(0,0,0,0.3);
        }
        /* ── Encabezado de la ficha de Estado del Mar — reorganizado
           (Sesión 24, a pedido del cliente, con imagen de referencia):
           ícono a la izquierda, más grande, abarcando las 2 líneas de
           título; "Estado del Mar" chico arriba, nombre de la playa
           grande y en negrita abajo (es la parte que importa de un
           vistazo), "Fuente: AEMET" debajo de las 2. ── */
        .ficha-mar-cabecera { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; }
        .ficha-mar-icono { width: 52px; height: 52px; flex-shrink: 0; }
        .ficha-mar-icono svg { width: 100%; height: 100%; display: block; }
        .ficha-mar-textos { min-width: 0; }
        .ficha-mar-subtitulo {
            color: #0288d1; font-size: var(--vm-fs-cuerpo); font-weight: 700; line-height: 1.25;
            text-transform: uppercase; letter-spacing: 0.3px;
        }
        .ficha-mar h2 { color: #01579b; font-size: var(--vm-fs-titulo-grande); font-weight: 800; margin: 0; line-height: 1.2; text-transform: uppercase; letter-spacing: 0.3px; }
        .ficha-mar .fuente-mar { font-size: var(--vm-fs-caption); color: #999; margin-top: var(--vm-esp-pegado); }
        .grid-estado-mar { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 14px; }
        .dato-mar { background: #e1f5fe; border-radius: 10px; padding: 10px 12px; }
        .dato-mar .etiqueta-dato-mar { font-size: var(--vm-fs-caption); color: #0277bd; text-transform: uppercase; letter-spacing: .03em; font-weight: 700; }
        .dato-mar .valor-dato-mar { font-size: var(--vm-fs-cuerpo-destacado); color: #01579b; font-weight: 600; margin-top: var(--vm-esp-pegado); }

        /* ══════════════════════════════════════════════════════════
           Fase 6 del rediseño gráfico — Estado del Mar con el mismo
           nivel de detalle que el clima. .mar-hero también lleva la
           clase .clima-hero (ver HTML) para heredar gratis todos los
           fondos de cielo por escena ya definidos ahí — cero reglas
           duplicadas entre las 2 fichas.
           ══════════════════════════════════════════════════════════ */
        /* Altura FIJA, no mínima (a pedido del cliente, sesión del 16 de
           septiembre de 2026) — antes era min-height, así que el modal
           cambiaba de tamaño entre consultas según cuántos chips
           aparecían (viento, oleaje, sensación térmica no siempre están
           los 4 a la vez) y si había o no overlay de lluvia/nieve/nubes
           con viento encima de las olas. El valor cubre con margen la
           combinación más alta posible (título + ícono + temperaturas +
           2 filas de chips) — a revisar en vivo si hiciera falta un
           ajuste fino, sin poder medirlo en un navegador real desde
           acá. */
        .mar-hero { height: 260px; }
        .mar-hero-olas {
            position: absolute; left: 0; right: 0; bottom: 0; height: 44%;
            overflow: hidden; z-index: 0; pointer-events: none; border-radius: 0 0 14px 14px;
        }
        .mar-ola-capa {
            position: absolute; left: 0; bottom: 0; width: 200%; height: 100%;
            animation-name: mar-mover-ola; animation-timing-function: linear; animation-iteration-count: infinite;
        }
        @keyframes mar-mover-ola { from { transform: translateX(0); } to { transform: translateX(-50%); } }
        .mar-hero-contenido { flex: 1; text-align: center; position: relative; z-index: 1; }
        /* Blanco siempre (a pedido del cliente, sesión del 16 de
           septiembre de 2026) — antes era un azul oscuro (#0E6E8C) sin
           ninguna sombra que lo protegiera: se leía bien contra el
           celeste brillante de "Despejado", pero se perdía contra los
           fondos más grises/oscuros de Nuboso, Muy Nuboso y Cubierto
           (de día y de noche). Con blanco + una sombra sutil (mismo
           valor que ya usan los chips de temperatura, coherencia entre
           elementos), se lee bien en cualquiera de los 2 extremos, sin
           necesitar un color distinto por escena. */
        .mar-hero-fecha { font-family: 'Inter', sans-serif; font-weight: 600; font-size: var(--vm-fs-cuerpo-destacado); color: #FFFFFF; text-shadow: 0 1px 3px rgba(0,0,0,0.35); }
        .mar-hero-cielo { display: flex; align-items: center; justify-content: center; margin: 2px 0; }
        .mar-hero-cielo svg { display: block; width: 46px; height: 46px; }
        .mar-hero-temps { display: flex; align-items: center; justify-content: center; gap: 10px; margin-top: 2px; }
        .mar-hero-temp-col { display: flex; flex-direction: column; align-items: flex-start; }
        .mar-hero-etiqueta-temp {
            display: inline-flex; align-items: center; gap: 3px; margin-top: 2px;
            font-size: var(--vm-fs-caption); color: #FFFFFF; font-weight: 600;
            text-shadow: 0 1px 3px rgba(0,0,0,0.35);
        }
        .mar-hero-etiqueta-temp svg { flex-shrink: 0; }
        /* Colores de respaldo — se ven solo antes de que cargue el dato
           real, o si no hay temperatura para mostrar ("—°"). Con dato
           real, el color lo pone la escala de colorParaTemperatura()
           directo en el elemento (ver renderMarMomento()). Sombra suave
           (a pedido del cliente) para que el número resalte un poco más
           sin verse invasiva — mismo valor que el del clima, coherencia
           entre las 2 fichas. */
        /* Mismo criterio que clima-hero-temp: borde por fuera (sombras
           apiladas, no text-stroke), más oscuro y al 70% de opacidad.
           A diferencia del clima, acá el borde se queda en escritorio/
           tablet y se saca solo en celular (a pedido del cliente,
           Sesión 76) — ver el @media más abajo. */
        .mar-hero-temp-agua {
            font-family: 'Inter', sans-serif; font-weight: 700; font-size: var(--vm-fs-hero); color: #0288d1;
            text-shadow:
                1px 0 0 rgba(60,60,60,0.7), -1px 0 0 rgba(60,60,60,0.7),
                0 1px 0 rgba(60,60,60,0.7), 0 -1px 0 rgba(60,60,60,0.7),
                0 2px 4px rgba(0,0,0,0.15);
        }
        .mar-hero-temp-aire {
            font-family: 'Inter', sans-serif; font-weight: 700; font-size: var(--vm-fs-hero); color: #FF8A3D;
            text-shadow:
                1px 0 0 rgba(60,60,60,0.7), -1px 0 0 rgba(60,60,60,0.7),
                0 1px 0 rgba(60,60,60,0.7), 0 -1px 0 rgba(60,60,60,0.7),
                0 2px 4px rgba(0,0,0,0.15);
        }
        @media (max-width: 719px) {
            .mar-hero-temp-agua, .mar-hero-temp-aire { text-shadow: 0 2px 4px rgba(0,0,0,0.15); }
        }
        /* Blanco siempre, mismo motivo que .mar-hero-fecha arriba —
           antes era negro de día (invisible contra fondos oscuros) con
           una excepción manual para de noche; ahora un solo valor que
           funciona en cualquier escena. */
        .mar-hero-separador { font-size: var(--vm-fs-titulo); color: rgba(255,255,255,0.75); font-weight: 300; text-shadow: 0 1px 3px rgba(0,0,0,0.35); }
        .mar-datos {
            display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; margin-bottom: 14px;
        }
        .mar-datos .clima-hero-chip { width: 100%; box-sizing: border-box; }
        .mar-datos .clima-hero-chip { background: #E1F5FE; color: #01579B; }
        /* Chips con título — antes solo tenían ícono+valor, sin decir
           qué dato representaban ("Flojo" solo no dice nada). */
        .mar-chip-titulada { align-items: center; padding: 8px 10px; }
        .mar-chip-titulada svg { flex-shrink: 0; }
        .mar-chip-textos { display: flex; flex-direction: column; gap: 1px; }
        .mar-chip-etiqueta { font-size: var(--vm-fs-micro); text-transform: uppercase; letter-spacing: 0.03em; font-weight: 700; opacity: 0.7; }
        .mar-chip-valor { font-size: var(--vm-fs-cuerpo); font-weight: 700; line-height: var(--vm-lh-cuerpo); }
        .mar-franjas { display: flex; gap: 32px; justify-content: center; margin-bottom: 10px; }
        .mar-franja-oleaje { display: inline-flex; align-items: center; gap: 3px; font-size: var(--vm-fs-caption); color: #0277bd; font-weight: 600; margin-top: var(--vm-esp-pegado); }
        .mar-franja-oleaje svg { flex-shrink: 0; }
        /* Marca cuál de las 2 mitades (mañana/tarde) es la que se ve
           arriba en el hero — referencia rápida sin tener que releer el
           encabezado. */
        .mar-franja-activa { background: #E1F5FE; border-radius: 10px; padding-left: 10px; padding-right: 10px; margin: 0 -8px; }
        .mar-aviso-generico { text-align: center; color: #999; font-size: var(--vm-fs-cuerpo); padding: 10px 0; }

        /* ── Cartel de precios de gasolinera (imagen del cliente) — en
           miniatura por defecto (mismo tamaño que un pin de Lugar
           Especial), crece con una transición animada al tocarlo.
           Posiciones de cada fila de dígitos calibradas a mano por el
           cliente sobre la imagen real, con una herramienta aparte. ── */
        .letrero-gasolinera-mapa {
            position: relative; cursor: pointer;
            transition: transform 0.4s cubic-bezier(0.34, 1.2, 0.64, 1);
            transform-origin: center bottom;
        }
        .letrero-gasolinera-mapa img { display: block; width: 100%; }
        .letrero-gasolinera-mapa.expandido { transform: scale(1) !important; }
        .fila-letrero-gasolinera, .fuente-letrero-gasolinera {
            opacity: 0; transition: opacity 0.25s ease; pointer-events: none;
        }
        .letrero-gasolinera-mapa.expandido .fila-letrero-gasolinera,
        .letrero-gasolinera-mapa.expandido .fuente-letrero-gasolinera {
            opacity: 1; transition-delay: 0.15s;
        }
        .fila-letrero-gasolinera {
            position: absolute; display: flex; justify-content: space-between;
            font-family: 'DSEG7 Classic', 'Courier New', monospace;
            color: #eafff0; text-shadow: 0 0 6px #4dff88, 0 0 12px #2eff77, 0 0 2px #fff;
        }
        .fila-letrero-gasolinera span { display: inline-block; text-align: center; }
        .fuente-letrero-gasolinera { text-align: center; color: #888; font-size: var(--vm-fs-micro); margin-top: var(--vm-esp-pegado); }

        /* ── Selector de modo de transporte para "Ir" (Sesión 18) —
           caminando o en vehículo. Importa de verdad: el perfil peatón
           de OSRM permite ir en contraflujo y usar sendas (correcto para
           quien camina), y el perfil de auto respeta el sentido único,
           las restricciones de giro y los accesos prohibidos (necesario
           para no dar una indicación peligrosa a quien maneja). ── */
        .selector-modo-ir {
            background: white; border-radius: 16px; padding: 22px 20px;
            width: min(320px, 88vw); text-align: center; box-shadow: 0 8px 30px rgba(0,0,0,0.28);
        }
        .selector-modo-ir h3 { font-size: var(--vm-fs-subtitulo); color: #1b3a2f; margin-bottom: 16px; font-weight: 700; }
        .botones-modo-ir { display: flex; gap: 12px; }
        .botones-modo-ir button {
            flex: 1; display: flex; flex-direction: column; align-items: center; gap: 6px;
            padding: 16px 10px; border: 2px solid #dde3dd; border-radius: 12px;
            background: #fafbfa; cursor: pointer; font-size: var(--vm-fs-cuerpo); font-weight: 600; color: #1b3a2f;
            transition: background 0.15s, border-color 0.15s;
        }
        .botones-modo-ir button span:first-child { font-size: var(--vm-fs-hero); }
        /* ── Agendar un tren puntual a Mi Agenda Viu Map (Sesión 83) —
           reutiliza el mismo marco de "¿Cómo vas?" (.selector-modo-ir,
           .fondo-ficha-mar), con su propio botón de cerrar como el
           resto de las fichas. Se abre al tocar cualquier casilla
           verde (todavía no pasó, no es la próxima) de la grilla de
           horarios de RENFE. ── */
        .tarjeta-agendar-tren { position: relative; padding-top: 30px; }
        .icono-agendar-tren { margin-bottom: 10px; }
        .icono-agendar-tren img { width: 56px; height: 56px; object-fit: contain; }
        .detalle-agendar-tren {
            font-family: 'Inter', sans-serif; font-weight: 800; font-size: var(--vm-fs-cuerpo-destacado);
            color: #14204A; margin-bottom: var(--vm-esp-seccion);
        }
        /* Mismo tono verde que los números de las casillas todavía por
           pasar en la grilla de horarios (a pedido del cliente) — así
           la persona reconoce que este botón "viene" de ese verde. */
        .btn-confirmar-agendar-tren {
            width: 100%; padding: 12px; border: none; border-radius: 10px;
            background: #1b5e20; color: white; font-weight: 800; font-size: var(--vm-fs-cuerpo);
            font-family: 'Inter', sans-serif; cursor: pointer; transition: background 0.15s;
        }
        .btn-confirmar-agendar-tren:hover, .btn-confirmar-agendar-tren:active { background: #164c1a; }
        .btn-confirmar-agendar-tren.agendado { background: #6b6b6b; cursor: default; }
        /* Colores propios por modo (a pedido del cliente) — los mismos
           que ya usan el ícono de "Ir" en las listas y la línea de la
           ruta trazada en el mapa: dorado para caminar (además, caminar
           suma Viu Coins), el verde real del ícono de vehículo
           (#329212, no un verde genérico) para ir en coche. Así el
           color ya anticipa, antes de elegir, cómo se va a ver la ruta. */
        #btnModoCaminando { border-color: var(--vm-dorado); background: white; }
        #btnModoCaminando:hover, #btnModoCaminando:active { background: var(--vm-dorado-suave); border-color: var(--vm-dorado); }
        #btnModoVehiculo { border-color: #329212; background: white; }
        #btnModoVehiculo:hover, #btnModoVehiculo:active { background: #e6f2df; border-color: #329212; }

        /* ── Etapa 52 — bus en tiempo real (NexIABus, por el relevo).
           Mismo tamaño que el bus de antes. Alrededor, un aro del color
           de la ruta con una flecha que gira según el rumbo; el dibujo
           del bus no gira. El giro (0,6 s) y el color los cambia
           map_tmp.js directamente sobre el aro, sin recrear el icono.
           🎨 Diseño provisional: revisión anotada para el final. ── */
        .bus-viu {
            --color-ruta: #1a4b8c;
            position: relative; width: 22px; height: 22px;
        }
        .bus-viu-aro {
            position: absolute; inset: 0; border-radius: 50%;
            background: white; border: 2px solid var(--color-ruta);
            box-shadow: 0 2px 6px rgba(0,0,0,0.35);
            transition: transform 0.6s ease-out;
        }
        .bus-viu-flecha {
            position: absolute; top: -7px; left: 50%; margin-left: -4px;
            width: 0; height: 0;
            border-left: 4px solid transparent; border-right: 4px solid transparent;
            border-bottom: 6px solid var(--color-ruta);
        }
        /* Etapa 53: la cara ocupa todo el círculo interior del aro (el aro
           tiene 2 px de borde) con el diseño de Jaime. */
        .bus-viu-cara {
            position: absolute; inset: 2px; border-radius: 50%; overflow: hidden;
            display: flex; align-items: center; justify-content: center;
            font-size: var(--vm-fs-micro); line-height: 1; pointer-events: none;
        }
        .bus-viu-cara img { width: 14px; height: 14px; object-fit: contain; }
        .bus-viu-cara img.bus-viu-img { width: 100%; height: 100%; object-fit: cover; display: block; }

        /* ── Etapa 52 — paradas de bus. Sin activar: claras. Activadas
           (el ciudadano la tocó): oscuras, con su reloj al lado.
           🎨 Provisional hasta el diseño definitivo de Jaime. ── */
        /* Etapa 52b: el borde es el fondo exterior (color de la ruta, o
           varios colores repartidos con conic-gradient, que pone
           map_tmp.js), y el interior blanco (oscuro si está activada). */
        .parada-bus-viu {
            box-sizing: border-box; width: 20px; height: 20px; border-radius: 6px; padding: 2px;
            box-shadow: 0 1px 4px rgba(0,0,0,0.35); cursor: pointer;
        }
        .parada-bus-viu-dentro {
            width: 100%; height: 100%; border-radius: 4px; background: white;
            display: flex; align-items: center; justify-content: center;
            font-size: 12px; line-height: 1;
        }
        .parada-bus-viu.activa .parada-bus-viu-dentro { background: #0a1a7a; }
        /* Etapa 53: el interior es el diseño de Jaime (natural o marcada). */
        .parada-bus-viu-dentro { overflow: hidden; }
        .parada-bus-viu-dentro img { width: 100%; height: 100%; display: block; pointer-events: none; }
        .parada-bus-viu.tam-media { width: 15px; height: 15px; border-radius: 5px; }
        .parada-bus-viu.tam-media .parada-bus-viu-dentro { font-size: 9px; border-radius: 3px; }
        .parada-bus-viu.tam-pequena { width: 10px; height: 10px; border-radius: 3px; padding: 2px; }
        .parada-bus-viu.tam-pequena .parada-bus-viu-dentro { border-radius: 1px; }
        .reloj-parada-bus {
            background: rgba(27,27,27,0.92); color: white;
            border-radius: 8px; padding: 4px 8px;
            font-size: 12px; line-height: 1.35; white-space: nowrap;
            box-shadow: 0 2px 6px rgba(0,0,0,0.35); pointer-events: none;
        }
        .reloj-parada-bus:empty { display: none; }
        .reloj-parada-nombre { font-size: 11px; opacity: 0.8; }
        .reloj-parada-linea { display: flex; align-items: center; gap: 5px; }
        .reloj-parada-punto { width: 8px; height: 8px; border-radius: 50%; display: inline-block; flex: 0 0 auto; border: 1px solid rgba(255,255,255,0.7); }
        .reloj-parada-retraso { color: #ffb74d; font-size: 11px; }
        /* Etapa 52b: "llegando" en verde brillante; "En parada" en el verde de WhatsApp. */
        .reloj-parada-llegando { color: #76ff03; }
        .reloj-parada-en-parada { color: #25d366; }
        .aviso-buses { font-size: 12px; color: #8a6d3b; padding: 4px 2px 0; max-width: 220px; }

        /* ── Reloj de tren C1 (Renfe Cercanías Valencia) — aviso
           automático. Ya no es un marcador del mapa: es un elemento fijo
           de pantalla, anclado a la misma esquina que "centrar en mi
           ubicación" y el botón de farmacia de guardia (Sesión 22).
           Reubicado en la Sesión 46, continuación: quedaba exactamente
           en la misma posición que el botón "Destacado" de favoritos
           (ambos en top:158px, de cuando este contenedor se armó antes
           de que existieran los 3 botones de etiqueta) — Jaime lo
           reportó tapado bajo esos botones. Ahora va DEBAJO de los 3,
           en especial del último ("Por Visitar", top:246px, alto 34px):
           246+34+10=290. Si los 2 relojes están activos a la vez, el
           contenedor los apila con flexbox y un espacio de 40px entre
           uno y otro — nunca se solapan entre sí, sin necesidad de
           calcular posiciones a mano.
           Nota menor, no resuelta a propósito: el panel de favoritos
           (.panel-favoritos) también arranca cerca de acá (top:286px)
           — como ese panel solo se ve mientras alguien lo tiene
           abierto activamente (no todo el tiempo, a diferencia de los
           botones), la chance de que se solape con una tarjeta de tren
           visible al mismo tiempo es rara — no se resolvió en esta
           pasada, solo la superposición real y constante con los
           botones. ── */
        .contenedor-relojes-c1 {
            /* Vuelve a continuar justo después de la columna de íconos
               (280 = fin de #btnPorVisitar, +10 de aire) — el botón de
               incidencias, que antes obligaba a dejarle 340px de
               margen, se mudó a la esquina inferior derecha (Sesión
               66), así que ya no hace falta ese espacio de más acá; de
               hecho conviene MENOS espacio reservado arriba, no más —
               le deja más aire libre antes de llegar al logo de Viu Map
               en la esquina inferior. */
            position: absolute; left: 10px; top: 290px; z-index: 850;
            display: flex; flex-direction: column; gap: 40px;
            pointer-events: none;
        }
        .reloj-c1-tarjeta {
            display: none; background: white; border-radius: 12px;
            box-shadow: 0 3px 14px rgba(0,0,0,0.28); border: 2px solid #ffb300;
            padding: 12px 26px 12px 16px; font-size: var(--vm-fs-cuerpo); line-height: var(--vm-lh-cuerpo); max-width: 210px;
            position: relative;
        }
        /* Animación de aparición (a pedido del cliente) — sutil a
           propósito: un ligero desvanecido + deslizamiento desde el
           borde izquierdo del mapa (de donde "viene" conceptualmente,
           ya que la columna vive pegada a ese borde), no algo llamativo
           que distraiga de golpe. */
        @keyframes vmApareceRelojC1 {
            from { opacity: 0; transform: translateX(-10px); }
            to { opacity: 1; transform: translateX(0); }
        }
        .reloj-c1-tarjeta.visible { display: block; pointer-events: auto; animation: vmApareceRelojC1 0.35s ease; }
        /* Botón de cerrar (a pedido del cliente) — antes la tarjeta no
           tenía forma de cerrarse a mano, solo desaparecía sola cuando
           el tren llegaba. Proporcionado al tamaño real de la tarjeta
           (chica), no un botón grande genérico. */
        .btn-cerrar-reloj-c1 {
            position: absolute; top: 4px; right: 4px; width: 18px; height: 18px;
            border: none; background: none; color: #999; font-size: 13px; line-height: 1;
            cursor: pointer; border-radius: 50%; display: flex; align-items: center; justify-content: center;
        }
        .btn-cerrar-reloj-c1:hover, .btn-cerrar-reloj-c1:active { background: #f0f0f0; color: #555; }
        .reloj-c1-estado { font-size: var(--vm-fs-caption); color: #555; margin-top: 6px; }
        .reloj-c1-distancia { font-size: var(--vm-fs-titulo); font-weight: 800; color: #1b3a2f; margin-top: 2px; }
        .reloj-c1-fuente { font-size: var(--vm-fs-caption); color: #888; margin-top: var(--vm-esp-pegado); }
        /* Letras más chicas en el celular (pantallas retina) — a pedido
           del cliente, el cartel se sentía grande ahí comparado con la
           computadora. Mismo criterio ya usado para el zoom (L.Browser.
           retina), aplicado acá como clase en <body> porque el CSS no
           puede leer esa variable de JS directo. */
        body.pantalla-retina .reloj-c1-tarjeta { padding: 9px 22px 9px 12px; font-size: var(--vm-fs-caption); max-width: 165px; }
        body.pantalla-retina .reloj-c1-estado { font-size: var(--vm-fs-micro); }
        body.pantalla-retina .reloj-c1-distancia { font-size: var(--vm-fs-subtitulo); }
        body.pantalla-retina .reloj-c1-fuente { font-size: var(--vm-fs-micro); }
        /* Créditos del mapa (pie, Leaflet/IGN/Renfe/Aemet/MITECO) — en
           escritorio se ven bien con el tamaño por defecto de Leaflet;
           en celular quedaban grandes, a pedido del cliente. */
        /* Créditos en 2 líneas (Sesión 28) — alineados a la derecha a
           propósito, ya que la primera línea ("Desarrollado por...") es
           más corta que la segunda y sin esto quedaría pegada a la
           izquierda en vez de prolija contra el borde. */
        /* Créditos en 1 sola línea en escritorio — hay ancho de sobra
           (Sesión 60, a pedido del cliente). El corte a 2 líneas en
           celular ya no es un <br> fijo: es .creditos-linea2 pasando a
           su propia línea solo por debajo del breakpoint de "es
           celular" que ya usa el resto del mapa (720px), con el
           separador " | " oculto ahí (no hace falta si van en líneas
           distintas). Ver actualizarCreditosMapa() más abajo. */
        .leaflet-control-attribution { text-align: right; }
        body.pantalla-retina .leaflet-control-attribution { font-size: 8.5px; line-height: 1.3; padding: 1px 5px; }
        @media (max-width: 720px) {
            .leaflet-control-attribution .creditos-linea2 { display: block; }
            .leaflet-control-attribution .creditos-separador { display: none; }
            .leaflet-control-attribution .creditos-legal { display: block; }   /* Etapa 93a: su propia línea en el móvil */
        }

        /* ══════════════════════════════════════════════════════════
           Modal "Mis Viu Coins" (22 de septiembre de 2026). Todo con
           prefijo .vc- para no chocar con nada. Usa el mismo marco que
           el resto de fichas (.fondo-ficha / .ficha-negocio) y los
           tokens de la paleta Viu Map. Lo pinta pintarModalViuCoins().
           ══════════════════════════════════════════════════════════ */

        .ficha-viu-coins { padding: 0; max-width: 400px; overflow: hidden auto; }
        .vc-cabecera { position: relative; text-align: center; padding: 26px 22px 20px;
        background: radial-gradient(120% 90% at 50% 0%, #FFF6DA 0%, var(--vm-dorado-suave) 45%, #F5DC96 100%);
        border-bottom: 1px solid #EFD48A; }
        .vc-moneda { width: 64px; height: 64px; border-radius: 50%; display: block; margin: 0 auto 10px; object-fit: cover;
        box-shadow: 0 4px 14px rgba(215,145,9,.35); }
        /* El título "Mis Viu Coins" se retiró el 22 de septiembre de 2026:
           la moneda ya dice de qué va, y el saldo pasa a "Tienes N Viu Coins". */
        .vc-saldo { display: flex; align-items: baseline; justify-content: center; gap: 7px; margin-top: 6px; }
        .vc-saldo-pre { font-size: var(--vm-fs-cuerpo-destacado); font-weight: 700; color: #9A6700; }
        .vc-saldo-num { font-size: var(--vm-fs-hero-grande); font-weight: 800; color: #9A6700; line-height: 1; }
        .vc-saldo-uni { font-size: var(--vm-fs-cuerpo-destacado); font-weight: 700; color: #9A6700; }
        .vc-subtitulo { font-size: var(--vm-fs-cuerpo); color: #7A5A12; margin-top: 6px; line-height: var(--vm-lh-cuerpo); }
        .vc-cuerpo { padding: 16px 20px 20px; }
        .vc-aviso { display: flex; gap: 10px; align-items: flex-start; background: var(--vm-azul-suave);
        color: var(--vm-azul-oscuro); border-radius: var(--vm-radio-boton); padding: 10px 12px;
        font-size: var(--vm-fs-cuerpo); line-height: var(--vm-lh-cuerpo); margin-bottom: var(--vm-esp-accion); }
        .vc-grupo { font-size: var(--vm-fs-caption); font-weight: 800; letter-spacing: .12em; text-transform: uppercase;
        color: var(--vm-dorado); margin: var(--vm-esp-accion) 0 4px; }
        .vc-fila { display: flex; align-items: center; gap: 9px; padding: 9px 0; border-bottom: 1px solid var(--vm-borde); }
        .vc-fila:has(+ .vc-grupo), .vc-fila:last-child { border-bottom: none; }
        .vc-icono { width: 40px; height: 40px; border-radius: 50%; flex-shrink: 0; display: flex; align-items: center;
        justify-content: center; background: #fff; border: 1.5px solid #F1DFAE; overflow: hidden; }
        .vc-icono img { width: 28px; height: 28px; object-fit: contain; display: block; }
        .vc-icono.vc-foto img { width: 100%; height: 100%; object-fit: cover; }
        /* La cruz de farmacia y el surtidor vienen recortados al borde de su
           propia imagen: al mismo tamaño que el resto quedan apretados contra
           el círculo, así que se les deja aire. */
        .vc-icono.vc-grande img { width: 28px; height: 28px; }
        .vc-icono.vc-ajustado img { width: 25px; height: 25px; }
        /* el peatón de "caminar" es apaisado: se deja algo más ancho y con
           alto automático, igual que en la vista previa aprobada. */
        .vc-icono.vc-ancho img { width: 34px; height: auto; }
        .vc-icono.vc-ok { background: #e8f5e9; border-color: #c8e6c9; font-size: 19px; }
        .vc-icono.vc-falta { border: 1.5px dashed #C9A441; background: #FFFBEF; }
        .vc-icono.vc-falta span { font-size: 7.5px; line-height: 1.1; text-align: center; color: #9A6700; font-weight: 700; padding: 2px; word-break: break-all; }
        .vc-texto { flex: 1; min-width: 0; }
        .vc-accion { font-size: var(--vm-fs-cuerpo-destacado); font-weight: 600; color: var(--vm-texto); }
        .vc-detalle { font-size: var(--vm-fs-caption); color: var(--vm-texto-suave); margin-top: var(--vm-esp-pegado); line-height: var(--vm-lh-cuerpo); }
        /* 🔑 Dos valores por fila, SIEMPRE a la misma altura: rejilla de dos
           columnas y dos filas, con los botones arriba y la frecuencia
           ("al día" / "por km") debajo del blanco. Antes la frecuencia
           empujaba el bloque y las filas no casaban entre sí. Para quien no
           está en el plan solo se pinta la columna blanca. */
        /* El límite diario, dicho en la propia actividad: lo que pasa del
           tope no se guarda para mañana, se pierde, y el usuario tiene que
           saberlo antes (decisión de Jaime, 22 de septiembre de 2026). */
        .vc-limite {
            font-size: var(--vm-fs-micro); font-weight: 700; color: var(--vm-dorado);
            margin-top: 3px; letter-spacing: .01em;
        }
        .vc-valores {
            flex-shrink: 0; display: grid; grid-template-columns: 46px 46px;
            grid-template-rows: auto auto; gap: 3px 9px; align-items: center; justify-items: center;
        }
        .vc-valores.vc-valores-solos { grid-template-columns: 46px; }
        /* 🔑 La etiqueta de frecuencia se centra sobre el botón blanco, pero
           "por evento" mide justo los 46 px de la columna y quedaba apretada
           contra ella, leyéndose desalineada respecto a "al día". Se le deja
           sobresalir hasta 70 px: el hueco entre columnas es de 9 px por lado,
           así que nunca llega a tocar el botón dorado. */
        .vc-valores .vc-por {
            grid-column: 1; margin: 0; width: max-content; justify-self: center;
            /* 🔑 64 px es el límite exacto para no invadir el botón dorado: la
               columna mide 46 y el hueco entre columnas, 9 por lado. A tamaño
               normal "por evento" cabe de sobra en una línea; solo con la
               letra muy ampliada parte en dos, centradas — que se lea en dos
               líneas es mejor que cortar una palabra por la mitad. */
            max-width: 64px; text-align: center;
        }
        .vc-chip { display: inline-block; min-width: 46px; text-align: center;
        font-weight: 800; font-size: var(--vm-fs-cuerpo-destacado); border-radius: 999px; padding: 5px 8px; }
        /* Lo que RECIBES por la actividad: blanco, borde dorado más grueso que
           el de los iconos (1,5 px) y letra dorada. */
        .vc-chip-valor { background: #fff; color: var(--vm-dorado); border: 2.5px solid var(--vm-dorado); }
        /* Lo que LLEVAS ganado: dorado macizo, letra blanca, sin borde. */
        .vc-chip-ganado { background: var(--vm-dorado); color: #fff; border: 2.5px solid transparent; }
        .vc-cabecera-columnas {
            display: flex; gap: 9px; align-items: flex-end; justify-content: flex-end;
            font-size: var(--vm-fs-micro); font-weight: 700; color: var(--vm-texto-suave);
            text-transform: uppercase; letter-spacing: .04em; padding-bottom: 4px;
        }
        .vc-cabecera-columnas span { width: 46px; text-align: center; line-height: 1.15; }
        /* "Llevas hoy" ocupa dos líneas: se centra con "Recibes" por su
           mitad, no por su base, para que las dos cabeceras queden a la
           misma altura (pedido de Jaime, 22 de septiembre de 2026). */
        .vc-cabecera-columnas { align-items: center; padding-bottom: 0; }
        .vc-col-dos-lineas { align-self: center; }
        /* El título de la sección y las cabeceras de columna comparten línea
           en la PRIMERA sección, cada uno con lo suyo: el título dorado a la
           izquierda, las cabeceras grises a la derecha. */
        .vc-grupo-linea.con-columnas {
            display: flex; align-items: center; justify-content: space-between; gap: 10px;
            margin: var(--vm-esp-accion) 0 4px;
        }
        /* El margen lo lleva la fila, no el título: si lo llevara el título,
           su caja quedaría descentrada respecto a las cabeceras y las dos
           cosas no estarían a la misma altura (se medían 5 px de desfase). */
        .vc-grupo-linea.con-columnas .vc-grupo { margin: 0; }
        /* Botón de canje: ocupa el ancho del aviso azul al que sustituye,
           dorado macizo con letras blancas como los contadores, y la misma
           estela que los botones de la ficha de negocio (vm-brillo-boton-cta:
           Reservar, Cita Previa, Donar a la Falla, Comprar entradas). */
        .vc-btn-canjear {
            width: 100%; margin-bottom: var(--vm-esp-accion); padding: 13px;
            border: none; border-radius: var(--vm-radio-boton);
            background: var(--vm-dorado); color: #fff; cursor: pointer;
            font: 800 var(--vm-fs-cuerpo-destacado) 'Inter', sans-serif;
            text-transform: uppercase; letter-spacing: .06em;
            position: relative; overflow: hidden; isolation: isolate;
            box-shadow: 0 2px 10px rgba(215,145,9,.35);
        }
        .vc-btn-canjear::before {
            content: ''; position: absolute; top: 0; left: -60%;
            width: 45%; height: 100%; z-index: -1; pointer-events: none;
            background: linear-gradient(115deg, transparent 0%, rgba(255,255,255,0.45) 45%, rgba(255,255,255,0.45) 55%, transparent 100%);
            animation: vm-brillo-boton-cta 3.6s ease-in-out infinite;
        }
        .vc-btn-canjear:hover { background: #c07f08; }
        /* "Próximamente…" al pulsar el canje: aparece y se va solo, sin sacar
           al usuario del modal. El canje de verdad (ruleta y premios) es
           trabajo posterior. */
        .vc-proximamente {
            max-height: 0; opacity: 0; overflow: hidden; text-align: center;
            background: var(--vm-dorado-suave); color: #7A5A12; border-radius: var(--vm-radio-boton);
            font-size: var(--vm-fs-cuerpo-destacado); font-weight: 700; padding: 0 12px;
            transition: max-height .25s ease, opacity .25s ease, padding .25s ease;
        }
        .vc-proximamente.visible { max-height: 60px; opacity: 1; padding: 10px 12px; margin-bottom: var(--vm-esp-accion); }
        /* ── Pop-up de fechas especiales (cumpleaños y mayoría de edad) ── */
        .ficha-aviso-viucoins {
            max-width: 340px; text-align: center; padding: 26px 22px 20px;
            background: linear-gradient(180deg, var(--vm-dorado-suave) 0%, #fff 55%);
        }
        .vc-aviso-moneda { width: 110px; height: 110px; object-fit: contain; margin-bottom: 10px; }
        /* Etapa 33: la meta del aviso "¡Has llegado a tu destino!" (imagen de Jaime, 320 × 342) */
        .vc-aviso-llegada { width: 150px; height: 160px; }
        .vc-aviso-texto + .vc-aviso-texto { margin-top: 8px; }
        .vc-aviso-texto b { font-weight: 800; color: #8a5a00; }
        .vc-aviso-titulo { font-size: var(--vm-fs-titulo); font-weight: 800; color: #8a5a00; margin-bottom: 8px; }
        .vc-aviso-texto {
            font-size: var(--vm-fs-cuerpo); line-height: var(--vm-lh-cuerpo);
            color: var(--vm-texto); margin-bottom: var(--vm-esp-accion);
        }
        .vc-aviso-cerrar {
            display: block; width: 100%; margin-top: 8px; padding: 10px; border: none; background: none;
            color: var(--vm-texto-suave); font: 600 var(--vm-fs-cuerpo) 'Inter', sans-serif; cursor: pointer;
        }

        /* ── Motor de Viu Coins: cabecera con lo de hoy, pestañas e histórico ── */
        .vc-saldo-hoy {
            font-size: var(--vm-fs-caption); font-weight: 800; color: #9A6700;
            background: rgba(255,255,255,.55); border-radius: 999px; padding: 2px 8px; align-self: center;
        }
        .vc-pestanas { margin-bottom: var(--vm-esp-accion); }
        .vc-historico-cabecera {
            display: flex; align-items: center; gap: 10px; padding: 6px 0 10px;
            border-bottom: 1px solid var(--vm-borde); margin-bottom: 6px;
        }
        .vc-volver { border: none; background: #f2f2f2; border-radius: 8px; width: 28px; height: 28px; font-size: 17px; color: #555; cursor: pointer; line-height: 1; }
        .vc-historico-titulo { flex: 1; font-weight: 800; font-size: var(--vm-fs-cuerpo-destacado); color: var(--vm-texto); }
        .vc-historico-total { font-weight: 800; color: #fff; background: var(--vm-dorado); border-radius: 999px; padding: 4px 10px; font-size: var(--vm-fs-cuerpo-destacado); }
        .vc-hist-fila {
            display: flex; justify-content: space-between; align-items: center; padding: 11px 2px;
            border-bottom: 1px solid var(--vm-borde); font-size: var(--vm-fs-cuerpo-destacado); color: var(--vm-texto);
        }
        .vc-hist-fila:last-child { border-bottom: none; }
        .vc-hist-fila.pulsable { cursor: pointer; }
        .vc-hist-fila.pulsable:hover { background: #fdf8ec; }
        .vc-hist-num { font-weight: 800; color: var(--vm-dorado); }
        .vc-hist-flecha { color: #c9a441; font-weight: 700; margin-left: 8px; }
        .vc-historico-pie { font-size: var(--vm-fs-micro); color: var(--vm-texto-suave); text-align: center; padding: 10px 0 2px; }
        .vc-historico-vacio { text-align: center; color: var(--vm-texto-suave); font-size: var(--vm-fs-cuerpo); line-height: var(--vm-lh-cuerpo); padding: 26px 10px; }
        .vc-cabecera-columnas { align-items: center; }

        /* Aviso para quien apagó el registro de movilidad: sin él, caminar y
           completar recorridos no pueden contarse. Solo lo ve quien lo apagó
           a propósito, porque viene activado desde el alta. */
        .vc-aviso-movilidad {
            background: var(--vm-azul-suave); color: var(--vm-azul-oscuro);
            border-radius: var(--vm-radio-boton); padding: 12px 14px; margin-bottom: var(--vm-esp-accion);
            font-size: var(--vm-fs-cuerpo); line-height: var(--vm-lh-cuerpo); text-align: center;
        }
        .vc-btn-movilidad {
            display: block; width: 100%; margin-top: 10px; padding: 10px; border: none;
            border-radius: var(--vm-radio-boton); background: var(--vm-azul); color: #fff;
            font: 700 var(--vm-fs-cuerpo-destacado) 'Inter', sans-serif; cursor: pointer;
        }
        .vc-btn-movilidad:hover { background: var(--vm-azul-oscuro); }
        /* Para quien no está en el plan, en el lugar del canje. */
        .vc-solo-registrados {
            background: var(--vm-dorado-suave); border: 1px solid #EFD48A; border-radius: var(--vm-radio-boton);
            padding: 12px 14px; margin-bottom: var(--vm-esp-accion);
            font-size: var(--vm-fs-cuerpo); line-height: var(--vm-lh-cuerpo); color: #7A5A12; text-align: center;
        }
        .vc-por { display: block; font-size: var(--vm-fs-micro); color: var(--vm-texto-suave); margin-top: 3px; font-weight: 600; }
        .vc-reglas { margin-top: var(--vm-esp-accion); background: #FAFAFB; border: 1px solid var(--vm-borde);
        border-radius: var(--vm-radio-boton); padding: 10px 12px; }
        .vc-reglas summary { cursor: pointer; font-size: var(--vm-fs-cuerpo); font-weight: 700; color: var(--vm-azul); }
        .vc-reglas ul { margin: 8px 0 0 16px; }
        .vc-reglas li { font-size: var(--vm-fs-caption); color: var(--vm-texto-suave); line-height: var(--vm-lh-cuerpo); margin-bottom: 5px; }
        .vc-btn { width: 100%; margin-top: var(--vm-esp-accion); padding: 13px; border: none; border-radius: var(--vm-radio-boton);
        background: var(--vm-azul); color: #fff; font: 700 var(--vm-fs-cuerpo-destacado) 'Inter', sans-serif; cursor: pointer; }
        .vc-btn:hover { background: var(--vm-azul-oscuro); }

        /* ── Fecha de nacimiento ya registrada y pantalla "Mis Viu Coins"
              del menú de perfil (22 de septiembre de 2026) ── */
        .perfil-dato-bloqueado {
            padding: 9px 12px; background: #f4f5f7; border-radius: 8px;
            color: #555; font-size: var(--vm-fs-cuerpo-destacado);
        }
        .vc-perfil-aviso {
            background: var(--vm-dorado-suave); border: 1px solid #EFD48A;
            border-radius: var(--vm-radio-boton); padding: 14px 16px; margin-bottom: var(--vm-esp-accion);
        }
        .vc-perfil-aviso-titulo { font-weight: 800; color: #7A5A12; font-size: var(--vm-fs-cuerpo-destacado); margin-bottom: 4px; }
        .vc-perfil-aviso-texto { color: #7A5A12; font-size: var(--vm-fs-cuerpo); line-height: var(--vm-lh-cuerpo); }
        .vc-perfil-saldo {
            display: flex; align-items: center; gap: 14px; padding: 16px;
            background: var(--vm-dorado-suave); border: 1px solid #EFD48A;
            border-radius: var(--vm-radio-boton); margin-bottom: var(--vm-esp-accion);
        }
        .vc-perfil-saldo img { width: 52px; height: 52px; border-radius: 50%; object-fit: cover; }
        .vc-perfil-saldo-num { font-size: var(--vm-fs-hero-grande); font-weight: 800; color: #9A6700; line-height: 1; }
        .vc-perfil-saldo-txt { font-size: var(--vm-fs-cuerpo); color: #7A5A12; margin-top: 2px; }

        /* ══════════════════════════════════════════════════════════════
           v57 — ESTILO OSCURO PARA MÓVILES Y TABLETS (26 de septiembre de
           2026, decisión de Jaime tras probarlo en los dos aparatos)
           ══════════════════════════════════════════════════════════════
           El azul oscuro translúcido se ve bien en la pantalla nítida de
           un móvil, pero no en la de un ordenador. Por eso:
             · ORDENADOR: todo como antes de la v54 (botones blancos).
             · MÓVIL Y TABLET: el nav y los botones del mapa con el mismo
               fondo oscuro translúcido; lo que era negro pasa a blanco.
           Se distingue por el TIPO DE PANTALLA, no por el ancho: "sin
           ratón y con dedo" (hover: none + pointer: coarse) es un móvil o
           una tablet, sea del tamaño que sea; un ordenador con ratón nunca
           entra aquí aunque se estreche la ventana.
           NO se tocan, a propósito (pedido de Jaime): el botón del
           usuario, la moneda de puntos, el botón de incidencias y el del
           idioma. Tampoco los paneles que se despliegan (siguen blancos).
           Este bloque va al FINAL del archivo para ganar a las reglas
           normales de más arriba con la misma especificidad. */
        @media (hover: none) and (pointer: coarse) {
            /* Nav: solo el fondo y el icono del menú. */
            nav { background: var(--mando-oscuro-fondo); }
            .btn-hamburguesa { color: #fff; }
            .btn-hamburguesa:hover, .btn-hamburguesa:active { background: rgba(255,255,255,0.15); }

            /* Botones del mapa: mismo fondo, sin borde, CADA UNO conserva
               su propia sombra de siempre. */
            .btn-clima-flotante, .btn-renfe-flotante,
            .btn-mi-ubicacion, .btn-farmacia-flotante, .btn-etiqueta, .btn-buscador-mapa {
                background: var(--mando-oscuro-fondo); border-color: transparent;
            }
            .btn-clima-flotante:hover, .btn-clima-flotante:active,
            .btn-renfe-flotante:hover, .btn-renfe-flotante:active,
            .btn-mi-ubicacion:hover, .btn-mi-ubicacion:active,
            .btn-etiqueta:hover, .btn-etiqueta:active,
            .btn-buscador-mapa:hover, .btn-buscador-mapa:active {
                background: var(--mando-oscuro-fondo-activo);
            }

            /* Clima: la temperatura conserva su color, SIN borde (v58, a
               pedido de Jaime: el blanco de la v57 sobraba). */
            .temp-clima-nav { text-shadow: none; }
            /* Renfe: textos oscuros → blancos. */
            .renfe-etiqueta-destino { color: rgba(255,255,255,0.8); }
            .renfe-flecha, .renfe-hora { color: #fff; }

            /* Favoritos (⭐ ❤️ 🎯): la versión de sus iconos SIN el disco blanco
               (v58). Llegan por un <picture> que arma actualizarIconosEstaticos()
               en el JS con esta misma condición de pantalla. */
            .btn-etiqueta picture { display: block; width: 100%; height: 100%; }

            /* Aro dorado de la foto de perfil del nav, más fino (v58): 2 px →
               1,5 px. !important porque el grosor lo pone pintarAvatarEn() en
               línea; solo afecta al botón del nav, no al avatar grande del
               panel de la cuenta. */
            #btnPerfilUsuario { border-width: 1.5px !important; }
            /* v59: perfil y moneda, del MISMO tamaño y a la MISMA altura. El
               botón del perfil es "inline-block" (lo pone pintarAvatarEn() en
               línea) y eso le dejaba debajo el hueco de una línea de texto:
               quedaba 2 px más alto que la moneda. Como bloque, los dos
               círculos miden 34 px y quedan centrados en el nav. */
            #btnPerfilUsuario { display: block !important; }
            /* v59: los puntos de Viu Coins del nav, en dorado claro. */
            .contador-puntos-nav { color: #f5c32e; }

            /* v60: columna izquierda alineada con el resto.
               · El perfil (y con él la moneda y los puntos, que lo siguen
                 con la misma separación) se corre a la izquierda hasta
                 quedar centrado con ubicación, farmacia y favoritos: el
                 nav pasa de 24 a 10 px de margen izquierdo, el mismo
                 left: 10px de esa columna. El lado derecho no cambia.
               · Ubicación sube 4 px (70 → 66) para quedar a la altura del
                 botón de trenes, el del clima y el mando de zoom (todos
                 en top 66, lo fija alinearPilaSuperiorDerecha() en el JS).
                 Farmacia, favoritos y las tarjetas del tren suben lo mismo,
                 así que las distancias entre ellos no cambian. */
            nav { padding-left: 10px; }
            .btn-mi-ubicacion { top: 66px; }
            .btn-farmacia-flotante { top: 110px; }
            #btnDestacados { top: 154px; }
            #btnFavoritoCorazon { top: 198px; }
            #btnPorVisitar { top: 242px; }
            .contenedor-relojes-c1 { top: 286px; }

            /* v61: SOMBRA PARALELA A 45° (pedido de Jaime, 26 de septiembre de
               2026) para TODO lo que vive sobre el fondo oscuro translúcido:
               textos, iconos e imágenes del nav y de los botones del mapa.
               Dirección, distancia y difuminado: ver la v62 justo debajo.
               Un solo valor para todos: si hay que
               ajustarlo, se cambia en --sombra-45 y en ningún otro sitio.
               · Textos → text-shadow. Iconos e imágenes → filter:
                 drop-shadow(), que sigue la forma real del dibujo (la cruz,
                 la estrella...) y no la caja cuadrada.
               · Perfil, moneda e idioma ya tenían una sombra propia (de caja):
                 se sustituye por esta para que no se sumen dos sombras. */
            /* v62: la sombra pasa a la IZQUIERDA (abajo a la izquierda, 45°)
               y más recogida: 0,8 px de distancia en cada eje y 0,6 px de
               difuminado (antes 1 px / 1,5 px hacia la derecha), para que
               marque el contorno sin extenderse. Mismo 35 % de negro. */
            :root { --sombra-45: -0.8px 0.8px 0.6px rgba(0, 0, 0, 0.35); }

            /* Nav */
            .btn-idioma-actual, #btnPerfilUsuario, #btnPuntosViu img { box-shadow: none; filter: drop-shadow(var(--sombra-45)); }
            .contador-puntos-nav { text-shadow: var(--sombra-45); }
            .btn-hamburguesa svg { filter: drop-shadow(var(--sombra-45)); }

            /* Mando de zoom: + y −, el % y el icono de capas (que suma la
               sombra a su borde blanco fino). */
            .btn-zoom-mapa, .indicador-zoom { text-shadow: var(--sombra-45); }
            .btn-capas svg {
                filter: drop-shadow(0.7px 0 0 #fff) drop-shadow(-0.7px 0 0 #fff)
                        drop-shadow(0 0.7px 0 #fff) drop-shadow(0 -0.7px 0 #fff)
                        drop-shadow(var(--sombra-45));
            }

            /* Buscador, ubicación, farmacia y favoritos */
            .btn-buscador-mapa svg, .btn-mi-ubicacion svg,
            .btn-farmacia-flotante img, .btn-etiqueta img { filter: drop-shadow(var(--sombra-45)); }

            /* Tren: icono de Renfe, VALENCIA/GANDÍA, flechas y horas */
            .icono-renfe-nav { filter: drop-shadow(var(--sombra-45)); }
            .renfe-etiqueta-destino, .renfe-flecha, .renfe-hora { text-shadow: var(--sombra-45); }

            /* Clima: temperatura e icono del estado del cielo */
            .temp-clima-nav { text-shadow: var(--sombra-45); }
            .icono-clima-nav { filter: drop-shadow(var(--sombra-45)); }

            /* Mi ubicación: gris claro mientras no hay GPS, blanco cuando sí. */
            .btn-mi-ubicacion { color: rgba(255,255,255,0.55); }
            .btn-mi-ubicacion.disponible { color: #fff; }
            /* Buscador (solo existe en móvil): lupa blanca. */
            .btn-buscador-mapa { color: #fff; }
            /* Farmacia: la cruz sin su fondo blanco (el <picture> del HTML
               sirve farmacia-guardia-sin-fondo.webp con esta misma condición). */
            .btn-farmacia-flotante img { object-fit: contain; padding: 6px; }   /* v59: cruz más pequeña y centrada (antes 3px) */

            /* Mando de zoom/capas: fondo oscuro con sombra corta y definida
               (la difusa de siempre se deshacía bajo el translúcido). */
            .grupo-mando-mapa { background: var(--mando-oscuro-fondo); box-shadow: 0 2px 8px rgba(0,0,0,0.22); }
            .btn-zoom-mapa { color: #fff; }
            .btn-zoom-mapa:disabled { color: var(--vm-borde); }
            .btn-zoom-mapa:hover, .btn-zoom-mapa:active,
            .btn-capas:hover, .btn-capas:active { background: rgba(13, 35, 64, 0.30); }
            .btn-zoom-mapa:disabled:hover { background: transparent; }
            .indicador-zoom { color: #fff; }
            /* v59: mando más estrecho (44 → 38 px) para que ocupe menos en el
               móvil. El + , el − y el icono de capas conservan su tamaño;
               solo el % baja de letra (9 → 8 px) para que "100%" no llegue
               a los bordes. La posición del clima se recalcula sola, porque
               alinearPilaSuperiorDerecha() mide el ancho real del mando. */
            .grupo-mando-mapa, .btn-zoom-mapa, .indicador-zoom, .btn-capas { width: 38px; }
            .indicador-zoom { font-size: 8px; }
            /* (el filtro del icono de capas, con su borde blanco, está en el
               bloque v61 de la sombra a 45°, más arriba) */

            /* v63: la MISMA sombra de los elementos interiores (--sombra-45:
               abajo a la izquierda, 45°, recogida) pasa también a los botones
               y al nav, en lugar de sus sombras anchas y difuminadas de antes,
               que no les daban relieve. Incluye el botón de incidencias (solo
               su sombra; su forma e imagen no cambian). Va al FINAL del bloque
               para ganar a las sombras definidas más arriba (p. ej. la del
               mando de zoom). */
            nav, .btn-clima-flotante, .btn-renfe-flotante, .grupo-mando-mapa,
            .btn-mi-ubicacion, .btn-farmacia-flotante, .btn-etiqueta, .btn-buscador-mapa {
                box-shadow: var(--sombra-45);
            }
            .btn-incidencias-flotante img { filter: drop-shadow(var(--sombra-45)); }

            /* v64: columna izquierda un 15 % más pequeña (34 → 29 px):
               ubicación, farmacia y ⭐ ❤️ 🎯. Lo que NO cambia:
               · el borde izquierdo (left: 10px, el mismo que el perfil, que
                 queda más grande a propósito para destacar);
               · la altura de ubicación (top 66, a la par del tren, el clima
                 y el zoom);
               · los 10 px de separación entre botones — por eso cada uno sube
                 un poco más que el anterior: farmacia 5 px, ⭐ 10, ❤️ 15 y
                 🎯 20; y con ellos el panel de favoritos y las tarjetas del
                 tren, que van 10 px por debajo de 🎯.
               Lo de dentro se reduce en la misma proporción: el icono de
               ubicación (21 → 18 px), el margen de la cruz de farmacia (6 →
               5 px) y los iconos de favoritos (ocupan todo el botón, bajan
               solos). */
            .btn-mi-ubicacion, .btn-farmacia-flotante, .btn-etiqueta { width: 29px; height: 29px; }
            .btn-mi-ubicacion svg { width: 18px; height: 18px; }
            .btn-farmacia-flotante img { padding: 5px; }
            .btn-etiqueta { font-size: calc(var(--vm-fs-subtitulo) * 0.85); }
            .btn-farmacia-flotante { top: 105px; }
            #btnDestacados { top: 144px; }
            #btnFavoritoCorazon { top: 183px; }
            #btnPorVisitar { top: 222px; }
            .contenedor-relojes-c1, .panel-favoritos { top: 261px; }

            /* v65: el buscador y "mi ubicación" intercambian su sitio.
               · Buscador → cabeza de la columna izquierda (left 10, top 66),
                 con su tamaño de siempre (34 px): un poco mayor que el resto
                 de la columna, a propósito.
               · Ubicación → debajo de capas, alineada a la derecha con el
                 mando; la posición exacta la mide el JS
                 (alinearBuscadorConCapas). Conserva sus 29 px.
               · El resto de la columna mantiene los 10 px de separación
                 contando desde el buscador (34 px): farmacia 110, ⭐ 149,
                 ❤️ 188, 🎯 227, y el panel de favoritos y las tarjetas del
                 tren, 10 px por debajo de 🎯. */
            .btn-buscador-mapa { top: 66px; left: 10px; right: auto; }
            .btn-mi-ubicacion { position: fixed; left: auto; }
            .btn-farmacia-flotante { top: 110px; }
            #btnDestacados { top: 149px; }
            #btnFavoritoCorazon { top: 188px; }
            #btnPorVisitar { top: 227px; }
            .contenedor-relojes-c1, .panel-favoritos { top: 266px; }

            /* v66: "mi ubicación" DENTRO del mando de zoom, como última celda,
               debajo de capas (el JS mueve el botón ahí: colocarBotonUbicacion).
               Mismo estilo que el resto del mando: sin fondo propio (usa el del
               mando), sin sombra propia, separador fino arriba y las esquinas
               de abajo redondeadas. Su icono baja de 18 a 16 px para no verse
               mayor que el de capas. Capas pierde sus esquinas redondeadas de
               abajo mientras ubicación esté debajo (si ubicación se oculta por
               estar fuera de Cullera, capas vuelve a cerrar el mando). */
            .grupo-mando-mapa .btn-mi-ubicacion {
                position: static; width: 38px; height: 30px; margin: 0;
                border: none; border-top: 1px solid var(--vm-borde);
                border-radius: 0 0 var(--vm-radio-panel) var(--vm-radio-panel);
                background: transparent; box-shadow: none;
            }
            .grupo-mando-mapa .btn-mi-ubicacion svg { width: 16px; height: 16px; }
            .grupo-mando-mapa .btn-mi-ubicacion:hover,
            .grupo-mando-mapa .btn-mi-ubicacion:active { background: rgba(13, 35, 64, 0.30); }
            .grupo-mando-mapa:has(> .btn-mi-ubicacion:not(.fuera-de-cullera)) .btn-capas { border-radius: 0; }
            /* Etapa 39: el botón de centrar dice si el mapa te está siguiendo:
               azul del GPS (el mismo del halo de tu punto) y relleno; si has
               movido el mapa, blanco y solo contorno, como antes. */
            .btn-mi-ubicacion.disponible.siguiendo { color: #4285f4; }
            .btn-mi-ubicacion.disponible.siguiendo svg circle:last-of-type { fill: currentColor; fill-opacity: 0.3; }
        }
        /* v65 — TABLETS (táctil y 720 px o más): ahí el botón del buscador no
           existe (la búsqueda va en la barra del nav), así que la columna
           izquierda empieza directamente por farmacia, en el top 66, con los
           mismos 10 px entre botones. */
        @media (hover: none) and (pointer: coarse) and (min-width: 720px) {
            .btn-farmacia-flotante { top: 66px; }
            #btnDestacados { top: 105px; }
            #btnFavoritoCorazon { top: 144px; }
            #btnPorVisitar { top: 183px; }
            .contenedor-relojes-c1, .panel-favoritos { top: 222px; }
        }

        /* ══════════════════════════════════════════════════════════════
           v68 (26 de septiembre de 2026) — SOLO MÓVIL Y TABLET. El
           ordenador no cambia (ninguna regla sale de esta media query).
           ══════════════════════════════════════════════════════════════ */
        @media (hover: none) and (pointer: coarse) {
            /* ── Menú hamburguesa y menú del usuario: más juntos y con la
               letra un poco más pequeña (antes 12 px de relleno arriba y
               abajo y 13 px de letra). ── */
            .panel-menu { padding: 5px 0; width: auto; min-width: 170px; max-width: calc(100vw - 20px); }
            .opcion-menu { padding: 8px 15px; font-size: 12px; line-height: 1.3; white-space: nowrap; }
            /* Mis Viu Coins en dorado y Cerrar sesión en rosa, como en el
               ordenador. Allí se ven al pasar el ratón; en una pantalla
               táctil no hay ratón, así que el fondo se queda puesto. Mismos
               tonos exactos, y un poco más intensos al tocarlos. */
            .opcion-menu-dorada { background: rgba(215,145,9,0.12); }
            .opcion-menu-dorada:active { background: rgba(215,145,9,0.22); }
            .opcion-menu-roja { background: rgba(139,0,0,0.09); }
            .opcion-menu-roja:active { background: rgba(139,0,0,0.16); }

            /* ── Ficha: las estrellas justo debajo de la categoría, sin
               hueco. Las estrellas eran una caja "en línea" dentro de un
               bloque con la altura de línea del texto normal, y esa línea
               invisible añadía aire por encima (más o menos según la
               fuente del teléfono). Ahora son una caja propia pegada a la
               categoría. ── */
            #fichaValoracion { margin-top: 2px; }
            #estrellasValoracion { display: flex; width: max-content; line-height: 1; }
            #estrellasValoracion .estrella-val { display: block; line-height: 1; }

            /* ── Comparador de gasolineras (renderComparadorGasolinerasMovil):
               cabecera compacta solo mientras está abierto el comparador
               (el mismo panel lo usan farmacias, que no cambia). ── */
            .fondo-buckets:has(.gas-movil, .gas-cargando) .panel-buckets { padding: 16px 14px 12px; }
            .fondo-buckets:has(.gas-movil, .gas-cargando) .cabecera-buckets { margin-bottom: 10px; align-items: center; }
            .fondo-buckets:has(.gas-movil, .gas-cargando) .ceja-buckets { font-size: 9px; }
            .fondo-buckets:has(.gas-movil, .gas-cargando) .titulo-buckets h2 { font-size: 18px; margin-top: 1px; }
            .fondo-buckets:has(.gas-movil, .gas-cargando) .subtitulo-buckets { font-size: 11px; line-height: 1.35; margin-top: 3px; }
            .fondo-buckets:has(.gas-movil, .gas-cargando) .btn-cerrar-buckets { width: 30px; height: 30px; font-size: 13px; margin-left: 10px; }

            .pastillas-combustible-movil {
                position: sticky; top: 0; z-index: 1;
                display: flex; gap: 6px; overflow-x: auto; scrollbar-width: none;
                padding: 2px 0 10px; background: var(--vm-fondo-calido);
            }
            .pastillas-combustible-movil::-webkit-scrollbar { display: none; }
            .pastilla-combustible-movil {
                flex-shrink: 0; font-family: 'Inter', sans-serif; font-size: 11.5px; font-weight: 600;
                padding: 6px 12px; border-radius: 15px; cursor: pointer; white-space: nowrap;
                border: 1px solid #e0e6e8; background: white; color: #37474f;
            }
            .pastilla-combustible-movil.activa { background: #0d2340; border-color: #0d2340; color: white; }

            .tarjeta-gas-movil {
                background: white; border-radius: 14px; padding: 0 12px;
                box-shadow: 0 2px 8px rgba(14,110,140,0.08);
            }
            .fila-gas-movil {
                display: grid; grid-template-columns: minmax(0, 1fr) 70px 42px;
                column-gap: 8px; align-items: center;
                padding: 9px 0; border-top: 1px solid #f0f4f5;
            }
            .fila-gas-movil:first-child { border-top: none; }
            .info-gas-movil { min-width: 0; }
            .nombre-gas-movil {
                font-family: 'Inter', sans-serif; font-size: 13px; font-weight: 600; color: #263238;
                white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.25;
            }
            .nombre-gas-movil .estrella-mas-barato { margin-right: 3px; }
            .detalle-gas-movil {
                font-family: 'Inter', sans-serif; font-size: 10.5px; color: #8ba3a8; margin-top: 2px;
                white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.25;
            }
            .etiquetas-gas-movil { display: flex; gap: 5px; margin-top: 5px; }
            .etiquetas-gas-movil .btn-accion-resultado.btn-etiqueta-resultado { width: 22px; height: 22px; font-size: 11px; }
            .precio-gas-movil {
                font-family: 'Inter', sans-serif; font-weight: 700; font-size: 14px; color: #55575c;
                text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums;
            }
            .precio-gas-movil small { font-size: 9.5px; font-weight: 600; color: #90a4ae; }
            .precio-gas-movil.precio-mas-barato { color: var(--viu-acento); }
            .precio-gas-movil.precio-segundo-barato { color: #173e19; }
            .ir-gas-movil { display: flex; justify-content: flex-end; }
            .btn-ir-gas-movil {
                appearance: none; -webkit-appearance: none; border: none; background: transparent;
                padding: 0; margin: 0; cursor: pointer; display: block; line-height: 0;
            }
            .btn-ir-gas-movil:active { opacity: 0.8; }
            .btn-ir-gas-movil img { display: block; width: 40px; height: 26px; }
            .gas-movil .nota-fuente-gasolina { font-size: 9.5px; margin-top: 8px; }
        }

        /* ══════════════════════════════════════════════════════════════
           v69 (27 de septiembre de 2026)
           ══════════════════════════════════════════════════════════════ */
        /* Letrero negro del destino (#chipFiltroBucket) — MÓVIL Y ORDENADOR:
           con icono de imagen (farmacia abierta o surtidor de Viu Coins) en
           vez de emoji, y sin su propia ✕ mientras hay ruta: la única ✕ es
           la de "Quitar recorrido", que cierra todo el conjunto. */
        .icono-letrero-destino {
            width: 18px; height: 18px; object-fit: contain; border-radius: 4px;
            vertical-align: -4px; margin-right: 7px; display: inline-block;
        }
        body.ruta-activa #btnQuitarFiltroBucket { display: none; }
        body.ruta-activa .chip-filtro-bucket { padding-right: 16px; }
        /* Pin temporal de la gasolinera: mismo marco que el de la farmacia
           de guardia; el surtidor es transparente, así que no se recorta. */
        .pin-gasolinera-ruta { background-size: 82%; background-repeat: no-repeat; }

        @media (hover: none) and (pointer: coarse) {
            /* Letrero del destino en el MÓVIL: el JS (colocarLetreroDestino)
               lo pone debajo del tren y del clima con un ancho máximo del
               borde izquierdo del tren al borde derecho del clima; si el
               nombre no cabe, pasa a dos líneas. */
            .chip-filtro-bucket { box-sizing: border-box; }
            .chip-filtro-bucket span { white-space: normal; line-height: 1.3; min-width: 0; }

            /* Moneda de Viu Coins un 20 % más pequeña (34 → 27 px), sin mover
               su borde izquierdo ni su borde de abajo (el botón baja al fondo
               de la fila, que marca el perfil de 34 px). El número no cambia
               de tamaño y queda centrado a la altura de la moneda nueva. */
            .btn-puntos-nav { align-self: flex-end; }
            .btn-puntos-nav img { width: 27px; height: 27px; }
        }

        /* ══════════════════════════════════════════════════════════════
           v70/v71 (27 de septiembre de 2026) — SOLO ORDENADOR: la sombra
           paralela a 45° del móvil, en TRES elementos y ninguno más.
           ⚠️ La v70 la puso en todos los elementos de los botones y Jaime la
           retiró en la v71: "no resolvieron mi idea y lo empeoró… incluso hay
           cosas que se ven borrosas con la sombra". Sobre los botones
           BLANCOS del ordenador la sombra no da el relieve que da sobre el
           fondo oscuro del móvil; en iconos pequeños y textos finos emborrona.
           Solo se queda donde funcionó: la temperatura del clima (que además
           crece de 13 a 17 px), la imagen de incidencias y el logo redondo
           de Renfe del botón de horarios. No se vuelve a extender sin que
           Jaime lo pida.
           "not all and (hover: none) and (pointer: coarse)" = todo lo que NO
           es táctil, es decir, el ordenador (la tablet usa el bloque táctil).
           ══════════════════════════════════════════════════════════════ */
        @media not all and (hover: none) and (pointer: coarse) {
            :root { --sombra-45: -0.8px 0.8px 0.6px rgba(0, 0, 0, 0.35); }
            .temp-clima-nav { text-shadow: var(--sombra-45); }
            .btn-incidencias-flotante img { filter: drop-shadow(var(--sombra-45)); }
            .icono-renfe-nav { filter: drop-shadow(var(--sombra-45)); }
            /* (anidado: un "not" niega la consulta ENTERA, así que no se puede
               sumar "and (min-width: 720px)" en la misma línea sin que valga
               también para los móviles) */
            @media (min-width: 720px) {
                /* v72: con la letra a 17 px la sombra de 0,8 px se quedaba
                   pequeña (Jaime). Se agranda en la misma proporción que la
                   letra ha crecido, y un poco más: 1,2 px de distancia y 1 px
                   de difuminado, mismo 45° hacia la izquierda y mismo 35 %. */
                .temp-clima-nav { font-size: 17px; text-shadow: -1.2px 1.2px 1px rgba(0, 0, 0, 0.35); }
            }
        }


        /* ══════════════════════════════════════════════════════════════
           Etapa 56 (6 de octubre de 2026) — GUÍA v8 DE NEXIABUS Y COLUMNA
           IZQUIERDA NUEVA. Diseños aprobados por Jaime (bocetos B1–B5).
           Va al FINAL del archivo a propósito: estas reglas tienen que
           ganar a las de más arriba con la misma especificidad.
           Orden de la columna izquierda:
            · Ordenador: Ubicación, Anuncios, Farmacias, Gasolina, Favoritos
              (sin campana).
            · Móvil: Buscador (fijo), Anuncios, Notificaciones, Farmacias,
              Gasolineras, Favoritos. Ubicación sigue dentro del mando de zoom.
            · Tablet: igual que el móvil, sin el buscador (empieza en 66).
           Siempre 10 px entre botones.
           ══════════════════════════════════════════════════════════════ */

        /* ── Botones nuevos: mismo lenguaje que el de farmacia ── */
        .btn-anuncios-flotante, .btn-gasolina-flotante, .btn-notificaciones-flotante {
            --sombra-boton: 0 2px 10px rgba(0,0,0,0.3);
            position: absolute; left: 10px; z-index: 900;
            width: 34px; height: 34px; padding: 0; border-radius: 50%; border: none;
            cursor: pointer; display: flex; align-items: center; justify-content: center;
            background: white; box-shadow: var(--sombra-boton);
        }
        .btn-anuncios-flotante img, .btn-gasolina-flotante img, .btn-notificaciones-flotante img {
            width: 100%; height: 100%; object-fit: contain; display: block; pointer-events: none;
        }
        .btn-anuncios-flotante img { padding: 6px 6px 7px; }
        .btn-gasolina-flotante img { padding: 5px; }
        .btn-notificaciones-flotante img { padding: 5px; }
        .btn-gasolina-flotante:hover, .btn-gasolina-flotante:active,
        .btn-notificaciones-flotante:hover, .btn-notificaciones-flotante:active { filter: brightness(1.08); }
        /* La campana solo existe en móvil y tablet (ver el bloque táctil). */
        .btn-notificaciones-flotante { display: none; }

        /* Anuncios: gris y apagado sin anuncios; fondo amarillo claro con
           amarillos; rosa claro con rojos (el JS alterna cada 2 s si hay de
           los dos). */
        .btn-anuncios-flotante.sin-anuncios { cursor: default; }
        .btn-anuncios-flotante.con-amarillos { background: #FFF1C4; }
        .btn-anuncios-flotante.con-rojos { background: #FDDADA; }
        .btn-anuncios-flotante img { transition: opacity 0.25s; }
        /* Sin leer: pulso blanco translúcido detrás del botón, hasta que se
           abre el panel. Nunca se abre nada solo. */
        .btn-anuncios-flotante.sin-leer { animation: pulsoAnunciosSinLeer 1.6s ease-in-out infinite; }
        @keyframes pulsoAnunciosSinLeer {
            0%, 100% { box-shadow: 0 0 0 0 rgba(255,255,255,0), var(--sombra-boton); }
            50% { box-shadow: 0 0 0 6px rgba(255,255,255,0.75), var(--sombra-boton); }
        }

        /* Ordenador */
        .btn-anuncios-flotante { top: 114px; }
        .btn-farmacia-flotante { top: 158px; }
        .btn-gasolina-flotante { top: 202px; }
        #btnDestacados { top: 246px; }
        .panel-favoritos { top: 286px; }
        .panel-anuncios { top: 158px; }

        /* ── Panel de anuncios (blanco, como el de favoritos) ── */
        .panel-anuncios {
            position: absolute; left: 10px; z-index: 1000;
            background: white; border-radius: 14px; box-shadow: 0 8px 28px rgba(0,0,0,0.18);
            width: min(340px, calc(100vw - 20px)); max-height: calc(100vh - 180px);
            overflow-y: auto; display: none;
        }
        .panel-anuncios.abierto { display: block; }
        .panel-anuncios-titulo {
            padding: 10px 14px; font-size: 13px; font-weight: 700; color: #999;
            border-bottom: 1px solid #eee; text-transform: uppercase; letter-spacing: 0.04em;
            display: flex; align-items: center; gap: 7px;
        }
        .icono-grupo-anuncios { width: 22px; height: 22px; border-radius: 50%; object-fit: cover; }
        .item-anuncio { display: flex; gap: 10px; padding: 10px 14px; border-bottom: 1px solid #eee; font-size: 13px; line-height: 1.4; color: #333; }
        .item-anuncio:last-child { border-bottom: none; }
        .item-anuncio > img { width: 22px; height: 22px; flex: 0 0 auto; margin-top: 1px; }
        .item-anuncio b { font-size: 14px; color: #1B2430; }
        .item-anuncio.rojo { background: #fff5f5; }
        .anuncio-alcance { display: inline-block; margin-top: 5px; font-size: 11px; color: #3E4F86; background: #E7EAF4; border-radius: 8px; padding: 2px 7px; }
        .anuncio-vigencia { font-size: 11px; color: #6B7280; margin-left: 6px; white-space: nowrap; display: inline-block; }

        /* ── Favoritos: un solo panel con tres títulos ── */
        .panel-favoritos { max-height: calc(100vh - 306px); overflow-y: auto; }
        .panel-favoritos-seccion { background: #fafafa; font-size: 12px; }
        .sin-favoritos-seccion { padding: 9px 14px; font-size: 13px; color: #9aa0a6; border-bottom: 1px solid #eee; font-style: italic; }

        /* ── Capas: título del grupo "Bus municipal" ── */
        .grupo-capas {
            font-size: 11px; font-weight: 700; color: #999; text-transform: uppercase; letter-spacing: 0.04em;
            padding: 8px 0 2px; border-top: 1px solid #eee; margin-top: 4px;
        }

        /* ── Aviso "Próximamente" (campana) ── */
        .toast-viu {
            position: fixed; z-index: 1700; transform: translateY(-50%);
            background: rgba(27,27,27,0.92); color: white; border-radius: 14px;
            padding: 5px 11px; font-size: 13px; white-space: nowrap;
            opacity: 0; pointer-events: none; transition: opacity 0.2s;
        }
        .toast-viu.visible { opacity: 1; }

        /* ── Paradas: caja con la señal de anuncio en la esquina superior
           derecha (amarilla o roja), que crece y mengua con la parada. ── */
        .parada-bus-caja { position: relative; width: 100%; height: 100%; }
        .parada-bus-viu.apagada { filter: grayscale(1); opacity: 0.55; }
        .senal-parada-bus { position: absolute; top: -12px; right: -12px; padding: 3px; cursor: pointer; line-height: 0; }
        .senal-parada-bus img { width: 13px; height: 13px; display: block; filter: drop-shadow(0 1px 1px rgba(0,0,0,0.4)); }
        .parada-bus-caja.tam-media .senal-parada-bus { top: -10px; right: -10px; }
        .parada-bus-caja.tam-media .senal-parada-bus img { width: 10px; height: 10px; }
        .parada-bus-caja.tam-pequena .senal-parada-bus { top: -9px; right: -9px; }
        .parada-bus-caja.tam-pequena .senal-parada-bus img { width: 8px; height: 8px; }

        /* ── Bus: señal de desvío en el aro y pulso suave del que llega
           primero a una parada recién activada ── */
        .bus-viu-senal { display: none; position: absolute; top: -8px; right: -9px; width: 13px; height: 13px; pointer-events: none; filter: drop-shadow(0 1px 1px rgba(0,0,0,0.4)); }
        .bus-viu.con-desvio .bus-viu-senal { display: block; }
        .bus-viu::after {
            content: ''; position: absolute; inset: 0; border-radius: 50%;
            background: var(--color-ruta); opacity: 0; pointer-events: none; z-index: -1;
        }
        .bus-viu.pulso-llega::after { animation: pulsoBusLlega 1.2s ease-out 2; }
        /* Etapa 64 (bocetos 16 a 18): "Retrasado", "termina aquí" y la guía con la llegada. */
        .pill-retrasado { display: inline-block; background: #e65100; color: #fff; border-radius: 9px; padding: 0 6px; font-size: 11px; font-weight: 700; line-height: 16px; white-space: nowrap; vertical-align: 1px; }
        .globo-bus-viu .pill-retrasado, .btv-sub .pill-retrasado { font-size: 12px; line-height: 18px; padding: 0 8px; }
        .reloj-termina { color: #b9bec6; font-size: 11px; }
        .bus-viu-retraso { display: none; position: absolute; left: 25px; top: 2px; background: #e65100; color: #fff; font-size: 11px; font-weight: 800; line-height: 16px; border-radius: 8px; padding: 0 6px; white-space: nowrap; box-shadow: 0 1px 4px rgba(0,0,0,0.35); pointer-events: none; font-family: 'Inter', sans-serif; }
        .bus-viu.retrasado .bus-viu-retraso { display: block; }
        .bus-grilla-horas.con-llegada { grid-template-columns: repeat(4, 1fr); }
        .chip-horario-renfe.chip-bus-llegada { display: flex; flex-direction: column; align-items: center; line-height: 1.25; padding: 5px 1px; }
        .chip-horario-renfe.chip-bus-llegada small { font-weight: 500; font-size: 10.5px; color: #4b6b52; }
        .bus-solo-llegan { font-size: var(--vm-fs-caption); color: #1b5e20; background: #e8f5e9; border-radius: 8px; padding: 6px 9px; margin-top: 8px; line-height: 1.35; }
        @keyframes pulsoBusLlega {
            0% { transform: scale(1); opacity: 0.45; }
            100% { transform: scale(2.6); opacity: 0; }
        }

        /* ── Reloj de la parada ── */
        .reloj-parada-vivo { width: 6px; height: 6px; border-radius: 50%; background: #25d366; box-shadow: 0 0 0 3px rgba(37,211,102,0.25); display: inline-block; flex: 0 0 auto; }
        .reloj-parada-programado { color: #b9bec6; font-weight: 400; }
        .reloj-parada-aviso { display: flex; align-items: center; gap: 5px; color: #ffd54f; font-size: 11px; }
        .reloj-parada-aviso img { width: 12px; height: 12px; }

        /* ── Avisos dentro de los globos (bus y parada) ── */
        .globo-aviso-bus { display: flex; gap: 7px; align-items: flex-start; margin-top: 6px; padding-top: 6px; border-top: 1px solid #eee; max-width: 260px; white-space: normal; }
        .globo-aviso-bus img { width: 18px; height: 18px; flex: 0 0 auto; margin-top: 1px; }
        .globo-aviso-vigencia { font-size: 11px; color: #6B7280; }

        /* ── MÓVIL Y TABLET (táctil) ── */
        @media (hover: none) and (pointer: coarse) {
            .btn-anuncios-flotante, .btn-gasolina-flotante, .btn-notificaciones-flotante {
                --sombra-boton: var(--sombra-45);
                width: 29px; height: 29px; background: var(--mando-oscuro-fondo);
            }
            .btn-anuncios-flotante.con-amarillos { background: #FFF1C4; }
            .btn-anuncios-flotante.con-rojos { background: #FDDADA; }
            .btn-anuncios-flotante img { padding: 5px 5px 6px; }
            .btn-gasolina-flotante img, .btn-notificaciones-flotante img { padding: 4px; filter: drop-shadow(var(--sombra-45)); }
            .btn-notificaciones-flotante { display: flex; }

            /* Móvil: el buscador (34 px) encabeza la columna en 66 */
            .btn-anuncios-flotante { top: 110px; }
            .panel-anuncios { top: 149px; max-height: calc(100vh - 170px); }
            .btn-notificaciones-flotante { top: 149px; }
            .btn-farmacia-flotante { top: 188px; }
            .btn-gasolina-flotante { top: 227px; }
            #btnDestacados { top: 266px; }
            .contenedor-relojes-c1, .panel-favoritos { top: 305px; }
            .panel-favoritos { max-height: calc(100vh - 325px); }
        }
        /* Tablet: sin el botón del buscador, la columna empieza en 66 */
        @media (hover: none) and (pointer: coarse) and (min-width: 720px) {
            .btn-anuncios-flotante { top: 66px; }
            .panel-anuncios { top: 105px; max-height: calc(100vh - 125px); }
            .btn-notificaciones-flotante { top: 105px; }
            .btn-farmacia-flotante { top: 144px; }
            .btn-gasolina-flotante { top: 183px; }
            #btnDestacados { top: 222px; }
            .contenedor-relojes-c1, .panel-favoritos { top: 261px; }
            .panel-favoritos { max-height: calc(100vh - 281px); }
        }

        /* ══════════════════════════════════════════════════════════════
           Etapa 56b (6 de octubre de 2026) — ajustes de Jaime en el móvil.
           Al FINAL del archivo, para ganar a lo anterior.
           ══════════════════════════════════════════════════════════════ */
        /* Globo del bus: "Ruta:" en gris, encima del de dónde a dónde. */
        .globo-bus-etiqueta { color: #6B7280; }
        /* Menú del usuario: la moneda a la izquierda de "Mis Viu Coins". */
        .opcion-menu-con-icono { display: flex; align-items: center; gap: 8px; }
        .icono-opcion-menu { width: 20px; height: 20px; border-radius: 50%; object-fit: cover; flex: 0 0 auto; }

        @media (hover: none) and (pointer: coarse) {
            /* 1 · Globos del bus y de la parada: letra más pequeña. */
            .globo-bus-viu .leaflet-popup-content, .globo-parada-bus .leaflet-popup-content {
                font-size: 11px; line-height: 1.4; margin: 10px 14px;
            }
            .globo-bus-viu .globo-aviso-bus img, .globo-parada-bus .globo-aviso-bus img { width: 15px; height: 15px; }
            .globo-bus-viu .globo-aviso-vigencia, .globo-parada-bus .globo-aviso-vigencia { font-size: 10px; }
            /* 3 · Sin la etiqueta blanca de la parada: en una pantalla táctil
               no hay ratón y salía al tocar, con el texto ya cambiado. */
            .leaflet-tooltip.tooltip-parada-bus { display: none; }
            /*     Reloj de la parada (el negro): mismo contenido, letra algo
               más pequeña. */
            .reloj-parada-bus { font-size: 11px; padding: 3px 7px; }
            .reloj-parada-nombre { font-size: 10px; }
            .reloj-parada-aviso { font-size: 10px; }
            .reloj-parada-aviso img { width: 11px; height: 11px; }
            /* 8 · Campana y gasolina con la misma proporción que la cruz de
               farmacia (5 px de margen en el botón de 29 px). La estrella no
               cambia: va más grande a propósito. */
            .btn-gasolina-flotante img, .btn-notificaciones-flotante img { padding: 5px; }
            .icono-opcion-menu { width: 16px; height: 16px; }
        }

        /* ══════════════════════════════════════════════════════════════
           Etapa 57 (6 de octubre de 2026) — NAV NUEVO. Bocetos 6 y 7 (v2)
           aprobados por Jaime. Las posiciones de la fila de arriba (tren,
           bus, clima y bandera) las calcula alinearPilaSuperiorDerecha().
           Al FINAL del archivo, para ganar a lo anterior.
           ══════════════════════════════════════════════════════════════ */
        /* La moneda y la cantidad salen del nav: van en el menú del usuario.
           El botón sigue existiendo (oculto) porque su lógica engancha el
           saldo y cuenta la entrada del día. */
        #btnPuntosViu { display: none !important; }

        /* Tren, bus y clima, por encima del nav (suben a su fila). */
        .btn-clima-flotante, .btn-renfe-flotante, .btn-bus-nav { z-index: 2001; }
        /* El aviso "Próximamente…" siempre por encima de todo. */
        .toast-viu { z-index: 2300; }
        /* Con un menú del nav abierto, el nav pasa por encima de ellos. */
        nav:has(.panel-menu.abierto, .panel-idiomas.abierto) { z-index: 2200; }

        /* El botón del bus: redondo, con el fondo del clima; dentro, el bus
           del pin del mapa en un círculo blanco. */
        .btn-bus-nav {
            position: absolute; top: 9px; right: 160px; width: 26px; height: 26px; padding: 0;
            border-radius: 50%; border: 1px solid #e5e5e5; background: #f7f7f7;
            box-shadow: 0 2px 8px rgba(0,0,0,0.18); cursor: pointer;
            display: flex; align-items: center; justify-content: center;
        }
        .btn-bus-nav:hover, .btn-bus-nav:active { background: #eee; }
        .btn-bus-nav span { display: block; width: 76%; height: 76%; border-radius: 50%; background: white; overflow: hidden; box-shadow: 0 0 0 1px rgba(0,0,0,0.08); pointer-events: none; }
        .btn-bus-nav img { width: 100%; height: 100%; object-fit: cover; display: block; }

        /* Menú del usuario: crece con su contenido (la cantidad de Viu Coins
           nunca queda fuera). */
        #panelMenuPerfil { width: auto; min-width: 200px; max-width: calc(100vw - 20px); }
        #panelMenuPerfil .opcion-menu { white-space: nowrap; }
        .cantidad-opcion-menu { white-space: nowrap; }
        .cantidad-opcion-menu b { color: #b8860b; }

        @media (hover: none) and (pointer: coarse) {
            /* Sin el nav gris: transparente y sin sombra. El usuario y la
               bandera se quedan en el mismo sitio. Lo vacío del nav deja
               pasar los toques al mapa. */
            nav { background: transparent; box-shadow: none; pointer-events: none; }
            nav .marca-viu-nav, nav .grupo-nav-derecha, nav .barra-busqueda-nav-wrap { pointer-events: auto; }
            .btn-bus-nav { background: var(--mando-oscuro-fondo); border-color: transparent; box-shadow: var(--sombra-45); }
            .btn-bus-nav:hover, .btn-bus-nav:active { background: var(--mando-oscuro-fondo-activo); }
            #panelMenuPerfil { min-width: 170px; }
        }

        /* ══════════════════════════════════════════════════════════════
           Etapa 57b (6 de octubre de 2026) — en móviles estrechos (360 px)
           el clima tocaba la bandera. Solo móvil y tablet.
           ══════════════════════════════════════════════════════════════ */
        @media (hover: none) and (pointer: coarse) {
            /* El clima, más estrecho por dentro: el icono y la temperatura
               conservan su tamaño; solo bajan el relleno (9 → 6 px) y el
               espacio entre los dos (5 → 3 px). Sigue creciendo solo si
               llega una temperatura más ancha. */
            .btn-clima-flotante { padding: 0 6px; gap: 3px; }
            /* El mando de zoom, del ancho de la bandera (38 → 32 px), con todo
               lo que lleva dentro. El JS lo acerca al borde (7 px). */
            .grupo-mando-mapa, .btn-zoom-mapa, .indicador-zoom, .btn-capas { width: 32px; }
            .grupo-mando-mapa .btn-mi-ubicacion { width: 32px; }
        }

        /* ══════════════════════════════════════════════════════════════
           Etapa 57c (6 de octubre de 2026) — el botón del bus lleva el icono
           nuevo de Jaime (bus de frente con el logo de Miguel Ribera), sin el
           círculo blanco: directamente sobre el fondo del botón, como la
           campana y la gasolina.
           ══════════════════════════════════════════════════════════════ */
        .btn-bus-nav img { width: 76%; height: 76%; object-fit: contain; display: block; pointer-events: none; }
        @media (hover: none) and (pointer: coarse) {
            .btn-bus-nav img { filter: drop-shadow(var(--sombra-45)); }
        }
        /* Etapa 62 (boceto 14): el bus espera a saber si hay cuenta. */
        .btn-bus-nav.esperando-sesion { opacity: 0.75; pointer-events: none; }
        .btn-bus-nav.esperando-sesion::after {
            content: ''; position: absolute; inset: -4px; border-radius: 50%;
            border: 2.5px solid rgba(26,75,140,0.18); border-top-color: #1a4b8c;
            animation: busEsperaGira 0.8s linear infinite; pointer-events: none;
        }
        @keyframes busEsperaGira { to { transform: rotate(360deg); } }

        /* ══════════════════════════════════════════════════════════════
           Etapa 57d (6 de octubre de 2026) — SOLO MÓVIL (la columna con la
           lupa). Regla de Jaime:
            · Dx = el hueco entre la lupa y el botón de anuncios (10 px).
            · Dy = Dx × 1,6 (16 px) = el hueco entre el avatar y la lupa.
           El avatar termina en 45 px (top 11 + 34), así que la lupa sube a
           45 + 16 = 61 px (antes 66) y toda la columna sube lo mismo (5 px),
           con las mismas distancias de siempre entre botones. El mando de
           zoom va a la altura de la lupa (lo coloca el JS).
           Base de la columna en --col-top; cada botón, a su distancia de la
           lupa (lupa 34 px + 10; los demás, 29 px + 10).
           ══════════════════════════════════════════════════════════════ */
        @media (hover: none) and (pointer: coarse) and (max-width: 719px) {
            :root { --col-fin-avatar: 45px; --col-dx: 10px; --col-dy: calc(var(--col-dx) * 1.6); --col-top: calc(var(--col-fin-avatar) + var(--col-dy)); }
            .btn-buscador-mapa { top: var(--col-top); }
            .btn-anuncios-flotante { top: calc(var(--col-top) + 44px); }
            .btn-notificaciones-flotante, .panel-anuncios { top: calc(var(--col-top) + 83px); }
            .btn-farmacia-flotante { top: calc(var(--col-top) + 122px); }
            .btn-gasolina-flotante { top: calc(var(--col-top) + 161px); }
            #btnDestacados { top: calc(var(--col-top) + 200px); }
            .contenedor-relojes-c1, .panel-favoritos { top: calc(var(--col-top) + 239px); }
            .panel-favoritos { max-height: calc(100vh - var(--col-top) - 259px); }
            .panel-anuncios { max-height: calc(100vh - var(--col-top) - 103px); }
        }

        /* ══════════════════════════════════════════════════════════════
           Etapa 58.2 (7 de octubre de 2026) — GUÍA PARA COGER EL BUS
           (boceto 9 aprobado por Jaime) y la invitación sin cuenta (boceto
           8 D). Mismo marco que "Pedir un turno" (.ficha-reserva, .cp-*).
           ══════════════════════════════════════════════════════════════ */
        .ficha-bus { padding-top: 18px; }
        .cabecera-bus-logo { display: flex; justify-content: center; margin-bottom: 6px; }
        .cabecera-bus-logo img { height: 64px; width: auto; display: block; }
        .ficha-bus .reserva-subtitulo { margin-bottom: 10px; }
        .cp-chip-servicio.bus-sentido.seleccionado { border-color: var(--vm-azul); background: #EEF2FB; box-shadow: inset 0 0 0 1px var(--vm-azul); }
        .bus-punto { display: inline-block; width: 9px; height: 9px; border-radius: 50%; margin-right: 8px; vertical-align: 1px; }
        .bus-fila-hora { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
        .bus-btn-ahora { border: 1.5px solid #dce8ea; background: white; border-radius: 12px; padding: 10px 16px; font-weight: 700; font-size: var(--vm-fs-cuerpo-destacado); font-family: 'Inter', sans-serif; color: var(--vm-texto); cursor: pointer; }
        .bus-btn-ahora.seleccionado { background: var(--vm-azul); border-color: var(--vm-azul); color: white; }
        .campo-reserva .bus-fila-hora input[type=time] { flex: 0 0 auto; width: 118px; padding: 9px 10px; }
        .bus-o { font-size: var(--vm-fs-caption); color: #8ba3a8; }
        .bus-btn-cercana { width: 100%; border: 1.5px dashed var(--vm-azul); background: #F4F7FD; color: var(--vm-azul); border-radius: 12px; padding: 10px; font-weight: 700; font-size: var(--vm-fs-cuerpo); font-family: 'Inter', sans-serif; cursor: pointer; }
        .bus-nota-cercana { text-align: center; font-size: var(--vm-fs-caption); color: #6f8890; margin-top: 5px; min-height: 1em; }
        .bus-tarjeta-vivo { background: #0f2a4a; color: white; border-radius: 14px; padding: 12px 14px; text-align: center; min-height: 64px; }
        .btv-etq { font-size: var(--vm-fs-caption); opacity: .8; }
        .btv-tiempo { font-size: 30px; font-weight: 800; font-family: 'Inter', sans-serif; display: flex; align-items: center; justify-content: center; gap: 8px; color: #76ff03; line-height: 1.2; }
        .btv-tiempo.btv-no-vivo { color: #e3f2fd; font-size: 24px; }
        .btv-tiempo.btv-sin { color: #cfd8dc; font-size: var(--vm-fs-cuerpo-destacado); font-weight: 600; padding: 6px 0; }
        .btv-sub { font-size: var(--vm-fs-caption); opacity: .85; }
        .bus-grilla-horas { display: grid; grid-template-columns: repeat(5, 1fr); gap: 5px; }
        .bus-grilla-horas .chip-horario-renfe { border: 1px solid transparent; font-family: 'Inter', sans-serif; }
        .bus-nota-agenda { text-align: center; font-size: var(--vm-fs-caption); color: #5c7d84; margin-top: 6px; }
        .bus-aviso-horarios { font-size: 10.5px; color: #9aa0a6; text-align: center; margin: 10px 0 8px; line-height: 1.35; }
        .bus-btn-ir { display: flex; align-items: center; justify-content: center; gap: 8px; background: #2E7D32; }
        .bus-btn-ir:disabled { background: #bbb; }
        .bus-btn-ir img { height: 22px; width: auto; }
        .ficha-bus .cp-botones-paso .btn-confirmar-codigo { flex: 1; }

        /* Sin cuenta: centrado entre la columna izquierda y el mando de zoom. */
        .pop-bus-sin-cuenta {
            position: fixed; top: 66px; left: 50%; transform: translateX(-50%); width: min(320px, calc(100vw - 104px)); z-index: 2300;
            display: flex; gap: 10px; align-items: flex-start; background: white; border-radius: 14px; padding: 12px;
            border-left: 5px solid var(--vm-azul); box-shadow: 0 8px 28px rgba(0,0,0,0.22);
            font-family: 'Inter', sans-serif; font-size: var(--vm-fs-cuerpo); color: #1B2430;
        }
        .pop-bus-sin-cuenta[hidden] { display: none; }
        .pop-bus-sin-cuenta img { height: 40px; width: auto; flex: 0 0 auto; }
        .pop-bus-sin-cuenta span { font-size: var(--vm-fs-caption); color: #6B7280; }
        .pop-bus-sin-cuenta .btn-confirmar-codigo { padding: 9px; font-size: var(--vm-fs-cuerpo); }
        @media (hover: none) and (pointer: coarse) and (max-width: 719px) { .pop-bus-sin-cuenta { top: 54px; } }

        /* Mi Agenda: los buses guardados (color propio, como trenes y citas). */
        .cp-dia.mia-dia-bus { background: rgba(0,131,143,0.16); }
        .tarjeta-acto-bus-mia { background: #e0f7fa; position: relative; padding-right: 66px; }
        .hora-acto-bus-mia { color: #00838F; }
        .origen-acto-bus-mia { font-size: var(--vm-fs-caption); color: #006064; margin-top: var(--vm-esp-pegado); }
        .cp-dia.mia-dia-bus.seleccionado { background: rgba(46,125,50,0.75); }

/* ══════════════════════════════════════════════════════════════
   Etapa 59 — anuncios del Ayuntamiento, AEMET y Renfe; nivel naranja
   (bocetos 10 y 11 aprobados por Jaime)
   ══════════════════════════════════════════════════════════════ */
.panel-anuncios { width: min(360px, calc(100vw - 20px)); }
.btn-anuncios-flotante.con-naranjas { background: #FFE0C2; }
@media (hover: none) and (pointer: coarse) {
    .btn-anuncios-flotante.con-naranjas { background: #FFE0C2; }
}
.item-anuncio.naranja { background: #fff8f1; }
.anuncio-fuente { display: block; margin-top: 4px; font-size: 10.5px; color: #8a8f98; }
.anuncio-ver-mapa {
    display: inline-block; margin: 5px 0 0 6px; padding: 0; border: none; background: none;
    font: inherit; font-size: 11px; font-weight: 700; color: #1565c0; cursor: pointer;
}
.anuncio-ver-mapa:hover { text-decoration: underline; }
.icono-grupo-redondo {
    width: 22px; height: 22px; border-radius: 50%; background: #eef2f7; flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center; overflow: hidden;
}
.icono-grupo-redondo img { width: 100%; height: 100%; object-fit: contain; }

/* El anuncio del Ayuntamiento en un punto del mapa */
.pin-anuncio-ayto {
    position: relative; width: 34px; height: 34px; border-radius: 50%; background: white;
    border: 3px solid #e0a800; box-shadow: 0 2px 6px rgba(0,0,0,.35);
    display: flex; align-items: center; justify-content: center; cursor: pointer;
}
.pin-anuncio-ayto.naranja { border-color: #e8650f; }
.pin-anuncio-ayto.rojo { border-color: #c62828; }
.pin-anuncio-ayto img.escudo { width: 24px; height: 24px; object-fit: contain; }
.pin-anuncio-ayto img.senal { position: absolute; top: -9px; right: -9px; width: 15px; height: 15px; filter: drop-shadow(0 1px 1px rgba(0,0,0,.4)); }
.pin-anuncio-ayto::after {
    content: ''; position: absolute; inset: -3px; border-radius: 50%;
    border: 3px solid rgba(224,168,0,.5); animation: pulsoAnuncioAyto 1.6s ease-out infinite; pointer-events: none;
}
.pin-anuncio-ayto.naranja::after { border-color: rgba(232,101,15,.5); }
.pin-anuncio-ayto.rojo::after { border-color: rgba(198,40,40,.5); }
@keyframes pulsoAnuncioAyto {
    0% { transform: scale(1); opacity: 1; }
    100% { transform: scale(1.6); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) { .pin-anuncio-ayto::after { animation: none; opacity: 0; } }
.globo-anuncio-ayto .leaflet-popup-content-wrapper { border-radius: 12px; }
.globo-anuncio-ayto .leaflet-popup-content { margin: 11px 13px; font-size: 12.5px; line-height: 1.4; color: #333; }
.cab-anuncio-ayto {
    display: flex; align-items: center; gap: 7px; font-size: 10.5px; font-weight: 800; color: #999;
    text-transform: uppercase; letter-spacing: .04em; margin-bottom: 6px;
}
.cab-anuncio-ayto img { width: 18px; height: 18px; object-fit: contain; }
.cuerpo-anuncio-ayto { display: flex; gap: 8px; }
.cuerpo-anuncio-ayto > img { width: 18px; height: 18px; flex: 0 0 auto; margin-top: 1px; }
.cuerpo-anuncio-ayto b { font-size: 13.5px; color: #1B2430; }


/* ══════════════════════════════════════════════════════════════
   Etapa 69 — Mis Rutas Viu (boceto 26) y la agenda personal (boceto 27)
   ══════════════════════════════════════════════════════════════ */
.mrv-pestanas { display: flex; background: #eef1f3; border-radius: 12px; padding: 3px; margin: 4px 0 12px; }
.mrv-pestana { flex: 1; border: 0; background: transparent; padding: 7px 2px; border-radius: 9px; font: inherit; font-weight: 700; font-size: var(--vm-fs-caption); color: #5f6f7a; cursor: pointer; }
.mrv-pestana.activa { background: #fff; color: var(--vm-texto); box-shadow: 0 1px 3px rgba(0,0,0,0.12); }
.mrv-cifras { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.mrv-km { border-radius: 16px; padding: 12px; display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.mrv-km.mrv-pie { background: #e8f5e9; color: #1b5e20; }
.mrv-km.mrv-veh { background: #e3edf9; color: #1a4b8c; }
.mrv-icono { line-height: 0; margin-bottom: 2px; }
.mrv-valor { font-size: 28px; font-weight: 800; line-height: 1.1; }
.mrv-valor small { font-size: 14px; font-weight: 700; }
.mrv-etq { font-weight: 700; font-size: var(--vm-fs-caption); }
.mrv-extra { font-size: 11px; opacity: 0.8; }
.mrv-total { margin-top: 12px; background: #fff7e6; border: 1px solid #f3dfb3; border-radius: 14px; padding: 10px 12px; font-size: var(--vm-fs-caption); line-height: 1.5; color: var(--vm-texto); }
.mrv-grafica { margin-top: 14px; }
.mrv-graf-tit { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; font-size: var(--vm-fs-caption); margin-bottom: 8px; color: var(--vm-texto); }
.mrv-graf-tit small { color: #6b7a85; }
.mrv-barras { display: flex; align-items: flex-end; gap: 8px; height: 110px; border-bottom: 1.5px solid #e3e6ea; padding: 0 2px; }
.mrv-barras.mrv-m-mes { gap: 2px; }
.mrv-barras.mrv-m-total { gap: 6px; }
.mrv-col { flex: 1; height: 100%; display: flex; align-items: flex-end; min-width: 0; }
.mrv-barra { width: 100%; display: flex; flex-direction: column; border-radius: 5px 5px 0 0; overflow: hidden; }
.mrv-barra .mrv-p, .mrv-leyenda i.mrv-p { background: #43a047; }
.mrv-barra .mrv-v, .mrv-leyenda i.mrv-v { background: #5b8fd6; }
.mrv-col.hoy .mrv-barra { box-shadow: 0 0 0 2px rgba(46,125,50,0.35); }
.mrv-etqs { display: flex; gap: 8px; padding: 4px 2px 0; }
.mrv-etqs.mrv-m-mes { gap: 2px; }
.mrv-etqs.mrv-m-total { gap: 6px; }
.mrv-etqs span { flex: 1; min-width: 0; text-align: center; font-size: 10.5px; font-weight: 700; color: #6b7a85; white-space: nowrap; overflow: visible; }
.mrv-etqs span.hoy { color: var(--vm-texto); }
.mrv-leyenda { display: flex; gap: 12px; font-size: 11px; color: #55646e; margin-top: 6px; }
.mrv-leyenda span { display: inline-flex; align-items: center; gap: 4px; }
.mrv-leyenda i { width: 10px; height: 10px; border-radius: 3px; display: inline-block; }
.mrv-logros { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-top: 14px; }
.mrv-logro { background: #f4f6f8; border-radius: 12px; padding: 8px 6px; text-align: center; }
.mrv-logro b { display: block; font-size: 17px; color: var(--vm-texto); }
.mrv-logro span { display: block; font-size: 10.5px; color: #6b7a85; line-height: 1.3; }
.mrv-priv { margin-top: 12px; font-size: 11px; color: #8a959c; text-align: center; line-height: 1.45; }
.mrv-borrar { border: 0; background: none; color: #c62828; text-decoration: underline; font: inherit; font-size: 11px; cursor: pointer; padding: 2px; }
.mrv-vacio { text-align: center; color: var(--vm-texto); padding: 10px 4px; }
.mrv-vacio svg { width: 44px; height: 44px; color: #43a047; }
.mrv-vacio p { font-size: var(--vm-fs-cuerpo); line-height: var(--vm-lh-cuerpo); margin: 8px 0 0; }

/* La agenda personal: el punto dorado, la tarjeta, el botón «+» y su ficha */
.agenda-mia-leyenda { flex-wrap: wrap; row-gap: 4px; }
.punto-personal-mia { background: #c9a227; box-shadow: inset 0 0 0 1.5px #8a6a10; }
.cp-dia.mia-dia-personal { background: rgba(201,162,39,0.26); }
.cp-dia.mia-dia-personal.seleccionado { background: rgba(46,125,50,0.75); }
.tarjeta-acto-personal-mia { background: #fbf3dc; position: relative; }
.hora-acto-personal-mia { color: #8a6a10; }
.nota-acto-personal-mia { font-size: var(--vm-fs-caption); color: #5a4a1a; margin-top: 2px; white-space: pre-wrap; word-break: break-word; }
.origen-acto-personal-mia { font-size: var(--vm-fs-caption); color: #8a6a10; margin-top: var(--vm-esp-pegado); display: flex; align-items: center; gap: 5px; }
.acciones-personal-mia { display: flex; gap: 14px; justify-content: flex-end; margin-top: 2px; }
.acciones-personal-mia button { border: 0; background: none; font: inherit; font-weight: 700; font-size: var(--vm-fs-caption); cursor: pointer; padding: 4px 2px; }
.btn-editar-personal-mia { color: var(--vm-azul); }
.btn-quitar-personal-mia { color: #c62828; }
.ap-mas-zona { position: sticky; bottom: 2px; display: flex; justify-content: flex-end; margin-top: 10px; pointer-events: none; z-index: 2; }
.ap-btn-mas { pointer-events: auto; width: 56px; height: 56px; border-radius: 50%; border: 0; background: #1b3a2f; color: #ffd54f; font-size: 34px; font-weight: 300; line-height: 1; cursor: pointer; box-shadow: 0 4px 14px rgba(0,0,0,0.35), 0 0 0 3px #c9a227; display: grid; place-items: center; padding: 0 0 3px; font-family: inherit; }
.ap-btn-mas:active { transform: scale(0.95); }
.ap-fondo { position: fixed; inset: 0; z-index: 3300; background: rgba(14,34,38,0.55); display: none; align-items: center; justify-content: center; padding: 16px; box-sizing: border-box; }
.ap-fondo.abierto { display: flex; animation: fbFundido 0.2s ease; }
.ap-ficha { width: 100%; max-width: 380px; max-height: 90vh; overflow-y: auto; background: var(--vm-fondo-calido); border-radius: 24px; padding: 18px 20px; box-shadow: 0 10px 34px rgba(0,0,0,0.3); font-size: var(--vm-fs-cuerpo); color: var(--vm-texto); box-sizing: border-box; }
.ap-cab { display: flex; align-items: center; gap: 8px; font-size: 19px; font-weight: 800; margin-bottom: 4px; }
.ap-punto { width: 12px; height: 12px; border-radius: 50%; background: #c9a227; box-shadow: inset 0 0 0 1.5px #8a6a10; flex: none; }
.ap-lbl { display: block; font-weight: 700; margin: 12px 0 5px; }
.ap-opc { font-weight: 500; color: #8a959c; }
.ap-inp { width: 100%; box-sizing: border-box; border: 1.5px solid #dfe6e9; border-radius: 12px; padding: 10px 12px; font: inherit; background: #fff; color: var(--vm-texto); }
textarea.ap-inp { resize: vertical; }
.ap-fila { display: flex; gap: 8px; }
.ap-fila .ap-inp { flex: 1; min-width: 0; }
.ap-fila .ap-hora { flex: 0 0 110px; }
.ap-sug { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.ap-sug button { border: 0; background: #eef0f2; border-radius: 14px; padding: 5px 10px; font: inherit; font-size: 12px; color: #455; cursor: pointer; }
.ap-chk { display: flex; align-items: center; gap: 6px; margin-top: 8px; font-size: var(--vm-fs-caption); }
.ap-msj { color: #c62828; font-size: var(--vm-fs-caption); min-height: 16px; margin-top: 8px; }
.ap-botones { display: flex; gap: 8px; margin-top: 6px; }
.ap-cancelar { background: #ececec; color: #555; border: 0; border-radius: 14px; padding: 12px 16px; font: inherit; font-weight: 700; cursor: pointer; }
.ap-guardar { flex: 1; background: #2e7d32; color: #fff; border: 0; border-radius: 14px; padding: 12px; font: inherit; font-weight: 800; cursor: pointer; }
.ap-guardar:disabled { opacity: 0.6; }


/* ══════════════════════════════════════════════════════════════
   Etapa 70 — guía por ciudad, horario bus a bus e impreso (bocetos 22–24)
   ══════════════════════════════════════════════════════════════ */
.cabecera-bus-logo[hidden] { display: none; }
#busGuiaTitulo { text-align: center; margin: 0 0 2px; }
.bus-guia-resumen-red { text-align: center; font-size: var(--vm-fs-caption); color: #6f8890; margin-bottom: 2px; }
.bus-guia-resumen-red[hidden] { display: none; }
.btv-sentido { display: inline-flex; align-items: center; gap: 6px; margin: 4px auto 2px; padding: 3px 10px; border-radius: 12px; background: rgba(255,255,255,0.12); font-size: var(--vm-fs-caption); font-weight: 700; max-width: 100%; }
.btv-sentido i { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto; box-shadow: 0 0 0 1.5px rgba(255,255,255,0.8); }
.bus-grilla-horas.con-llegada { grid-template-columns: repeat(3, 1fr); }
.chip-horario-renfe.chip-bus-llegada span { display: inline-flex; align-items: center; gap: 5px; font-size: var(--vm-fs-cuerpo); }
.bus-chip-punto { width: 8px; height: 8px; border-radius: 50%; display: inline-block; flex: 0 0 auto; }
.chip-bus-llegada sup, .bus-nota-llamada sup, .bh-tabla sup, .bi-hora sup { color: #a36a00; font-weight: 800; font-size: 0.72em; margin-left: 1px; }
.bus-leyenda-sentidos { display: flex; flex-direction: column; gap: 3px; margin-top: 8px; font-size: var(--vm-fs-caption); color: #4a5d63; }
.bus-leyenda-sentidos span { display: inline-flex; align-items: center; gap: 6px; }
.bus-leyenda-sentidos i { width: 9px; height: 9px; border-radius: 50%; display: inline-block; }
.bus-mas-horas { display: block; margin: 6px auto 0; border: none; background: none; color: #6f8890; font-size: var(--vm-fs-caption); cursor: pointer; font-family: 'Inter', sans-serif; }
.bus-nota-llamada { font-size: 11px; color: #5c6b70; margin-top: 6px; line-height: 1.35; }
.bus-ninguno { font-weight: 700; color: #4a5d63; font-size: var(--vm-fs-cuerpo); }
.bus-btn-horario { width: 100%; margin-top: 10px; border: 1.5px solid #dce8ea; background: white; color: #2d3e8f; border-radius: 12px; padding: 10px; font-weight: 700; font-size: var(--vm-fs-cuerpo); font-family: 'Inter', sans-serif; cursor: pointer; }
.btn-bus-nav.sin-bus-aqui img { filter: grayscale(1) opacity(0.55); }
.bus-nav-ciudad { position: fixed; z-index: 2002; transform: translateX(-50%); background: white; color: #1b3a2f; border-radius: 8px; padding: 2px 8px; font-size: 11px; font-weight: 800; font-family: 'Inter', sans-serif; box-shadow: 0 2px 8px rgba(0,0,0,0.2); opacity: 0; transition: opacity .35s ease; pointer-events: none; white-space: nowrap; }
.bus-nav-ciudad.visible { opacity: 1; }
.bus-nav-ciudad[hidden] { display: none; }
.ficha-bus-ciudad { padding-top: 26px; }
.bus-ciudad-titulo { text-align: center; font-size: var(--vm-fs-subtitulo); margin: 0 26px 6px; color: var(--vm-texto); }
.bus-ciudad-item { display: flex; align-items: center; gap: 12px; width: 100%; text-align: left; border: 1.5px solid #e3ebed; background: white; border-radius: 14px; padding: 10px 12px; margin-top: 8px; cursor: pointer; font-family: 'Inter', sans-serif; color: var(--vm-texto); }
.bus-ciudad-item:hover { border-color: var(--vm-azul); }
.bus-ciudad-item b { display: block; font-size: var(--vm-fs-cuerpo-destacado); }
.bus-ciudad-item small { display: block; font-size: var(--vm-fs-caption); color: #6f8890; }
.bus-ciudad-logo { width: 46px; height: 46px; flex: 0 0 auto; display: grid; place-items: center; }
.bus-ciudad-logo img { max-width: 46px; max-height: 46px; object-fit: contain; }
.fondo-ficha.fondo-bus-encima { z-index: 3050; }
.fondo-ficha.fondo-bus-encima-2 { z-index: 3060; }
.ficha-bus-horario { max-width: 460px; padding: 0; overflow: hidden; display: flex; flex-direction: column; }
.bh-cabecera { background: #14204A; color: white; padding: 14px 16px 12px; display: flex; gap: 10px; align-items: flex-start; justify-content: space-between; }
.bh-cabecera b { display: block; font-size: var(--vm-fs-subtitulo); font-family: 'Inter', sans-serif; }
.bh-cabecera span { display: block; font-size: var(--vm-fs-caption); opacity: .85; margin-top: 2px; }
.bh-cerrar { border: none; background: rgba(255,255,255,0.14); color: white; width: 30px; height: 30px; border-radius: 50%; cursor: pointer; font-size: 15px; flex: 0 0 auto; }
#busHorarioCuerpo { overflow-y: auto; padding: 0 14px 14px; max-height: calc(85vh - 64px); }
.bh-pestanas { display: flex; gap: 4px; margin: 0 -14px; padding: 8px 14px 0; background: #14204A; }
.bh-pestanas button { flex: 1; border: none; border-radius: 10px 10px 0 0; padding: 8px 6px; font-weight: 700; font-size: var(--vm-fs-caption); background: rgba(255,255,255,0.14); color: white; cursor: pointer; font-family: 'Inter', sans-serif; }
.bh-pestanas button.activa { background: white; color: #14204A; }
.bh-dias { display: flex; gap: 6px; margin: 12px 0 4px; flex-wrap: wrap; }
.bh-dias button { border: 1.5px solid #dce8ea; background: white; border-radius: 14px; padding: 5px 12px; font-weight: 700; font-size: var(--vm-fs-caption); cursor: pointer; color: #4a5d63; font-family: 'Inter', sans-serif; }
.bh-dias button.activa { background: #2d3e8f; border-color: #2d3e8f; color: white; }
.bh-bloque { margin-top: 14px; }
.bh-tit { font-weight: 800; font-size: var(--vm-fs-cuerpo); color: var(--vm-texto); display: flex; align-items: center; gap: 7px; margin-bottom: 6px; font-family: 'Inter', sans-serif; }
.bh-tit i { width: 10px; height: 10px; border-radius: 50%; display: inline-block; flex: 0 0 auto; }
.bh-sin .bh-tit { color: #9aa5a8; }
.bh-sin-servicio { font-size: var(--vm-fs-caption); color: #8a979b; }
.bh-tabla { width: 100%; border-collapse: collapse; font-family: 'Inter', sans-serif; font-size: var(--vm-fs-caption); }
.bh-tabla th { font-size: 9.5px; text-transform: uppercase; color: #6f8890; font-weight: 700; padding: 0 3px 5px; text-align: center; border-bottom: 1px solid #e3ebed; }
.bh-tabla td { text-align: center; padding: 6px 3px; border-bottom: 1px solid #f0f3f4; font-weight: 700; color: var(--vm-texto); white-space: nowrap; }
.bh-tabla td.aprox { color: #7d8b8f; font-weight: 600; }
.bh-tabla td.vacia { color: #b8c2c5; font-weight: 500; }
.bh-tabla tr.pasado td { color: #b8c2c5; font-weight: 500; }
.bh-tabla tr.proximo td { background: #e8f5e9; color: #1b5e20; }
.bh-tabla tr.proximo td:first-child { box-shadow: inset 3px 0 0 #2e7d32; }
.bh-tabla td.bh-pill { width: 1%; padding-left: 0; padding-right: 0; }
.bh-tabla td.bh-pill span { background: #2e7d32; color: white; border-radius: 8px; padding: 1px 6px; font-size: 9.5px; }
.bh-tabla tr.bh-salto td { border-bottom: none; padding: 3px; }
.bh-tabla tr.bh-salto button { border: none; background: none; color: #8a979b; font-size: 11px; cursor: pointer; font-family: 'Inter', sans-serif; }
.bh-pie { margin-top: 12px; font-size: 11px; color: #6f8890; line-height: 1.4; }
/* Etapa 97 (boceto 52e): una tabla por pestaña, por hora de salida; el color dice el sentido. */
.bh-leyenda { display: flex; flex-wrap: wrap; gap: 4px 14px; margin: 2px 0 8px; font-family: 'Inter', sans-serif; font-size: 11.5px; font-weight: 800; color: var(--vm-texto); }
.bh-leyenda span { display: inline-flex; align-items: center; gap: 6px; }
.bh-leyenda i { width: 11px; height: 11px; border-radius: 3px; display: inline-block; flex: 0 0 auto; }
.bh-leyenda .bh-sin { color: #9aa5a8; font-weight: 600; }
.bh-tabla.bh-mezcla { table-layout: fixed; }
/* Etapa 97i: con «Ver todas las paradas», la tabla se desliza de lado dentro del modal; la hora de salida y el color se quedan fijos a la izquierda */
.bh-desliza { overflow-x: auto; -webkit-overflow-scrolling: touch; overscroll-behavior-x: contain; }
.bh-desliza.bh-todas-abiertas .bh-tabla.bh-mezcla th, .bh-desliza.bh-todas-abiertas .bh-tabla.bh-mezcla td { width: 50px; }
.bh-desliza.bh-todas-abiertas .bh-tabla.bh-mezcla .bh-color { position: sticky; left: 0; z-index: 2; width: 5px; }
.bh-desliza.bh-todas-abiertas .bh-tabla.bh-mezcla tr > :nth-child(2) { position: sticky; left: 5px; z-index: 2; background: #fff; box-shadow: 2px 0 4px -2px rgba(0,0,0,.18); }
.bh-desliza.bh-todas-abiertas .bh-tabla.bh-mezcla tr.proximo > :nth-child(2) { background: #e8f5e9; }
/* Etapa 97s: sin columna «Salida» (horario impreso), la primera parada no se queda fija al deslizar */
.bh-desliza.bh-sin-salida .bh-tabla.bh-mezcla tbody tr > td:nth-child(2) { position: static; box-shadow: none; background: transparent; }
.bh-desliza.bh-sin-salida .bh-tabla.bh-mezcla thead tr > th:nth-child(2) { left: auto; box-shadow: none; }
.bi-matriz .bh-desliza.bh-sin-salida .bh-tabla.bh-mezcla tbody tr > td:nth-child(2) { background: transparent; }
.bh-desliza.bh-todas-abiertas .bh-tabla.bh-mezcla tr.bh-salto > td { text-align: left; }
.bh-desliza.bh-todas-abiertas .bh-tabla.bh-mezcla tr.bh-salto button { position: sticky; left: 12px; }
.bh-tabla.bh-mezcla th { vertical-align: middle; height: 26px; font-size: 8px; letter-spacing: 0; padding: 0 1px 5px; }
.bh-tabla.bh-mezcla th span { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; line-height: 1.15; word-break: normal; }
.bh-tabla.bh-mezcla .bh-color { width: 5px; padding: 0; border-bottom-color: transparent; }
.bh-tabla.bh-mezcla th.bh-pill, .bh-tabla.bh-mezcla td.bh-pill { width: 50px; }
.bh-tabla.bh-mezcla td { padding-left: 1px; padding-right: 1px; }
.bh-tabla.bh-mezcla td.sale { font-weight: 800; }
.bh-tabla.bh-mezcla tr.proximo td.bh-color { box-shadow: none; }
.bh-tabla.bh-mezcla tr.pasado td.bh-color { opacity: .35; }
.bh-todas { margin-top: 10px; border: none; background: #eef2f8; color: #1a4b8c; font-weight: 700; font-size: 11.5px; border-radius: 10px; padding: 7px 12px; cursor: pointer; font-family: 'Inter', sans-serif; }
.ficha-bus-impreso { max-width: 760px; background: #fffdf6; }
.bi-cab { display: flex; gap: 12px; align-items: center; margin: 0 32px 10px 0; }
.bi-cab img { height: 52px; width: auto; }
.bi-cab b { display: block; font-size: var(--vm-fs-subtitulo); font-family: 'Inter', sans-serif; color: var(--vm-texto); }
.bi-cab span { display: block; font-size: var(--vm-fs-caption); color: #6f8890; }
.bi-columnas { display: grid; grid-template-columns: 1fr; gap: 10px; margin-top: 10px; }
@media (min-width: 720px) { .bi-columnas { grid-template-columns: repeat(3, 1fr); } }
.bi-col { border: 1px solid #ece5cf; border-radius: 12px; overflow: hidden; background: white; }
.bi-col-cab { color: white; padding: 8px 10px; text-shadow: 0 1px 2px rgba(0,0,0,0.25); }
.bi-col-cab b { display: block; font-size: var(--vm-fs-cuerpo); font-family: 'Inter', sans-serif; }
.bi-col-cab small { display: block; font-size: 10.5px; opacity: .92; }
.bi-horas { display: grid; grid-template-columns: repeat(2, 1fr); gap: 4px 10px; padding: 8px 10px 10px; }
.bi-hora { font-family: 'Inter', sans-serif; font-size: var(--vm-fs-cuerpo); }
.bi-hora.con-destino { grid-column: 1 / -1; }
.bi-hora small { color: #6f8890; font-size: 11px; }
.bi-sin { grid-column: 1 / -1; color: #9aa5a8; font-size: var(--vm-fs-caption); }
/* Etapa 97j: el horario impreso, como el horario del bus: pestañas, leyenda y todas las salidas por hora con el color de su sentido */
.bi-pestanas { margin: 4px 0 10px; padding: 6px 6px 0; border-radius: 12px 12px 0 0; }
.bi-matriz .bh-desliza { scrollbar-width: none; }
.bi-matriz .bh-desliza::-webkit-scrollbar { display: none; }
.bi-matriz .bh-tabla.bh-mezcla tr > :nth-child(2) { background: #fffdf6; }
.bh-tabla.bh-mezcla .bh-col-sale { font-weight: 800; color: #14204A; }
/* Etapa 97u (Jaime): el horario impreso, como el PDF. Filas alternas en gris y, encima, una columna sí y otra
   no en azul translúcido (como una capa: se ven las dos). Los nombres hasta en 3 líneas, centrados en altura.
   Sin la columna vacía del «próximo». Las notas, debajo de la tabla, con aire y el mismo ³. */
.bi-matriz .bh-tabla.bh-mezcla tbody tr:nth-child(even) { background: rgba(20, 32, 74, .035); }
.bi-matriz .bh-desliza .bh-tabla.bh-mezcla tr > :nth-child(even):not(.bh-pill) { box-shadow: inset 0 0 0 999px rgba(20, 32, 74, .055); }
.bi-matriz .bh-tabla.bh-mezcla .bh-pill { display: none; }
.bi-matriz .bh-tabla.bh-mezcla th { height: 38px; }
.bi-matriz .bh-tabla.bh-mezcla th span { -webkit-line-clamp: 3; }
.bi-notas-tabla { position: sticky; left: 0; width: var(--bi-ancho, 100%); box-sizing: border-box; padding: 14px 10px 10px; font-size: 11px; line-height: 1.45; color: #33424c; white-space: normal; }
.bi-llamada { margin-bottom: 4px; }
.bi-llamada sup { color: #a36a00; font-weight: 800; font-size: 0.72em; margin-right: 3px; }
.bi-nota-empresa { margin-top: 8px; color: #6f8890; font-size: 10.5px; }
/* Etapa 97k (boceto 56): todo cabe en el modal. Arriba fijo; la matriz en una ventana con dos barras;
   abajo fijo el pie, el botón de descargar y «Powered by: NexIABus», que nunca se deja de ver. */
.ficha-bus-impreso { display: flex; flex-direction: column; height: 85vh; overflow: hidden; }
#busImpresoCuerpo { flex: 1; min-height: 0; display: flex; flex-direction: column; }
#busImpresoCuerpo > .bi-fijo, #busImpresoCuerpo > .bi-pie, #busImpresoCuerpo > .bi-descargar, #busImpresoCuerpo > .bus-powered { flex: none; }
.bi-matriz { flex: 1; min-height: 120px; display: flex; flex-direction: column; }
.bi-matriz .bh-bloque { flex: 1; min-height: 0; display: flex; flex-direction: column; margin: 0; }
.bi-matriz .bh-leyenda { flex: none; }
.bi-ventana { position: relative; flex: 1; min-height: 0; border: 1px solid #ece5cf; border-radius: 10px; background: #fffdf6; }
.bi-ventana .bh-desliza { position: absolute; top: 0; left: 0; right: 14px; bottom: 14px; overflow: auto; overscroll-behavior: contain; }
.bi-matriz .bh-tabla.bh-mezcla thead th { position: sticky; top: 0; z-index: 3; background: #fffdf6; }
.bi-matriz .bh-tabla.bh-mezcla thead th.bh-color, .bi-matriz .bh-tabla.bh-mezcla thead th.bh-col-sale { z-index: 4; }
.bi-barra { position: absolute; background: #eef2f8; border-radius: 6px; cursor: pointer; touch-action: none; z-index: 5; }
.bi-barra[hidden] { display: none; }
.bi-barra-h { left: 2px; right: 16px; bottom: 2px; height: 10px; }
.bi-barra-v { top: 2px; bottom: 16px; right: 2px; width: 10px; }
.bi-pulgar { position: absolute; border-radius: 5px; background: #6f86ad; }
.bi-barra-h .bi-pulgar { top: 2px; bottom: 2px; left: 0; }
.bi-barra-v .bi-pulgar { left: 2px; right: 2px; top: 0; }
.bi-descargar { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px; margin: 10px auto 0; width: min(100%, 320px); padding: 6px 16px; border: none; border-radius: 12px; background: #14204A; color: #fff; cursor: pointer; font-family: 'Inter', sans-serif; text-align: center; line-height: 1.2; }
.bi-descargar b { font-size: 13px; font-weight: 700; }
.bi-descargar span { font-size: 12px; opacity: .9; }
.bi-descargar:active { transform: scale(.98); }
.bi-descargar.cargando { opacity: .6; cursor: progress; }
#busImpresoCuerpo > .bus-powered { margin-top: 8px; }
.bi-pie { margin-top: 8px; }
.bi-lista { columns: 2; column-gap: 14px; margin-top: 4px; }
@media (min-width: 720px) { .bi-lista { columns: 3; } }
.bi-fila { break-inside: avoid; display: flex; align-items: baseline; gap: 7px; padding: 4px 0; border-bottom: 1px solid #f0ead8; font-family: 'Inter', sans-serif; }
.bi-fila .bi-color { width: 4px; align-self: stretch; border-radius: 2px; flex: none; }
.bi-fila b { font-size: var(--vm-fs-cuerpo); font-variant-numeric: tabular-nums; flex: none; }
.bi-fila b sup { color: #a36a00; font-weight: 800; font-size: 0.72em; margin-left: 1px; }
.bi-fila small { color: #6f8890; font-size: 11px; min-width: 0; line-height: 1.25; }
.bi-llamadas { margin-top: 12px; font-size: 11.5px; color: #4a5d63; line-height: 1.5; }
.bi-llamadas span { margin-right: 8px; }
.bi-notas { margin-top: 8px; padding-top: 8px; border-top: 1px solid #ece5cf; font-size: 11px; color: #6f8890; line-height: 1.45; }

        /* ══════════════════════════════════════════════════════════════
           Etapa 71 (16 de octubre de 2026)
           ══════════════════════════════════════════════════════════════ */
        /* ── La ficha de la parada (boceto 31) ── */
        .ficha-parada-bus { position: fixed; z-index: 1600; background: #fff; border-radius: 20px; box-shadow: 0 10px 34px rgba(0,0,0,0.28);
            padding: 14px 14px 12px; font-family: 'Inter', sans-serif; color: #1b2a33; box-sizing: border-box; max-height: 70vh; overflow-y: auto; }
        .ficha-parada-bus[hidden] { display: none; }
        .ficha-parada-bus.movil { left: 12px; right: 12px; bottom: 14px; max-width: 480px; margin: 0 auto; }
        .ficha-parada-bus.ordenador { width: 320px; overflow: visible; max-height: none; }
        .ficha-parada-bus.ordenador::before { content: ''; position: absolute; left: -9px; top: var(--flecha, 40px); width: 18px; height: 18px; background: #fff; transform: rotate(45deg); box-shadow: -3px 3px 6px rgba(0,0,0,0.08); }
        .ficha-parada-bus.ordenador.flecha-derecha::before { left: auto; right: -9px; box-shadow: 3px -3px 6px rgba(0,0,0,0.08); }
        .fp-x { position: absolute; top: 12px; right: 12px; width: 28px; height: 28px; border-radius: 50%; border: none; background: #f2f2f2; color: #555; font-size: 14px; cursor: pointer; z-index: 1; }
        .fp-cab { display: flex; gap: 11px; align-items: center; padding-right: 34px; }
        .fp-icono { width: 40px; height: 40px; border-radius: 10px; padding: 2.5px; box-sizing: border-box; flex: none; box-shadow: 0 1px 5px rgba(0,0,0,0.3); }
        .fp-icono-dentro { width: 100%; height: 100%; background: #fff; border-radius: 7.5px; overflow: hidden; }
        .fp-icono-dentro img { width: 100%; height: 100%; display: block; }
        .fp-nombre { font-size: 17px; font-weight: 800; line-height: 1.15; }
        .fp-sub { font-size: 12px; color: #6b7a85; display: flex; align-items: center; gap: 6px; margin-top: 2px; }
        .fp-sub img { height: 16px; width: auto; max-width: 40px; object-fit: contain; }
        .fp-hasta { font-size: 12px; font-weight: 800; color: #1a4b8c; margin: 12px 0 3px; display: flex; align-items: center; gap: 6px; }
        .fp-flecha { font-size: 14px; }
        .fp-fila { display: flex; align-items: center; gap: 9px; padding: 6px 0; border-bottom: 1px solid #f0f2f4; font-size: 13.5px; }
        .fp-punto { width: 10px; height: 10px; border-radius: 50%; flex: none; box-shadow: 0 0 0 1.5px #fff, 0 0 0 2.5px rgba(0,0,0,0.12); }
        .fp-linea { flex: 1; min-width: 0; line-height: 1.25; }
        .fp-tiempo { font-weight: 800; font-size: 15px; white-space: nowrap; display: inline-flex; align-items: center; gap: 6px; font-variant-numeric: tabular-nums; }
        .fp-vivo { color: #2e7d32; }
        .fp-prog { color: #55646e; font-weight: 700; font-size: 14px; }
        .fp-en-parada { color: #fff; background: #25a55a; border-radius: 9px; padding: 3px 8px; font-size: 12.5px; }
        .fp-punto-vivo { width: 8px; height: 8px; border-radius: 50%; background: #34c759; box-shadow: 0 0 0 3px rgba(52,199,89,0.25); display: inline-block; }
        .fp-aviso { display: flex; gap: 8px; align-items: flex-start; background: #fff8e1; border-radius: 10px; padding: 7px 10px; font-size: 12px; line-height: 1.35; margin-top: 8px; color: #5d4a00; }
        .fp-aviso img { width: 17px; height: 17px; flex: none; margin-top: 1px; }
        .fp-nada { background: #f5f7f9; border-radius: 12px; padding: 10px 12px; font-size: 13px; line-height: 1.45; margin-top: 10px; color: #33424c; }
        .fp-botones { display: flex; gap: 8px; margin-top: 12px; }
        .fp-botones button { flex: 1; text-align: center; background: #eef2f8; color: #1a4b8c; font-weight: 700; font-size: 13px; border-radius: 12px; padding: 10px 6px; border: none; cursor: pointer; font-family: inherit; }
        .fp-pie { font-size: 10.5px; color: #8a959c; margin-top: 8px; display: flex; align-items: center; gap: 5px; justify-content: center; }
        .fp-pie .fp-punto-vivo { width: 6px; height: 6px; box-shadow: none; }
        /* Etapa 97b (bocetos 52 a 52f): «Ahora», «Todo el día en esta parada» y «Mañana» */
        .fp-pie { font-size: 9px; white-space: nowrap; }
        .fp-bloque { font-size: 12px; font-weight: 800; color: #1a4b8c; margin: 12px 0 3px; }
        .fp-linea .fp-ac { font-weight: 600; }
        .fp-linea small { display: block; font-size: 11.5px; color: #6b7a85; margin-top: 1px; }
        .fp-en-parada { display: inline-flex; align-items: center; gap: 5px; }
        .fp-pulso { width: 7px; height: 7px; border-radius: 50%; background: #fff; box-shadow: 0 0 0 3px rgba(255,255,255,0.35); display: inline-block; flex: none; }
        .fp-todo { width: 100%; margin-top: 10px; display: flex; justify-content: space-between; align-items: center; gap: 8px; background: #f5f7f9; border: none; border-radius: 12px; padding: 9px 12px; font: 700 13px 'Inter', sans-serif; color: #1a4b8c; cursor: pointer; text-align: left; }
        .fp-todo .fp-todo-n { font-weight: 600; font-size: 11.5px; color: #6b7a85; white-space: nowrap; }
        .fp-todo.on { border-radius: 12px 12px 0 0; }
        .fd { background: #f5f7f9; padding: 0 12px; border-radius: 0 0 12px 12px; }
        .fd-lista { max-height: 206px; overflow-y: auto; padding-left: 3px; overscroll-behavior: contain; }
        .fd-fila { display: flex; align-items: center; gap: 8px; padding: 5px 0; border-bottom: 1px solid #e8ecef; font-size: 13px; }
        .fd-fila .fp-punto { width: 8px; height: 8px; }
        .fd-h { width: 58px; flex: none; font-variant-numeric: tabular-nums; }
        .fd-ac { flex: 1; min-width: 0; }
        .fd-fila.pasado { color: #a3adb4; }
        .fd-fila.pasado .fp-punto { opacity: .4; }
        .fd-fila .fp-ultimo { margin-left: 6px; }
        .fd-pasadas { display: block; width: 100%; background: none; border: none; font: 700 11.5px 'Inter', sans-serif; color: #1a4b8c; padding: 7px 0; text-align: center; cursor: pointer; }
        .fd-nada { font-size: 10.5px; color: #6b7a85; padding: 8px 0; }
        /* Etapa 97f (boceto 54b): «🚌 Ir» bajo los minutos del bus que viene, y el botón para volver a la parada */
        .fp-der { display: flex; flex-direction: column; align-items: flex-end; gap: 5px; flex: none; }
        .fp-ir { background: #25a55a; color: #fff; border: none; border-radius: 10px; padding: 4px 9px; font: 700 11.5px 'Inter', sans-serif; cursor: pointer; white-space: nowrap; }
        .fp-ir:active { transform: scale(.96); }
        .volver-parada-bus { position: fixed; left: 12px; bottom: calc(60px + env(safe-area-inset-bottom, 0px)); z-index: 1200; background: #1b2350; color: #fff; border: none; border-radius: 20px; padding: 9px 15px; font: 700 13px 'Inter', sans-serif; box-shadow: 0 3px 10px rgba(0,0,0,.25); cursor: pointer; max-width: calc(100vw - 24px); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
        .globo-llega { display: inline-block; margin-top: 4px; background: #e9f6ee; color: #1b5e20; font-weight: 700; border-radius: 8px; padding: 2px 7px; font-size: 12px; }

        /* ── Los letreros de aviso: el del tren (boceto 32) y el rojo (boceto 33) ── */
        .letrero-aviso { position: fixed; z-index: 1500; display: flex; align-items: center; gap: 10px; box-sizing: border-box;
            color: #fff; border-radius: 22px; padding: 7px 8px 7px 9px; font-family: 'Inter', sans-serif; }
        .letrero-aviso[hidden] { display: none; }
        .letrero-aviso.aparece { animation: vmApareceLetrero 0.35s ease; }
        @keyframes vmApareceLetrero { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
        .letrero-tren { background: var(--vm-texto, #1b2a33); box-shadow: 0 6px 18px rgba(0,0,0,0.28); }
        .letrero-rojo { background: #c62828; box-shadow: 0 6px 18px rgba(120,0,0,0.35); z-index: 1510; padding: 0 8px 0 0; }
        .letrero-aviso .la-cuerpo { flex: 1; min-width: 0; display: flex; align-items: center; gap: 10px; background: none; border: none; color: inherit; font: inherit; text-align: left; padding: 7px 0 7px 9px; cursor: pointer; }
        .letrero-aviso .la-ic { width: 30px; height: 30px; border-radius: 50%; background: #fff; display: grid; place-items: center; flex: none; overflow: hidden; }
        .letrero-aviso .la-ic img { width: 26px; height: 26px; object-fit: contain; }
        .letrero-rojo .la-ic { box-shadow: 0 0 0 4px rgba(255,255,255,0.28); animation: vmLatidoRojo 2s ease-in-out infinite; overflow: visible; }
        .letrero-rojo .la-ic img { width: 22px; height: 22px; }
        @keyframes vmLatidoRojo { 0%, 100% { box-shadow: 0 0 0 4px rgba(255,255,255,0.28); } 50% { box-shadow: 0 0 0 7px rgba(255,255,255,0.12); } }
        .letrero-aviso .la-txt { flex: 1; min-width: 0; line-height: 1.3; display: flex; flex-direction: column; }
        .letrero-aviso .la-l1 { font-size: 13.5px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
        .letrero-aviso .la-l2 { font-size: 12px; color: rgba(255,255,255,0.75); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
        .letrero-rojo .la-l1 { font-weight: 800; }
        .letrero-rojo .la-l2 { color: rgba(255,255,255,0.92); }
        .letrero-tren.dos .la-l1, .letrero-tren.dos .la-l2 { font-size: 12.5px; color: #fff; font-weight: 500; }
        .letrero-aviso .la-min { color: #8be08b; font-weight: 800; }
        .letrero-aviso .la-fl { display: inline-block; width: 14px; text-align: center; font-weight: 800; color: #ffd54f; }
        .letrero-aviso .la-x { width: 23px; height: 23px; border-radius: 50%; border: none; background: rgba(255,255,255,0.2); color: #fff; font-size: 13px; flex: none; cursor: pointer; display: grid; place-items: center; padding: 0; }
        .letrero-aviso .la-x:hover { background: rgba(255,255,255,0.32); }
        body.con-letreros-aviso .bus-nav-ciudad { display: none !important; }
        /* Las tarjetas grandes del tren, de antes, ya no se usan. */
        .contenedor-relojes-c1 { display: none !important; }

        /* ── La ficha del aviso rojo (boceto 34) ── */
        .velo-aviso-rojo { position: fixed; inset: 0; background: rgba(0,0,0,0.45); z-index: 2400; }
        .velo-aviso-rojo[hidden], .ficha-aviso-rojo[hidden] { display: none; }
        .ficha-aviso-rojo { position: fixed; z-index: 2410; background: #fff; border-radius: 18px; overflow: hidden; box-shadow: 0 20px 50px rgba(0,0,0,0.35);
            font-family: 'Inter', sans-serif; color: #1b2a33; display: flex; flex-direction: column;
            width: 440px; max-width: calc(100vw - 24px); left: 50%; top: 50%; transform: translate(-50%, -50%); max-height: 86vh; }
        @media (max-width: 719px) { .ficha-aviso-rojo { top: 60px; transform: translateX(-50%); max-height: calc(100vh - 80px); } }
        .ar-cab { background: #c62828; color: #fff; padding: 14px 52px 14px 16px; position: relative; display: flex; gap: 12px; align-items: center; flex: none; }
        .ar-ic { width: 44px; height: 44px; border-radius: 50%; background: #fff; display: grid; place-items: center; flex: none; box-shadow: 0 0 0 5px rgba(255,255,255,0.25); }
        .ar-ic img { width: 30px; height: 30px; }
        .ar-etq { font-size: 10.5px; font-weight: 800; letter-spacing: 0.08em; opacity: 0.9; }
        .ar-tit { font-size: 17px; font-weight: 800; line-height: 1.2; margin-top: 2px; }
        .ar-zona { font-size: 12.5px; opacity: 0.92; margin-top: 2px; }
        .ar-x { position: absolute; right: 12px; top: 12px; width: 30px; height: 30px; border-radius: 50%; border: none; background: rgba(255,255,255,0.22); color: #fff; font-size: 15px; cursor: pointer; }
        .ar-pest { display: flex; gap: 6px; padding: 10px 14px 0; flex: none; }
        .ar-pest button { flex: 1; text-align: center; padding: 7px 4px; border-radius: 10px; font-size: 12.5px; font-weight: 700; color: #8a2d2d; background: #fdecea; border: none; cursor: pointer; font-family: inherit; }
        .ar-pest button.on { background: #c62828; color: #fff; }
        .ar-cuerpo { padding: 12px 16px 14px; overflow-y: auto; font-size: 13.5px; line-height: 1.45; }
        .ar-sec { font-size: 11px; font-weight: 800; color: #6b7a85; letter-spacing: 0.04em; margin: 12px 0 5px; text-transform: uppercase; }
        .ar-sec:first-child { margin-top: 0; }
        .ar-cuando { display: flex; gap: 8px; }
        .ar-cuando div { flex: 1; background: #f5f7f9; border-radius: 10px; padding: 7px 10px; font-size: 12px; color: #55646e; }
        .ar-cuando b { display: block; font-size: 14px; color: #1b2a33; }
        .ar-estado { display: inline-flex; align-items: center; gap: 6px; background: #fdecea; color: #c62828; font-weight: 800; font-size: 12px; border-radius: 10px; padding: 3px 9px; margin-top: 7px; }
        .ar-estado i { width: 8px; height: 8px; border-radius: 50%; background: #e53935; box-shadow: 0 0 0 3px rgba(229,57,53,0.25); }
        .ar-dato { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
        .ar-dato b { font-size: 26px; color: #c62828; font-weight: 900; }
        .ar-dato span { color: #33424c; }
        .ar-texto { background: #fff8e1; border-radius: 10px; padding: 8px 10px; font-size: 12.5px; margin-top: 6px; color: #5d4a00; }
        .ar-prob { display: flex; gap: 4px; }
        .ar-prob span { flex: 1; text-align: center; font-size: 11.5px; padding: 5px 2px; border-radius: 8px; background: #f0f2f4; color: #8a959c; font-weight: 700; }
        .ar-prob span.on { background: #c62828; color: #fff; }
        .ar-hacer { background: #fdecea; border-left: 4px solid #c62828; border-radius: 8px; padding: 8px 10px; font-size: 13px; color: #4a1010; }
        .ar-112 { display: flex; align-items: center; gap: 10px; margin-top: 10px; background: #1b2a33; color: #fff; border-radius: 12px; padding: 9px 12px; font-size: 13px; text-decoration: none; }
        .ar-112 b { margin-left: auto; background: #c62828; border-radius: 9px; padding: 5px 12px; font-size: 15px; }
        .ar-fuente { font-size: 11px; color: #8a959c; margin-top: 10px; line-height: 1.5; }
        .ar-fuente a { color: #1a4b8c; font-weight: 700; text-decoration: none; }

        /* ═══ Etapa 72 (boceto 35): la ficha de los avisos amarillos y naranjas ═══ */
        /* En la lista de avisos: los de AEMET se pueden tocar y abren su ficha. */
        .item-anuncio.con-ficha { cursor: pointer; }
        .anuncio-ver-ficha { display: flex; width: fit-content; align-items: center; margin-top: 7px; font: inherit; font-size: 12.5px; font-weight: 800; color: #1a4b8c; background: #eef2f8; border: none; border-radius: 9px; padding: 4px 10px; cursor: pointer; }
        .anuncio-ver-ficha::after { content: ' ›'; white-space: pre; }
        .anuncio-ver-ficha:hover { background: #e1e8f3; }
        /* La ficha: la misma del aviso rojo, con el color de su nivel. */
        .ficha-aviso-rojo.nivel-naranja .ar-cab { background: #ef6c00; }
        .ficha-aviso-rojo.nivel-naranja .ar-estado { background: #fff3e0; color: #e65100; }
        .ficha-aviso-rojo.nivel-naranja .ar-estado i { background: #fb8c00; box-shadow: 0 0 0 3px rgba(251,140,0,0.25); }
        .ficha-aviso-rojo.nivel-naranja .ar-prob span.on { background: #ef6c00; }
        .ficha-aviso-rojo.nivel-naranja .ar-dato b { color: #e65100; }
        .ficha-aviso-rojo.nivel-naranja .ar-hacer { background: #fff3e0; border-left-color: #ef6c00; color: #4a2600; }
        .ficha-aviso-rojo.nivel-amarillo .ar-cab { background: #f9c23c; color: #3d2e00; }
        .ficha-aviso-rojo.nivel-amarillo .ar-x { background: rgba(0,0,0,0.12); color: #3d2e00; }
        .ficha-aviso-rojo.nivel-amarillo .ar-estado { background: #fff8e1; color: #8a6d00; }
        .ficha-aviso-rojo.nivel-amarillo .ar-estado i { background: #f9a825; box-shadow: 0 0 0 3px rgba(249,168,37,0.3); }
        .ficha-aviso-rojo.nivel-amarillo .ar-prob span.on { background: #f9a825; color: #3d2e00; }
        .ficha-aviso-rojo.nivel-amarillo .ar-dato b { color: #8a6d00; }
        .ficha-aviso-rojo.nivel-amarillo .ar-hacer { background: #fff8e1; border-left-color: #f9a825; color: #4a3b00; }
        /* Aún no ha empezado: «Empieza en…», en azul. */
        .ficha-aviso-rojo .ar-estado.futuro { background: #eef2f8; color: #1a4b8c; }
        .ficha-aviso-rojo .ar-estado.futuro i { background: #1a4b8c; box-shadow: 0 0 0 3px rgba(26,75,140,0.2); }

        /* ═══ Etapa 73 (boceto 36): los avisos de AEMET, uno por fenómeno ═══ */
        .aviso-tramos { margin: 6px 0 4px; display: flex; flex-direction: column; gap: 3px; }
        .aviso-tramo { display: flex; align-items: center; gap: 6px; font-size: 12.5px; color: #1B2430; }
        .aviso-tramo i { width: 9px; height: 9px; border-radius: 50%; flex: none; background: #f9c23c; }
        .aviso-tramo.naranja i { background: #ef6c00; }
        .aviso-tramo.rojo i { background: #c62828; }
        .aviso-tramo .niv { font-weight: 800; min-width: 62px; }
        .aviso-tramo .dat { font-weight: 800; margin-left: auto; padding-left: 8px; white-space: nowrap; }
        .aviso-texto { font-size: 12.5px; color: #333; }
        .aviso-cambio { display: inline-block; font-size: 10.5px; font-weight: 800; color: #8a6d00; background: #fff8e1; border-radius: 7px; padding: 1px 6px; margin-left: 6px; vertical-align: 1px; }
        .aviso-cambio.naranja { color: #e65100; background: #fff3e0; }
        .aviso-cambio.rojo { color: #c62828; background: #fdecea; }
        /* La ficha: una pestaña por tramo, del color de su nivel */
        .ficha-aviso-rojo .ar-pest button.ar-tramo { display: flex; flex-direction: column; align-items: center; gap: 1px; line-height: 1.2; color: #4a3b00; background: #f5f7f9; }
        .ficha-aviso-rojo .ar-pest button.ar-tramo small { font-size: 10.5px; font-weight: 700; opacity: 0.85; }
        .ficha-aviso-rojo .ar-pest button.ar-tramo.amarillo.on { background: #f9c23c; color: #3d2e00; }
        .ficha-aviso-rojo .ar-pest button.ar-tramo.naranja.on { background: #ef6c00; color: #fff; }
        .ficha-aviso-rojo .ar-pest button.ar-tramo.rojo.on { background: #c62828; color: #fff; }

        /* ═══ Etapa 74: la ficha de los avisos, más corta y con pulso ═══ */
        /* «Se esperan 20 L/m²», en una sola línea, con la cifra en grande y del color del nivel */
        .ar-dato { display: block; margin-top: 12px; font-size: 15px; font-weight: 700; color: #1b2a33; }
        .ar-dato b { font-size: 24px; line-height: 1.1; }
        /* El triángulo de la cabecera late: el rojo, más fuerte y más rápido; el amarillo, suave y lento */
        @keyframes arPulso {
            0% { box-shadow: 0 0 0 5px rgba(255,255,255,0.25), 0 0 0 5px var(--ar-pulso, rgba(255,255,255,0.7)); transform: scale(1); }
            30% { transform: scale(var(--ar-escala, 1.08)); }
            70% { box-shadow: 0 0 0 5px rgba(255,255,255,0.25), 0 0 0 var(--ar-radio, 18px) rgba(255,255,255,0); transform: scale(1); }
            100% { box-shadow: 0 0 0 5px rgba(255,255,255,0.25), 0 0 0 5px rgba(255,255,255,0); transform: scale(1); }
        }
        .ficha-aviso-rojo .ar-ic { animation: arPulso 1.3s ease-out infinite; --ar-pulso: rgba(255,255,255,0.85); --ar-radio: 18px; --ar-escala: 1.1; }
        .ficha-aviso-rojo.nivel-naranja .ar-ic { animation-duration: 2s; --ar-pulso: rgba(255,255,255,0.7); --ar-radio: 13px; --ar-escala: 1.06; }
        .ficha-aviso-rojo.nivel-amarillo .ar-ic { animation-duration: 2.8s; --ar-pulso: rgba(61,46,0,0.3); --ar-radio: 9px; --ar-escala: 1.03; }
        @media (prefers-reduced-motion: reduce) { .ficha-aviso-rojo .ar-ic { animation: none; } }
        /* El tren en Cullera: la segunda línea, en verde y negrita, algo más grande */
        .letrero-tren:not(.dos) .la-l2 .la-min { font-size: 13px; }

        /* ══════════════════════════════════════════════════════════════
           Etapa 77 — Viu Map protege a los menores (bocetos 38 y 41)
           ══════════════════════════════════════════════════════════════ */
        .menor-banda {
            display: flex; gap: 10px; align-items: center; margin: 10px 0 12px;
            background: linear-gradient(135deg, var(--vm-azul-oscuro), var(--vm-azul)); color: #fff;
            border-radius: var(--vm-radio-boton); padding: 12px;
        }
        .menor-banda-escudo { font-size: 24px; line-height: 1; }
        .menor-banda b { display: block; font-size: var(--vm-fs-cuerpo-destacado); }
        .menor-banda span { display: block; font-size: var(--vm-fs-caption); opacity: .92; line-height: 1.4; margin-top: 2px; }
        .menor-familia { background: var(--vm-fondo-calido); border: 1px solid var(--vm-dorado-suave); border-radius: var(--vm-radio-boton); padding: 12px; margin: 6px 0 12px; }
        .menor-familia-titulo { font-weight: 800; color: var(--vm-azul-oscuro); font-size: var(--vm-fs-subtitulo); margin-bottom: 6px; }
        .menor-familia p { font-size: var(--vm-fs-cuerpo); line-height: 1.5; margin-bottom: 6px; color: var(--vm-texto); }
        .menor-enlace-proteccion { display: block; text-align: center; margin: 12px 0 2px; font-size: var(--vm-fs-caption); color: var(--vm-azul); text-decoration: underline; }
        .menor-espera p { font-size: var(--vm-fs-cuerpo); line-height: 1.5; margin: 6px 0; color: var(--vm-texto); }
        .menor-estado { display: inline-block; background: #fff3e0; color: #a35b00; border-radius: 20px; padding: 4px 10px; font-size: var(--vm-fs-caption); font-weight: 700; margin-bottom: 6px; }
        .menor-caja-azul { background: var(--vm-azul-suave); border-radius: var(--vm-radio-boton); padding: 11px 12px; margin: 10px 0; font-size: var(--vm-fs-cuerpo); line-height: 1.45; color: var(--vm-texto); }
        .menor-fila-dato { display: flex; justify-content: space-between; font-size: var(--vm-fs-cuerpo); padding: 8px 0; border-bottom: 1px solid #f0f1f4; }
        .menor-peq { font-size: var(--vm-fs-caption) !important; color: var(--vm-texto-suave) !important; }
        .ficha-proteccion-menor { font-size: var(--vm-fs-caption); color: var(--vm-texto-suave); }
        .ficha-proteccion-menor a { color: var(--vm-azul); }
        /* El modal de los avisos (permiso, 16, 18 y versión nueva) */
        .ficha-aviso-menor { max-width: 360px; padding: 24px 20px 18px; background: linear-gradient(180deg, var(--vm-azul-suave) 0%, #fff 40%); }
        .ficha-aviso-menor .am-logo { display: block; width: 130px; margin: 0 auto 6px; }
        .ficha-aviso-menor .am-icono { font-size: 34px; text-align: center; margin: 2px 0 4px; }
        .ficha-aviso-menor .am-titulo { font-size: var(--vm-fs-titulo); font-weight: 800; color: var(--vm-azul-oscuro); text-align: center; margin-bottom: 8px; }
        .ficha-aviso-menor p { font-size: var(--vm-fs-cuerpo); line-height: 1.5; color: var(--vm-texto); margin-bottom: 8px; }
        .ficha-aviso-menor .am-subtitulo { font-weight: 800; color: var(--vm-azul-oscuro); font-size: var(--vm-fs-cuerpo-destacado); margin: 10px 0 4px; }
        .ficha-aviso-menor .am-lista { list-style: none; padding: 0; margin: 0 0 6px; }
        .ficha-aviso-menor .am-lista li { position: relative; padding: 4px 0 4px 24px; font-size: var(--vm-fs-cuerpo); line-height: 1.4; }
        .ficha-aviso-menor .am-lista li.si::before { content: "✔"; position: absolute; left: 2px; color: #2e7d32; font-weight: 800; }
        .ficha-aviso-menor .am-lista li.no::before { content: "🛡️"; position: absolute; left: 0; font-size: 12px; top: 5px; }
        .ficha-aviso-menor .am-caja { background: var(--vm-fondo-calido); border: 1px solid var(--vm-dorado-suave); border-radius: var(--vm-radio-boton); padding: 10px 12px; font-size: var(--vm-fs-cuerpo); line-height: 1.45; margin: 8px 0; }
        .ficha-aviso-menor .am-acepto { display: flex; gap: 8px; align-items: flex-start; font-size: var(--vm-fs-cuerpo); margin: 10px 0 0; line-height: 1.4; }
        .ficha-aviso-menor .am-acepto input { margin-top: 2px; width: 18px; height: 18px; flex-shrink: 0; }
        .ficha-aviso-menor .am-enlaces { text-align: center; font-size: var(--vm-fs-caption); }
        .ficha-aviso-menor a { color: var(--vm-azul); }
        .ficha-aviso-menor p.am-centro { text-align: center; }
        .ficha-aviso-menor .am-peq { font-size: var(--vm-fs-caption); color: var(--vm-texto-suave); text-align: center; margin: 10px 0 0; }
        .ficha-aviso-menor .vc-btn:disabled { background: #bbb; cursor: default; }
        #fondoAvisoMenor { z-index: 3200; }

        /* ── Etapa 79 (bocetos 42 a 44, guía v14): el estado del bus ── */
        /* Sin señal: gris, sin flecha y sin etiqueta (decisión de Jaime, 23/10/2026; puede cambiar). */
        .bus-viu.sin-senal .bus-viu-aro { border-color: #9aa0a6; box-shadow: 0 1px 3px rgba(0,0,0,0.25); }
        .bus-viu.sin-senal .bus-viu-flecha { display: none; }
        .bus-viu.sin-senal .bus-viu-cara { filter: grayscale(1); opacity: .55; }
        .bus-viu.sin-senal .bus-viu-retraso { display: none; }
        /* Hacia su salida: atenuado, aro a trazos y «Sale 12:38». */
        .bus-viu.hacia-salida { opacity: .55; }
        .bus-viu.hacia-salida .bus-viu-aro { border-style: dashed; }
        /* La etiqueta pequeña: «Sale 12:38» (azul) o «Último» (negro). */
        .bus-viu-estado { display: none; position: absolute; left: 25px; top: 3px; color: #fff; font-size: 10px; font-weight: 800; line-height: 14px; border-radius: 7px; padding: 0 5px; white-space: nowrap; box-shadow: 0 1px 3px rgba(0,0,0,0.35); pointer-events: none; font-family: 'Inter', sans-serif; }
        .bus-viu.hacia-salida-etq .bus-viu-estado { display: block; background: var(--color-ruta, #1a4b8c); }   /* Etapa 97d: el color de la ruta que va a hacer */
        .bus-viu.ultimo .bus-viu-estado { display: block; background: #1b1b1b; }
        .bus-viu.ultimo.retrasado .bus-viu-retraso { top: 19px; }
        /* Etapa 97 (boceto 53c): el que espera su salida, «Sale 07:40» en el color del sentido con el punto de «En directo». */
        .bus-viu.espera-etq .bus-viu-estado { display: block; background: var(--color-ruta); }
        .bus-viu-vivo { display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: #fff; box-shadow: 0 0 0 2px rgba(255,255,255,0.35); margin-right: 4px; vertical-align: 1px; }
        /* Etapa 97: el letrero nunca tapa la flecha. Vertical: a la izquierda. Horizontal o diagonal: debajo; si baja, encima. */
        .bus-viu.etq-izq .bus-viu-estado, .bus-viu.etq-izq .bus-viu-retraso { left: auto; right: 25px; }
        .bus-viu.etq-abajo .bus-viu-estado, .bus-viu.etq-abajo .bus-viu-retraso { left: 50%; top: 31px; bottom: auto; transform: translateX(-50%); }
        .bus-viu.etq-arriba .bus-viu-estado, .bus-viu.etq-arriba .bus-viu-retraso { left: 50%; top: auto; bottom: 31px; transform: translateX(-50%); }
        .bus-viu.etq-der .bus-viu-estado, .bus-viu.etq-der .bus-viu-retraso { left: 25px; right: auto; top: 3px; bottom: auto; transform: none; }   /* Etapa 97d: a la derecha, si otro bus ocupa los demás lados */
        .bus-viu.etq-der.con-estado.retrasado .bus-viu-retraso { top: 19px; }
        .bus-viu.etq-izq.con-estado.retrasado .bus-viu-retraso { top: 19px; }
        .bus-viu.etq-abajo.con-estado.retrasado .bus-viu-retraso { top: 49px; }
        .bus-viu.etq-arriba.con-estado.retrasado .bus-viu-retraso { top: auto; bottom: 49px; }
        /* «Último» en el reloj, la ficha y la guía. */
        .reloj-ultimo { display: inline-block; border: 1px solid rgba(255,255,255,0.7); border-radius: 7px; padding: 0 5px; font-size: 10px; font-weight: 700; line-height: 13px; text-transform: uppercase; letter-spacing: .3px; }
        .fp-ultimo { display: inline-block; background: #1b1b1b; color: #fff; border-radius: 7px; padding: 0 6px; font-size: 10.5px; font-weight: 700; line-height: 15px; vertical-align: 1px; white-space: nowrap; }
        /* Etapa 101 (boceto 57b): la etiqueta «Mañana» del siguiente bus, en la ficha de la parada. */
        .fp-manana { display: inline-block; margin-right: 1px; background: #eef2f8; color: #1a4b8c; border-radius: 7px; padding: 0 6px; font-size: 10.5px; font-weight: 700; line-height: 15px; vertical-align: 1px; white-space: nowrap; }
        @media (hover: none) and (pointer: coarse) {
            .bus-viu-estado { font-size: 9px; line-height: 12px; padding: 0 4px; left: 24px; top: 4px; }
            .bus-viu.ultimo.retrasado .bus-viu-retraso { top: 17px; }
        }

        /* ── Etapa 80b (bocetos 45 y 46): «Powered by: NexIABus», al final de la ficha de la parada y del horario ── */
        .bus-powered { margin-top: 12px; padding-top: 9px; border-top: 1px solid #f0f2f4; text-align: center; font-size: 11.5px; color: #6B7280; font-family: 'Inter', sans-serif; }
        .bus-powered a { color: #1a4b8c; font-weight: 700; text-decoration: none; }
        /* ── Etapa 80d (boceto 47): el globo del bus y el de la parada, con la letra y el acabado del resto del bus ── */
        .globo-bus-viu .leaflet-popup-content-wrapper, .globo-parada-bus .leaflet-popup-content-wrapper { border-radius: 14px; box-shadow: 0 6px 20px rgba(0,0,0,0.22); }
        .globo-bus-viu .leaflet-popup-content, .globo-parada-bus .leaflet-popup-content { font-family: 'Inter', sans-serif; font-size: 13px; line-height: 1.4; color: #4b5563; }
        .globo-bus-viu .leaflet-popup-content b, .globo-parada-bus .leaflet-popup-content b { color: #1b1b1b; font-weight: 800; }
        .globo-bus-viu .globo-bus-etiqueta { color: #6b7a85; font-size: 0.92em; font-weight: 600; }
        @media (hover: none) and (pointer: coarse) {
            .globo-bus-viu .leaflet-popup-content, .globo-parada-bus .leaflet-popup-content { font-size: 12px; }
        }

        /* ── Etapa 82 (boceto 48): la fila de iconos de la ventana del teléfono, centrada a media altura ── */
        .ficha-aviso-menor .am-icono.fila-icono { display: flex; justify-content: center; align-items: center; gap: 4px; height: 48px; }
        .ficha-aviso-menor .fila-icono span { line-height: 1; display: block; }
        .ficha-aviso-menor .fila-icono .ic-grande { width: 56px; height: 56px; display: block; margin-left: -8px; }
        .ficha-aviso-menor .fila-icono .ic-moneda { width: 38px; height: 38px; display: block; }

        /* ══════════════════════════════════════════════════════════════
           Etapa 87 (24 de octubre de 2026) — Subir al bus (boceto 49)
           ══════════════════════════════════════════════════════════════ */
        /* Vas en el bus: aro verde con pulso en el bus y en el botón de arriba.
           La cola sigue AZUL para todos los buses (no se toca). */
        .bus-viu.a-bordo .bus-viu-aro { animation: vmPulsoABordo 1.4s ease-in-out infinite; }
        @keyframes vmPulsoABordo {
            0%, 100% { box-shadow: 0 0 0 3px #2fbf5b, 0 0 0 4px rgba(52,199,89,0.5), 0 2px 6px rgba(0,0,0,0.35); }
            50% { box-shadow: 0 0 0 3px #2fbf5b, 0 0 0 11px rgba(52,199,89,0), 0 2px 6px rgba(0,0,0,0.35); }
        }
        .btn-bus-nav.viaje-activo { border-radius: 50%; animation: vmPulsoBotonViaje 1.4s ease-in-out infinite; }
        @keyframes vmPulsoBotonViaje {
            0%, 100% { box-shadow: 0 0 0 3px #2fbf5b, 0 0 0 4px rgba(52,199,89,0.5); }
            50% { box-shadow: 0 0 0 3px #2fbf5b, 0 0 0 10px rgba(52,199,89,0); }
        }
        /* Tu punto negro se esconde mientras vas dentro (y su halo). */
        body.viaje-en-bus .contenedor-usuario { visibility: hidden; }
        body.viaje-en-bus .leaflet-panelHaloGps-pane { display: none; }
        /* Las tarjetas «¿Dónde te bajas?» y «Tu viaje»: centradas entre la
           columna y el mando de zoom (las coloca map_tmp.js), con la letra
           de la ficha de la parada. */
        .tarjeta-viaje-bus { position: fixed; z-index: 1590; background: #fff; border-radius: 20px; box-shadow: 0 10px 34px rgba(0,0,0,0.28);
            padding: 13px 13px 11px; font-family: 'Inter', sans-serif; color: #1b2a33; box-sizing: border-box; overflow-y: auto; }
        .tarjeta-viaje-bus[hidden], .barra-viaje-bus[hidden] { display: none; }
        .tv-cab { display: flex; gap: 10px; align-items: center; padding-right: 30px; }
        .tv-cab img { width: 34px; height: 36px; object-fit: contain; flex: none; }
        .tv-tit { font-size: 16px; font-weight: 800; line-height: 1.15; }
        .tv-sub { font-size: 11.5px; color: #6b7a85; display: flex; align-items: center; gap: 6px; margin-top: 2px; }
        .tv-ruta { font-size: 12px; color: #6b7a85; font-weight: 600; margin-top: 10px; }
        .tv-ruta b { display: block; color: #1b1b1b; font-weight: 800; font-size: 13.5px; margin-top: 1px; }
        .tv-bajas { background: #eef8f1; border-radius: 12px; padding: 8px 10px; margin-top: 9px; font-size: 13px; line-height: 1.35; }
        .tv-bajas small { display: block; color: #55646e; font-size: 11.5px; margin-top: 1px; }
        .tv-texto { font-size: 12.5px; line-height: 1.4; color: #33424c; margin-top: 8px; }
        .tv-lista { margin-top: 6px; max-height: 236px; overflow-y: auto; position: relative; }
        .tv-par { display: flex; width: 100%; align-items: center; gap: 9px; padding: 5px 0; border: none; border-bottom: 1px solid #f0f2f4; background: none;
            font: inherit; font-size: 12.5px; line-height: 1.25; color: inherit; text-align: left; cursor: pointer; }
        .tv-par:last-child { border-bottom: none; }
        .tv-punto { width: 10px; height: 10px; border-radius: 50%; flex: none; background: #fff; box-shadow: 0 0 0 2px #c62828; }
        .tv-nom { flex: 1; min-width: 0; }
        .tv-par .fp-tiempo { font-size: 13px; }
        .tv-par.pasada { color: #a7b0b7; cursor: default; }
        .tv-par.pasada .tv-punto { background: #d7dce0; box-shadow: none; }
        .tv-par.sigue .tv-nom { font-weight: 800; }
        .tv-par.sigue .tv-punto { background: #34c759; box-shadow: 0 0 0 1.5px #fff, 0 0 0 2.5px rgba(0,0,0,0.12); }
        .tv-par.mia .tv-nom { font-weight: 800; color: #1a4b8c; }
        .tv-par.mia .tv-punto { background: #1a4b8c; box-shadow: 0 0 0 1.5px #fff, 0 0 0 2.5px rgba(0,0,0,0.12); }
        .tv-pill-bajas { display: inline-block; background: #1a4b8c; color: #fff; border-radius: 7px; padding: 0 6px; font-size: 10.5px; font-weight: 700; line-height: 16px; white-space: nowrap; margin-left: 6px; }
        .tv-ayuda { font-size: 11px; color: #8a959c; text-align: center; margin-top: 6px; }
        .tarjeta-viaje-bus .fp-botones { margin-top: 10px; }
        .tarjeta-viaje-bus .fp-botones button.tv-prim { background: #1a4b8c; color: #fff; }
        .tarjeta-viaje-bus .fp-botones button:disabled { opacity: 0.5; cursor: default; }
        .tarjeta-viaje-bus .bus-powered { margin-top: 8px; padding-top: 7px; }
        /* La barra de abajo mientras viajas. */
        .barra-viaje-bus { position: fixed; left: 12px; right: 12px; bottom: 46px; z-index: 1550; max-width: 480px; margin: 0 auto; background: #fff; border-radius: 16px;
            box-shadow: 0 8px 26px rgba(0,0,0,0.26); display: flex; align-items: center; gap: 10px; padding: 9px 12px; box-sizing: border-box;
            font-family: 'Inter', sans-serif; color: #1b2a33; cursor: pointer; }
        .barra-viaje-bus img { width: 26px; height: 28px; object-fit: contain; flex: none; }
        .barra-viaje-bus .bv-t { flex: 1; min-width: 0; font-size: 13px; line-height: 1.3; }
        .barra-viaje-bus .bv-t small { display: flex; align-items: center; gap: 5px; color: #6b7a85; font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
        .barra-viaje-bus .bv-ver { background: #eef2f8; color: #1a4b8c; font-weight: 700; font-size: 12.5px; border-radius: 10px; padding: 7px 10px; flex: none; }
        /* Los avisos: el letrero oscuro de los avisos, con el aro verde. */
        .letrero-viaje-bus .la-ic-viaje { box-shadow: 0 0 0 3px #2fbf5b; }
        .letrero-viaje-bus .la-ic-viaje img { width: 22px; height: 22px; }
        .letrero-viaje-bus .la-l1, .letrero-viaje-bus .la-l2 { white-space: normal; }
        .letrero-viaje-bus .la-l1 b { font-weight: 800; }

/* ── Etapa 93a: aviso antes del permiso de ubicación (misma línea que las tarjetas de la bienvenida) ── */
.au-raiz { position: fixed; inset: 0; z-index: 6900; font-family: 'Inter', -apple-system, 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif; color: #1B2430; }
.au-raiz .au-velo { position: absolute; inset: 0; background: rgba(14, 34, 38, 0.55); display: flex; align-items: center; justify-content: center; padding: 12px; }
.au-raiz .au-tarjeta { background: #fff; border-radius: 18px; box-shadow: 0 14px 44px rgba(0,0,0,.28); width: 100%; max-width: 380px; max-height: calc(100dvh - 24px); overflow-y: auto; padding: 20px 24px 14px; text-align: center; }
.au-raiz .au-icono { width: 64px; height: 64px; object-fit: contain; display: block; margin: 2px auto 6px; }
.au-raiz h2 { font-size: 21px; font-weight: 800; color: #111; margin: 4px 0 12px; line-height: 1.2; }
.au-raiz .au-texto { font-size: 14.5px; color: #666; line-height: 1.45; margin: 0 6px 12px; }
.au-raiz .au-texto b { color: #333; }
.au-raiz .au-peq { font-size: 13px; }
.au-raiz .au-btn { border: none; border-radius: 9px; color: #fff; background: #D79109; font: 700 16px 'Inter', sans-serif; padding: 13px 8px; cursor: pointer; width: 86%; margin: 6px auto 0; display: block; box-shadow: 0 2px 5px rgba(0,0,0,.15); }
.au-raiz .au-btn:hover { background: #c18207; }
.au-raiz .au-enlace { display: inline-block; margin-top: 10px; font-size: 13px; color: #2E7D32; cursor: pointer; background: none; border: none; font-family: inherit; }
.au-raiz .au-nota { font-size: 11.5px; color: #888; margin: 10px 8px 0; }
.au-raiz .au-nota a { color: inherit; }
.au-raiz .au-motivo { background: #FFF6E0; border-radius: 12px; padding: 10px 12px; text-align: left; font-size: 14px; line-height: 1.45; margin: 4px 0 12px; color: #333; }
#viuAvisoCuenta { z-index: 7100; }   /* Etapa 93a: por encima de la bienvenida */
.au-raiz .au-emoji { font-size: 42px; line-height: 1; margin: 4px 0 6px; }
