/* ═══════════════════════════════════════════════════════════════════
   Widget de accesibilidad — v1 (5 toggles)
   Cargado desde portal/base.html. Coexiste con el theme institucional
   del tenant (--c-primary, --c-bg, etc.) y los toma del :root.
   ═══════════════════════════════════════════════════════════════════ */

.a11y-widget {
    position: fixed;
    left: 1.25rem;
    bottom: 1.25rem;
    z-index: 9990;          /* arriba de la mayoría del contenido del
                               portal, pero abajo del lightbox (z=10000)
                               para no taparlo cuando un visitante lo
                               abre desde una galería. */
}

/* ── FAB (botón flotante) ──────────────────────────────────────── */

.a11y-fab {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    border: 0;
    background: var(--c-primary, #13304D);
    color: var(--c-text-light, #FFFFFF);
    cursor: pointer;
    box-shadow: 0 .55rem 1.2rem rgba(0, 0, 0, .22);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: transform .15s ease, box-shadow .15s ease,
                background .15s ease;
}
.a11y-fab:hover,
.a11y-fab:focus-visible {
    transform: translateY(-2px) scale(1.03);
    box-shadow: 0 .85rem 1.6rem rgba(0, 0, 0, .28);
    outline: none;
}
.a11y-fab-icono {
    width: 30px;
    height: 30px;
}

/* Cuando el FAB queda visualmente sobre un fondo del mismo color que él
   (--c-primary) — el footer institucional, o cualquier zona del
   page-builder con "Fondo: color de marca" — invertimos la pareja de
   colores: pasamos a fondo claro (--c-bg) con ícono primario. Sin esto,
   el botón "desaparece" contra un fondo del mismo tono. El nombre de la
   clase (`is-over-footer`) quedó del primer alcance (solo footer); el JS
   la aplica hoy contra cualquier fondo que matchee, no solo el footer. */
.a11y-widget.is-over-footer .a11y-fab {
    background: var(--c-bg, #FFFFFF);
    color: var(--c-primary, #13304D);
}

/* ── Panel ─────────────────────────────────────────────────────── */

.a11y-panel {
    position: absolute;
    bottom: 70px;
    left: 0;
    width: 340px;
    max-height: 75vh;
    background: var(--c-bg-card, #FFFFFF);
    color: var(--c-text, #1f2937);
    border-radius: 14px;
    box-shadow: 0 1.25rem 2.5rem rgba(0, 0, 0, .22);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    animation: a11y-panel-aparecer .18s ease-out;
}
.a11y-panel[hidden] { display: none; }
@keyframes a11y-panel-aparecer {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0); }
}

.a11y-panel-header {
    background: var(--c-primary, #13304D);
    color: var(--c-text-light, #FFFFFF);
    padding: 1rem 1.15rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-shrink: 0;
}
.a11y-panel-titulo {
    margin: 0;
    font-size: 1.15rem;
    font-weight: 700;
    display: inline-flex;
    align-items: center;
    gap: .55rem;
}
.a11y-panel-titulo-icono {
    background: var(--c-text-light, #FFFFFF);
    color: var(--c-primary, #13304D);
    width: 28px;
    height: 28px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.a11y-panel-titulo-icono svg { width: 18px; height: 18px; }
.a11y-panel-acciones {
    display: flex;
    gap: .3rem;
}
.a11y-panel-accion {
    background: transparent;
    border: 0;
    color: var(--c-text-light, #FFFFFF);
    width: 32px;
    height: 32px;
    border-radius: 6px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background .12s ease;
}
.a11y-panel-accion:hover,
.a11y-panel-accion:focus-visible {
    background: rgba(255, 255, 255, .18);
    outline: none;
}
.a11y-panel-accion svg { width: 18px; height: 18px; }

.a11y-panel-body {
    padding: .85rem 1rem 1rem;
    overflow-y: auto;
    flex: 1 1 auto;
    min-height: 0;
}

.a11y-grupo + .a11y-grupo { margin-top: 1rem; }
.a11y-grupo-titulo {
    margin: 0 0 .55rem;
    font-size: .82rem;
    font-weight: 600;
    color: var(--c-muted, #6b7280);
    text-transform: uppercase;
    letter-spacing: .03em;
}
.a11y-grupo-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: .55rem;
}

.a11y-card {
    background: var(--c-bg-subtle, #f3f4f6);
    border: 1px solid transparent;
    border-radius: 10px;
    padding: .75rem .5rem;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .35rem;
    color: var(--c-text, #1f2937);
    text-align: center;
    transition: background .12s ease, border-color .12s ease,
                transform .08s ease;
}
.a11y-card:focus-visible {
    outline: 2px solid var(--c-primary, #13304D);
    outline-offset: 2px;
}
.a11y-card:active {
    transform: scale(.97);
}
.a11y-card.is-activo {
    background: var(--c-primary, #13304D);
    color: var(--c-text-light, #FFFFFF);
    border-color: var(--c-primary, #13304D);
}
.a11y-card-glyph {
    font-size: 1.4rem;
    line-height: 1;
    font-weight: 700;
}
.a11y-card-label {
    font-size: .82rem;
    line-height: 1.25;
    font-weight: 500;
}
.a11y-card-nivel {
    font-size: .72rem;
    opacity: .8;
    font-variant-numeric: tabular-nums;
}

.a11y-panel-footer {
    border-top: 1px solid var(--c-border-light, #e5e7eb);
    padding: .75rem 1rem;
    text-align: center;
    flex-shrink: 0;
}
.a11y-panel-link-decl {
    color: var(--c-primary, #13304D);
    font-size: .85rem;
    text-decoration: underline;
}

/* ═══════════════════════════════════════════════════════════════════
   TOGGLES — reglas que aplican al portal cuando hay una clase
   `a11y-*` en el <body>. Todo usa `:where()` para no inflar la
   especificidad y dejar que el operador siga pudiendo customizar
   bloques sin pelearse con !important.
   ═══════════════════════════════════════════════════════════════════ */

/* 1. Tamaño del texto — NO se maneja con clase. El JS setea
   `document.documentElement.style.fontSize = "110%"` (mismo patrón
   que el slider de gestión, ver `templates/setting.html` +
   `font-scale-overrides.css`). Todo lo que use `rem`/`em` en el
   portal escala automáticamente; lo que use `px` queda fijo. */

/* 2. Altura de línea — tres pasos para texto de lectura larga.
   Apuntamos al cuerpo de contenido (no a títulos ni navbar) para
   no romper el ritmo visual del chrome del sitio. */
body.a11y-lh-150 :where(.portal-main, .portal-texto, .portal-texto-cuerpo,
                       .portal-decl-a11y, .portal-card-descripcion,
                       .portal-acordeon-body-inner) { line-height: 1.5; }
body.a11y-lh-175 :where(.portal-main, .portal-texto, .portal-texto-cuerpo,
                       .portal-decl-a11y, .portal-card-descripcion,
                       .portal-acordeon-body-inner) { line-height: 1.75; }
body.a11y-lh-200 :where(.portal-main, .portal-texto, .portal-texto-cuerpo,
                       .portal-decl-a11y, .portal-card-descripcion,
                       .portal-acordeon-body-inner) { line-height: 2; }

/* 3. Fuente legible — swap a Verdana (universal, espaciado generoso,
   sin serifas finas que confunden a lectores con dislexia). Forzamos
   el override en TODO descendiente con `*` + `inherit` porque muchos
   bloques setean su propia font-family. El widget propio queda en
   su font normal (no aplica el override porque vive fuera de .portal-main). */
body.a11y-readable-font :where(.portal-main, .portal-navbar, .portal-topbar,
                                .portal-decl-a11y, footer) {
    font-family: Verdana, Tahoma, 'Trebuchet MS', Arial, sans-serif !important;
    letter-spacing: 0.02em;
    word-spacing: 0.04em;
}
body.a11y-readable-font :where(.portal-main, .portal-navbar, .portal-topbar,
                                .portal-decl-a11y, footer) :where(*) {
    font-family: inherit !important;
}

/* 3.b Fuente para dislexia (MHF-HU042) — OpenDyslexic real, no una
   sans-serif del sistema como "Fuente legible". El @font-face NO vive
   acá (este CSS se sirve siempre) — lo inyecta el JS bajo demanda,
   solo la primera vez que este toggle se activa
   (`asegurarFuenteDislexia` en accesibilidad-widget.js), así el
   .woff2 nunca se descarga para quien no lo pidió.

   A propósito va DESPUÉS de "Fuente legible" en la hoja: si un
   visitante llegara a activar los dos toggles a la vez, con igual
   especificidad y ambos `!important`, gana la regla que aparece
   último — priorizamos el soporte real de dislexia por sobre el
   ajuste genérico. */
body.a11y-dyslexic-font :where(.portal-main, .portal-navbar, .portal-topbar,
                                .portal-decl-a11y, footer) {
    font-family: 'OpenDyslexic', Verdana, Tahoma, sans-serif !important;
    letter-spacing: 0.01em;
}
body.a11y-dyslexic-font :where(.portal-main, .portal-navbar, .portal-topbar,
                                .portal-decl-a11y, footer) :where(*) {
    font-family: inherit !important;
}

/* 4. Contraste — tres modos. Sobrescribimos las variables --c-* del
   tema solo cuando el modo está activo, así afecta a todo el stack
   (bloques, navbar, footer) sin tener que tocar cada CSS individual.

   Cada modo apunta a un usuario distinto:
     - "claro": refuerza WCAG dentro del esquema institucional claro.
     - "oscuro": modo nocturno con alto contraste interno.
     - "alto":  máximo contraste, blanco/negro puro + bordes fuertes.
*/
/* Los tres modos juegan con dos ejes:
     1) Lienzo del contenido (--c-bg / --c-bg-card / --c-bg-subtle):
        cambia entre claro y oscuro.
     2) Texto del cuerpo (--c-text y derivados):
        se invierte para mantener contraste con el lienzo.

   El chrome institucional (hero/navbar/topbar pintado con --c-primary
   y --c-accent) NO se toca: sigue usando --c-text-light para su texto,
   variable que NO cambia entre modos. Por eso ya no hace falta el
   override defensivo sobre navbar/hero que tenía la versión anterior. */

body.a11y-contrast-claro {
    --c-bg:           #FFFFFF;
    --c-bg-card:      #FFFFFF;
    --c-bg-subtle:    #F3F4F6;
    --c-text:         #111111;
    --c-text-on-light:#111111;
    --c-text-soft:    #1F2937;
    --c-text-strong:  #000000;
    /* Más oscuro que el gris "tibio" por defecto — la referencia contra
       la que se comparó (UserWay) usa mucho más contraste en su modo
       claro; #4B5563 pasaba WCAG AA pero se sentía como poca diferencia
       frente al modo normal. */
    --c-muted:        #374151;
    --c-border:       #4B5563;
    --c-border-light: #6B7280;
    /* En claro mantenemos texto-marca = primary (el patrón institucional
       normal). Solo subimos el contraste del cuerpo, no del branding. */
    --c-text-brand:   var(--c-primary);
}

body.a11y-contrast-oscuro {
    /* Lienzo en gris neutro casi negro — NO navy. Si usáramos un dark
       basado en primary, el navbar/hero (pintado con --c-primary) se
       funde con el fondo del cuerpo y se pierde la separación visual.
       Con un gris neutro muy oscuro el primary institucional sigue
       identificable como "chrome".

       Los tonos siguen la escala Material dark partiendo más profundo
       que el default (#121212) — bajamos a #0d0d0d para abrir más
       contraste con el primary del tenant (que típicamente está en
       el rango #15-25 de luminancia). La diferencia entre
       lienzo/card/subtle es ~6% de luminancia, lo justo para que los
       paneles se despeguen sin pelearse con el chrome. */
    --c-bg:           #0d0d0d;
    --c-bg-card:      #1c1c1c;
    --c-bg-subtle:    #262626;
    /* En oscuro, el "texto del cuerpo" pasa de oscuro a claro. Reusamos
       la variable --c-text-light que ya es la polaridad clara — así no
       inventamos un tercer color, mantenemos la pareja semántica. */
    --c-text:         var(--c-text-light);
    --c-text-on-light:var(--c-text-light);
    --c-text-soft:    #E5E7EB;
    --c-text-strong:  #FFFFFF;
    /* Más claro que el gris apagado por defecto (#B0B0B0) — mismo
       motivo que en "claro": la referencia (UserWay) usa un salto de
       contraste mucho más marcado. #B0B0B0 pasaba WCAG pero se leía
       "tibio" al lado de fondo casi negro. */
    --c-muted:        #D1D5DB;
    --c-border:       #525252;
    --c-border-light: #333333;
    /* Texto de marca pasa a text-light (no accent) — la consigna del
       modo oscuro es priorizar contraste WCAG sobre identidad de marca.
       El accent queda reservado SOLO para hover en elementos
       clicleables, como única señal de "esto es interactivo" que
       sobrevive cuando todos los textos son del mismo color. */
    --c-text-brand:   var(--c-text-light);
    background: #0d0d0d;
    color: var(--c-text-light);
}
body.a11y-contrast-oscuro :where(.portal-main, .portal-body, footer) {
    background-color: var(--c-bg) !important;
    color: var(--c-text);
}

/* ── Modo oscuro: forzar todos los textos a text-light ────────────
   El usuario pidió que en dark mode todos los textos abandonen
   primary/text-brand y vayan a text-light, aceptando explícitamente
   que el diseño se vuelva "soso" a cambio de cumplir contraste. Esta
   regla barre el portal completo (main + footer + navbar) y aplica
   text-light a todos los nodos textuales típicos.

   No usamos `:where()` porque necesitamos especificidad para ganarle
   a las reglas de bloque que ya tienen color (ej. `.portal-card-titulo
   { color: var(--c-text-brand) }`). Con
   `body.a11y-contrast-oscuro :is(...) :is(...)` quedamos en (0,2,2) —
   beats todas las reglas de bloque que usan un solo ancestor. */
body.a11y-contrast-oscuro :is(.portal-main, .portal-navbar, footer) :is(
    h1, h2, h3, h4, h5, h6,
    p, a, li, td, th, span, div,
    button, label, small, strong, em, b, i,
    dt, dd, blockquote, summary, figcaption
) {
    color: var(--c-text-light);
}

/* Hover en elementos clicleables → accent. Es la única excepción al
   "todo blanco" y sirve de affordance: si al pasar el mouse el texto
   cambia, el visitante entiende que el elemento es interactivo. */
body.a11y-contrast-oscuro :is(.portal-main, .portal-navbar, footer)
    :is(a, button):hover,
body.a11y-contrast-oscuro :is(.portal-main, .portal-navbar, footer)
    :is(a, button):focus-visible {
    color: var(--c-accent);
}

/* Badges con fondo claro hardcoded (`.portal-material-badge--*`):
   tienen fondos pastel y texto oscuro. El sweep general les pisó el
   texto a blanco, que no contrasta con el fondo pastel. Acá los
   reinvertimos al patrón "fondo oscuro + texto color claro" típico
   de dark themes: mantiene la codificación semántica (rojo=PDF,
   azul=imagen, neutro=otros) y restaura contraste. La especificidad
   `body.<class> .portal-main .portal-material-badge--<x>` queda en
   (0,3,1), gana contra el sweep (0,2,2). */
body.a11y-contrast-oscuro .portal-main .portal-material-badge--pdf {
    background: rgba(220, 38, 38, 0.18);
    color: #fca5a5;
}
body.a11y-contrast-oscuro .portal-main .portal-material-badge--imagen {
    background: rgba(37, 99, 235, 0.20);
    color: #93c5fd;
}
body.a11y-contrast-oscuro .portal-main .portal-material-badge--otro {
    background: rgba(255, 255, 255, 0.10);
    color: var(--c-text-light);
}

/* Bloques con "fondo suave" (tinte claro de --c-primary/--c-accent,
   ej. `--fondo-primary-suave`): ese fondo NO se oscurece en ningún
   modo de contraste (solo "alto" lo hace, forzando primary a negro).
   El problema es que --c-text/--c-text-brand/etc. SÍ pasan a claro en
   oscuro, y el sweep de arriba fuerza lo mismo — el resultado es texto
   claro sobre un fondo que sigue siendo claro, ilegible. Se fija un
   texto oscuro fijo (no una variable --c-*, que cambiaría con el modo)
   porque el fondo real de estos bloques nunca cambia. */
body.a11y-contrast-oscuro .portal-main :is(
    .portal-bloque-wrapper--fondo-primary-suave,
    .portal-bloque-wrapper--fondo-accent-suave,
    .portal-texto--fondo-primary-suave,
    .portal-texto--fondo-accent-suave,
    .portal-card--fondo-primary-suave,
    .portal-card--fondo-accent-suave,
    .portal-listado-estados--fondo-primary-suave,
    .portal-listado-estados--fondo-accent-suave,
    .portal-panel-destacado--fondo-primary-suave,
    .portal-panel-destacado--fondo-accent-suave,
    .portal-citas.portal-fondo--primary-suave,
    .portal-citas.portal-fondo--accent-suave
) :is(
    h1, h2, h3, h4, h5, h6,
    p, a, li, td, th, span, div,
    button, label, small, strong, em, b, i,
    dt, dd, blockquote, summary, figcaption
) {
    color: #1F2937 !important;
}

body.a11y-contrast-alto {
    --c-bg:           #FFFFFF;
    --c-bg-card:      #FFFFFF;
    --c-bg-subtle:    #FFFFFF;
    --c-text:         #000000;
    --c-text-on-light:#000000;
    --c-text-soft:    #000000;
    --c-text-strong:  #000000;
    --c-muted:        #000000;
    --c-border:       #000000;
    --c-border-light: #000000;
    --c-primary:      #000000;
    --c-primary-dark: #000000;
    --c-primary-light:#FFFFFF;
    --c-primary-lighter:#FFFFFF;
    --c-accent:       #000000;
    /* En alto contraste el primary se vuelve negro puro — entonces el
       texto que iba sobre primary necesita seguir siendo claro para
       contrastar. Forzamos --c-text-light a blanco puro para máximo
       contraste (vs el #f2f2f2 default). */
    --c-text-light:   #FFFFFF;
    /* Texto-marca también negro puro — máximo contraste sobre blanco. */
    --c-text-brand:   #000000;
    background: #FFFFFF !important;
    color: #000000;
}
body.a11y-contrast-alto :where(img, video) {
    /* Aseguramos contraste mínimo de imágenes — sin esto pueden quedar
       lavadas sobre fondo blanco si tenían opacity o mix-blend. */
    filter: contrast(1.2);
}

/* ── Contraste + fondos "glass" — conflicto conceptual real ────────
   El sistema de "fondo tipo vidrio" (config `fondo_transparente`,
   estilosPortal.css ~L970) resuelve el look reasignando --c-bg-card/
   --c-border* a rgba() translúcidos DIRECTAMENTE en el wrapper del
   bloque — eso gana por especificidad a cualquier valor que el modo
   de contraste ponga a nivel body, así que el contraste nunca llega.
   Y aunque llegara: una superficie translúcida + blur no puede
   garantizar contraste por diseño, porque se funde con lo que sea que
   haya detrás (imagen, degradé), que el modo no controla ni predice.

   Con cualquier modo de contraste activo, el glass se apaga: pasa a
   superficie opaca con los MISMOS tokens que ya usa el resto del modo
   (no un color nuevo) y sin blur — el contraste vuelve a ser
   garantizado, no "lo que haya detrás, un poco atenuado". */
/* OJO: no se puede escribir `--c-bg-card: var(--c-bg-subtle)` acá — el
   propio wrapper glass YA redefine --c-bg-subtle (y --c-bg-card, --c-bg,
   --c-border*) a rgba() translúcido en esta misma regla/elemento
   (estilosPortal.css ~L985); referenciar la variable la lee contaminada
   por esa misma redefinición, no el valor opaco del modo. Por eso van
   los valores literales de cada modo, duplicados a propósito (si se
   cambia un tono de modo arriba, hay que repetirlo acá). */
body.a11y-contrast-claro :is(.portal-bloque-wrapper--sin-fondo, .portal-bloque-lateral--sin-fondo) {
    --c-bg-card:        #FFFFFF !important;
    --c-bg-subtle:      #F3F4F6 !important;
    --c-bg:             #FFFFFF !important;
    --c-border:         #4B5563 !important;
    --c-border-light:   #6B7280 !important;
    --c-border-lighter: #6B7280 !important;
}
body.a11y-contrast-oscuro :is(.portal-bloque-wrapper--sin-fondo, .portal-bloque-lateral--sin-fondo) {
    --c-bg-card:        #1c1c1c !important;
    --c-bg-subtle:      #262626 !important;
    --c-bg:             #0d0d0d !important;
    --c-border:         #525252 !important;
    --c-border-light:   #333333 !important;
    --c-border-lighter: #333333 !important;
}
body.a11y-contrast-alto :is(.portal-bloque-wrapper--sin-fondo, .portal-bloque-lateral--sin-fondo) {
    --c-bg-card:        #FFFFFF !important;
    --c-bg-subtle:      #FFFFFF !important;
    --c-bg:             #FFFFFF !important;
    --c-border:         #000000 !important;
    --c-border-light:   #000000 !important;
    --c-border-lighter: #000000 !important;
}
/* El blur en sí (aunque el fondo ya sea opaco, un blur de fondo sigue
   sin sumar nada útil bajo un modo de contraste) — se saca en TODO el
   subárbol, no solo en la lista puntual de "superficies card-like",
   para no depender de mantener esa lista sincronizada acá. */
:is(body.a11y-contrast-claro, body.a11y-contrast-oscuro, body.a11y-contrast-alto)
    :is(.portal-bloque-wrapper--sin-fondo, .portal-bloque-lateral--sin-fondo) * {
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
}

/* Botones/badge "glass" del hero y el banner: van DIRECTO sobre la
   imagen (no dentro de un wrapper con --c-bg-card), con colores fijos
   en rgba() pensados para funcionar "a ojo" sobre cualquier foto — cero
   garantía de contraste. Mismo criterio: superficie y texto del modo,
   sin blur. */
:is(body.a11y-contrast-claro, body.a11y-contrast-oscuro, body.a11y-contrast-alto)
    :is(.portal-hero-cta--glass, .portal-banner-cta--glass, .portal-hero-eyebrow) {
    background: var(--c-bg-card) !important;
    color: var(--c-text) !important;
    border-color: var(--c-border) !important;
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
}

/* 5. Escala de grises — filter en el wrapper principal. NO en el
   widget mismo, para que el visitante pueda seguir reconociendo el
   estado activo de los toggles por color. */
body.a11y-grayscale :where(.portal-main, .portal-navbar, .portal-topbar, footer) {
    filter: grayscale(1);
}

/* 5.b Ocultar imágenes (MHF-HU043) — acelera la carga percibida y
   reduce distracciones. Alcance: <img> reales (navbar/footer
   incluidos, sin excluir nada — "ocultar TODAS las imágenes" de la
   HU) + los `background-image` de CSS de los bloques que también
   pintan la foto así (noticias, hero de fotos) — un `<img>` oculto
   no alcanza ahí, no hay elemento al que aplicarle `display:none`.
   NO toca <svg> de íconos (UI/chrome, no "imágenes" en el sentido de
   la historia).

   Los contenedores listados abajo dejaban asomar, al ocultar su
   `<img>`, el fondo de "placeholder de carga" que cada uno ya tenía
   (degradé accent→primary en banners, `--c-primary` sólido y
   saturado en cards flip, `--c-bg-subtle` en otras) — cada uno
   pensado para el instante ANTES de que la foto cargue, no como
   estado final permanente, y por eso quedaban inconsistentes entre
   sí. Se unifican todos a un solo tono neutro derivado de
   `--c-primary` (mismo patrón `color-mix()` ya usado en
   theme-config.css para --c-primary-dark/light/lighter). */
body.a11y-hide-images {
    --a11y-img-fallback: color-mix(in srgb, var(--c-primary) 50%, #6b7280 50%);
}
body.a11y-hide-images img {
    display: none !important;
}
body.a11y-hide-images :where(
    .portal-noticia-thumb,
    .portal-hero--imagen-overlay,
    .portal-banner-media,
    .portal-card-flip-anverso,
    .portal-card--foto,
    .portal-card--overlay,
    .portal-panel-destacado-item,
    .portal-video-card-thumb,
    .portal-video-frame-thumb
) {
    background: var(--a11y-img-fallback) !important;
}
/* NO se toca `.portal-video-frame` (video_embebido/grilla en
   reproducción, o el <video> HTML5) — su fondo negro es letterboxing
   de reproductor de video real, no un placeholder de "sin imagen";
   usar el tono desaturado ahí se vería peor, no mejor.
   `.portal-video-frame-thumb` SÍ se unifica arriba: es la miniatura
   "click para cargar" del iframe (mismo rol que la de la grilla),
   no el reproductor en uso. El `poster` de <video> (imagen real,
   pero atributo HTML, no <img>) se resuelve por JS — ver
   `aplicarOcultarImagenesEnVideo` en accesibilidad-widget.js. */
/* Fondo de SECCIÓN con imagen (`--zona-bg-image`, ver estilosPortal.css
   ~línea 754) — mecanismo aparte de los bloques de arriba, el operador
   lo elige por zona (destacados/contenido/servicios) desde el editor.
   Compound selector a propósito: `.portal-zona--con-fondo` sola
   también cubre fondos de COLOR liso (elegidos aparte, sin imagen) —
   esos no se tocan, solo la variante que además tiene
   `.portal-zona--tiene-imagen`. */
body.a11y-hide-images .portal-zona--con-fondo.portal-zona--tiene-imagen {
    background-image: none !important;
    background-color: var(--a11y-img-fallback) !important;
}

/* Logo institucional del navbar: es NAVEGACIÓN (el único link a
   home), no "contenido" — ocultarlo sin más deja ese botón vacío e
   inutilizable (reportado por el usuario). El <img> real igual
   desaparece (cumple "ocultar TODAS las imágenes" al pie de la
   letra), pero se revela el nombre del sistema como texto, reusando
   el `title` que el link `.portal-logo` ya trae SIEMPRE —mismo dato
   que `.portal-logo-wordmark` muestra cuando no hay logo cargado,
   ver templates/portal/base.html~143. Sin declarar color: hereda
   `color: var(--c-text-brand)` que `.portal-logo` ya tiene. */
body.a11y-hide-images .portal-logo::after {
    content: attr(title);
    font-size: 1.1rem;
    font-weight: 800;
    letter-spacing: -0.02em;
}

/* 6. Pausar animaciones — útil para visitantes con vestibular
   sensitivity. Comprime todas las animations/transitions a casi-cero
   (0.001ms — no 0ms, así los listeners de `transitionend` igual
   disparan). Limita las animations infinitas a una sola iteración.
   El JS del widget además pausa los <video> y emite un evento que
   escucha el carrusel para detener su autoplay (setInterval, fuera
   del alcance de CSS). */
body.a11y-pause-animations,
body.a11y-pause-animations *,
body.a11y-pause-animations *::before,
body.a11y-pause-animations *::after {
    animation-duration: .001ms !important;
    animation-delay: 0ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    transition-delay: 0ms !important;
    scroll-behavior: auto !important;
}

/* 6. Resaltar enlaces — subrayado fuerte en accent para todos los
   `<a>` del portal. No tocamos `color` ni `background` (eso rompía
   links sobre fondos de color como acordeón en navy, cinta de
   WhatsApp, botones que son <a>, etc.). Sólo el subrayado, con color
   propio (decoration-color) para que sea visible sobre cualquier
   fondo, sin requerir cambiar el texto. */
body.a11y-highlight-links :where(.portal-main a, .portal-main a:link, .portal-main a:visited) {
    text-decoration: underline solid 2px !important;
    text-decoration-color: var(--c-accent, #fbbf24) !important;
    text-underline-offset: 3px !important;
}

/* 6.b Bloques clicleables (accesos directos, cards, botones, banners,
   acordeón) tienen su propio diseño con fondos/colores propios. Para
   ellos el subrayado del texto interno tampoco sirve (no se entiende
   "todo el bloque es clickeable"). Usamos OUTLINE accent — comunica
   "esto es interactivo" sin disturbar el contenido. */
body.a11y-highlight-links .portal-main :is(
    .portal-acceso,
    .portal-card--clickable,
    .portal-material-card,
    .portal-card--overlay,
    .portal-banner,
    .portal-acordeon-header,
    .portal-cinta-whatsapp,
    button
) {
    outline: 3px solid var(--c-accent, #fbbf24) !important;
    outline-offset: 2px !important;
}

/* 7. Outline de foco — visible tanto en navegación por teclado como
   en click con mouse. Antes solo usaba `:focus-visible` que en la
   mayoría de browsers NO matchea con mouse click (heurística para
   "el foco lo necesitás solo si navegás por teclado"). Para que el
   toggle realmente haga algo cuando el visitante mueve el mouse,
   ampliamos a `:focus` también. */
body.a11y-focus-outline :focus,
body.a11y-focus-outline :focus-visible {
    outline: 3px solid var(--c-primary, #13304D) !important;
    outline-offset: 3px !important;
    border-radius: 3px;
}

/* 8. Cursor de lectura — 3 sub-modos, ciclado por el JS
   (`aplicarCursorAsistido`). A diferencia de todo lo de arriba, acá el
   posicionamiento en tiempo real lo hace JS (`mousemove`); el CSS solo
   define la forma/apariencia de cada modo y el overlay contenedor. */
.a11y-cursor-overlay {
    position: fixed;
    inset: 0;
    /* Arriba del FAB/panel (9990): con el cursor nativo oculto en todo el
       documento (ver 8.1), si el overlay quedara debajo, el cursor
       personalizado desaparecería justo al pasar sobre el propio widget
       de accesibilidad. pointer-events:none evita que tape los clics. */
    z-index: 9995;
    pointer-events: none;
}

/* 8.1 Cursor grande — se oculta el nativo (todo el documento, no solo
   el overlay) y se dibuja uno propio más grande y de alto contraste
   (relleno oscuro + borde claro, visible sobre cualquier fondo). */
body.a11y-cursor-grande,
body.a11y-cursor-grande * {
    cursor: none !important;
}
.a11y-cursor-grande-punta {
    position: absolute;
    top: 0;
    left: 0;
    width: 40px;
    height: 40px;
    /* Corrige el origen para que la "punta" (esquina superior
       izquierda de la forma, antes de rotar) quede exactamente en la
       posición real del mouse, no el centro del cuadrado de 40px. */
    margin: -4px 0 0 -4px;
    background: #111;
    border: 3px solid #fff;
    border-radius: 50% 50% 50% 0;
    transform: rotate(-45deg);
    box-shadow: 0 1px 4px rgba(0, 0, 0, .5);
    will-change: transform;
}

/* 8.2 Máscara de lectura — dos paneles oscuros full-width que dejan
   una franja transparente centrada en la posición Y del mouse (~110px
   de alto), oscureciendo el resto de la página para que el visitante
   no pierda la línea que está leyendo. Los bordes en color marcan el
   límite de la franja legible, para distinguirla de la zona oscura. */
.a11y-cursor-mascara-panel {
    position: absolute;
    left: 0;
    right: 0;
    background: rgba(0, 0, 0, .6);
}
.a11y-cursor-mascara-arriba {
    top: 0;
    border-bottom: 3px solid var(--c-accent, #fbbf24);
}
.a11y-cursor-mascara-abajo {
    bottom: 0;
    border-top: 3px solid var(--c-accent, #fbbf24);
}

/* 8.3 Guía de lectura — línea corta (no ocupa todo el ancho) con
   marcador triangular, que resalta la línea actual SIN oscurecer el
   resto (más liviana que la máscara). El JS mueve
   `.a11y-cursor-guia-linea` en Y (centrada en X por CSS) y desliza
   `.a11y-cursor-guia-triangulo` en X para seguir el mouse, dentro de
   los límites de la línea — si el mouse se va más allá de un extremo,
   el triángulo queda "pegado" a ese borde en vez de salirse. */
.a11y-cursor-guia-linea {
    position: absolute;
    top: 0;
    left: 50%;
    width: min(700px, 90vw);
    height: 12px;
    border-radius: 6px;
    background: var(--c-accent, #fbbf24);
    border: 2px solid rgba(0, 0, 0, .55);
    box-shadow: 0 2px 8px rgba(0, 0, 0, .45);
    will-change: transform;
}
.a11y-cursor-guia-triangulo {
    position: absolute;
    top: -16px;
    left: 50%;
    transform: translateX(-50%);
    width: 0;
    height: 0;
    border-left: 12px solid transparent;
    border-right: 12px solid transparent;
    /* Mismo color que la barra (--c-accent), no negro: así el triángulo
       se lee como parte de una sola pieza con la línea. El contorno
       oscuro (mismo tono que el borde de la línea) es lo que le da
       contraste contra fondos claros — no se puede usar `border` real
       en una forma hecha con border-triangle, por eso va como
       drop-shadow apilado en las 4 direcciones cardinales. */
    border-top: 12px solid var(--c-accent, #fbbf24);
    filter:
        drop-shadow(1.5px 0 0 #111)
        drop-shadow(-1.5px 0 0 #111)
        drop-shadow(0 1.5px 0 #111)
        drop-shadow(0 -1.5px 0 #111);
}

/* ═══════════════════════════════════════════════════════════════════
   Página pública de la declaración (/accesibilidad/).
   Hereda layout y tipografía del portal (`portal-texto`, `portal-container`)
   — sólo sumamos los headers institucionales y la grilla de metadata.
   ═══════════════════════════════════════════════════════════════════ */

.portal-decl-a11y {
    padding-block: 2.5rem 3rem;
}
.portal-decl-a11y-header {
    border-bottom: 2px solid var(--c-primary, #13304D);
    padding-bottom: 1rem;
    margin-bottom: 1.5rem;
}
.portal-decl-a11y-header h1 {
    color: var(--c-primary, #13304D);
    font-size: 2rem;
    font-weight: 700;
    margin: 0 0 .25rem;
}
.portal-decl-a11y-subtitulo {
    color: var(--c-muted, #6b7280);
    font-size: 1rem;
    margin: 0;
}
.portal-decl-a11y-cuerpo {
    margin-bottom: 2rem;
}
.portal-decl-a11y-meta {
    background: var(--c-bg-subtle, #f3f4f6);
    border-radius: 10px;
    padding: 1.25rem 1.5rem;
}
.portal-decl-a11y-meta-grid {
    display: grid;
    grid-template-columns: minmax(180px, max-content) 1fr;
    gap: .75rem 1.5rem;
    margin: 0;
}
.portal-decl-a11y-meta-grid dt {
    font-weight: 600;
    color: var(--c-text-strong, #111827);
}
.portal-decl-a11y-meta-grid dd {
    margin: 0;
    color: var(--c-text, #1f2937);
}
.portal-decl-a11y-tel {
    color: var(--c-muted, #6b7280);
    font-size: .9rem;
}
@media (max-width: 600px) {
    .portal-decl-a11y-meta-grid {
        grid-template-columns: 1fr;
        gap: .25rem 0;
    }
    .portal-decl-a11y-meta-grid dt {
        margin-top: .75rem;
    }
    .portal-decl-a11y-meta-grid dt:first-child {
        margin-top: 0;
    }
}

/* ═══════════════════════════════════════════════════════════════════
   Responsive — en mobile el panel ocupa casi todo el ancho.
   ═══════════════════════════════════════════════════════════════════ */
@media (max-width: 480px) {
    .a11y-widget {
        left: .75rem;
        bottom: .75rem;
    }
    .a11y-panel {
        width: calc(100vw - 1.5rem);
        max-width: 360px;
    }
}
