/* ════════════════════════════════════════════════════════════════════
   estilosPortal.css
   Hoja de estilos del SHELL del portal público.

   Cubre: body, container, navbar, footer, zonas del layout, hero (que es
   parte del esqueleto, no de un bloque opcional) y el estado de "site no
   listo". El render concreto de cada tipo de bloque vive en
   estilosBloques.css para mantener la separación de responsabilidades.

   Convención de nombres:
     .portal-*  → todo lo del portal público
   ════════════════════════════════════════════════════════════════════ */


/* ═══════════════════════════════════════════════════════════════════
   Reset / base
   ═══════════════════════════════════════════════════════════════════ */

*, *::before, *::after { box-sizing: border-box; }

/* Accesibilidad — equivalente al .visually-hidden de Bootstrap.
   Oculta visualmente pero mantiene el elemento accesible para lectores
   de pantalla. Se usa en formularios del portal (ej. buscadores, filtros)
   para proporcionar label sin afectar el layout visual. */
.visually-hidden,
.visually-hidden-focusable:not(:focus):not(:focus-within) {
    position: absolute !important;
    width: 1px !important;
    height: 1px !important;
    padding: 0 !important;
    margin: -1px !important;
    overflow: hidden !important;
    clip: rect(0, 0, 0, 0) !important;
    white-space: nowrap !important;
    border: 0 !important;
}

/* Clipeamos también en <html> — los elementos `position: fixed` se
   posicionan respecto al viewport (html), no al body. Sin esto, el
   navbar mobile en estado cerrado (`transform: translateX(100%)`) deja
   ~25-30px de scroll horizontal en algunos browsers porque su área
   transformada cuenta para el overflow del documento, y queda una
   franja vacía a la derecha del contenido. */
html {
    /* `clip` es lo correcto (no crea un contexto de scroll, no rompe
       position: sticky en hijos como la navbar). Pero `clip` solo está
       soportado en Safari 16+ / iOS 16+, Chrome 90+, Firefox 81+. En
       iOS Safari ≤ 15 cae a `visible` y el navbar mobile (que vive con
       `transform: translateX(100%)` cuando está cerrado) hace que el
       body se ensanche 100vw a la derecha — toda la página se ve
       corrida hacia la derecha. Agregamos `hidden` como fallback ANTES
       de `clip`: browsers que entienden `clip` lo usan; los viejos
       caen a `hidden`, que también clipea pero crea un contexto de
       scroll (irrelevante porque clipea, no aparece scrollbar). */
    overflow-x: hidden;
    overflow-x: clip;
    /* Smooth scroll para que al clickear links con anchor (ej. botón del
       navbar a "/pagina/#seccion") la página haga un scroll animado
       hasta el bloque destinatario en lugar de saltar abruptamente.
       Para los visitantes que tildaron "Pausar animaciones" en el widget
       de accesibilidad, esta regla se neutraliza vía
       `body.a11y-pause-animations { scroll-behavior: auto !important; }`
       que ya existe en `accesibilidad-widget.css`. */
    scroll-behavior: smooth;
    /* Compensación para que el bloque destino quede DEBAJO de la navbar
       sticky en lugar de pegado al borde superior. La navbar mide unos
       80-90px en desktop; bajamos a 64 para mobile vía media query
       más abajo si hace falta. */
    scroll-padding-top: 90px;
}

.portal-body {
    margin: 0;
    font-family: var(--c-font-family, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
    color: var(--c-text);
    /* `--c-bg` viene de la ConfiguracionVisual: cambia con la paleta. */
    background: var(--c-bg);
    line-height: 1.55;
    font-size: 1.15rem;
    /* Evita scrollbar horizontal cuando alguna zona (típicamente el hero)
       usa margins negativos para romper el container y ocupar todo el
       viewport. Aplicado al body — no al .portal-main — para que no cree
       un containing block que interfiera con la posición de los hijos.
       `hidden` como fallback antes de `clip` para que iOS Safari ≤ 15
       (donde `clip` no existe) también clipee. Sin esto, el navbar
       mobile con transform empuja la página hacia la derecha. */
    overflow-x: hidden;
    overflow-x: clip;
}

.portal-container {
    /* Antes: 1200px. Subido a 1400px para aprovechar mejor el ancho
       en monitores de 1920+ — el espacio en blanco a los costados era
       excesivo. Los bloques con grilla (cards, noticias, accesos) ya
       respetan su propio `minmax()` así que no se estiran de más; lo
       que gana es el ancho de la sección, no el del contenido textual.
       En pantallas ≤ 1440px se sigue viendo casi como antes porque el
       container ocupa el 100% disponible menos el padding. */
    max-width: 1400px;
    margin: 0 auto;
    padding: 0 1.5rem;
    width: 100%;
}
/* En mobile chico (iPhone Pro 393px, Galaxy 360px, etc.), 24px de
   padding a cada lado del container (48px en total) come ~12% del
   viewport. Eso hace que los bloques internos peleen por los pocos
   pixels restantes y, cuando alguno tiene min-content cerca del
   borde, "se rompen" o se ven empujados hacia un lado. Bajamos a
   1rem (16px cada lado, 32px totales) por debajo de 480px — el
   hero acompaña con su propio breakpoint más abajo en este archivo. */
@media (max-width: 480px) {
    .portal-container {
        padding-inline: 1rem;
    }
}

/* Breakpoint extra a 410px — reportado por el usuario como "punto donde
   la página deja de adaptarse". Sintomáticamente: algunos bloques tienen
   un min-content (un texto largo sin espacios, un badge pegado a un
   ícono, una palabra con padding generoso) que cerca de los 362-378 px
   de ancho útil (410 menos padding) ya no entra. La solución defensiva
   es bajar más el padding del container y forzar que TODOS los wrappers
   de layout permitan shrink debajo de su contenido. */
@media (max-width: 410px) {
    .portal-container {
        padding-inline: .75rem;
    }
    /* Garantía de shrinking: cualquier elemento del layout principal
       puede achicarse debajo de su `min-content` default. Sin esto,
       un solo descendiente con texto largo basta para que la página
       se quede atascada al ancho del primer nodo grande. */
    .portal-main,
    .portal-zona,
    .portal-zona-inner,
    .portal-zona-contenido,
    .portal-bloque-wrapper,
    .portal-zona-lateral {
        min-width: 0;
    }
}

a { color: var(--c-text-brand); text-decoration: none; }
a:hover { text-decoration: underline; }


/* ═══════════════════════════════════════════════════════════════════
   Navbar institucional
   ═══════════════════════════════════════════════════════════════════ */

.portal-navbar {
    background: var(--c-bg-card);
    border-bottom: 1px solid var(--c-border-light);
    position: sticky;
    top: 0;
    z-index: 50;
}
.portal-navbar-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding-top: 1rem;
    padding-bottom: 1rem;
}

.portal-logo {
    display: inline-flex;
    align-items: center;
    text-decoration: none;
    color: var(--c-text-brand);
}
.portal-logo img {
    height: 56px;
    width: auto;
    display: block;
}
/* Fallback cuando ConfiguracionVisual.logo no está cargada: el nombre
   del sistema se muestra como wordmark institucional. */
.portal-logo-wordmark {
    font-size: 1.4rem;
    font-weight: 800;
    color: var(--c-text-brand);
    letter-spacing: -0.02em;
    line-height: 1.1;
}

/* Lazo de duelo institucional. Aparece al lado del logo en el portal
   público cuando el Modo Duelo está activo (toggle global de
   ConfiguracionVisual). El color queda hardcoded en negro porque es
   convención institucional fija — no depende de la paleta del sitio. */
.portal-logo-duelo {
    display: inline-flex;
    align-items: center;
    margin-left: 12px;
    height: 40px;
    color: #000;
    /* Pequeña separación visual con una línea sutil para que se lea
       como elemento independiente del logo, no como parte de él. */
    padding-left: 12px;
    border-left: 1px solid rgba(0, 0, 0, 0.12);
}
.portal-logo-duelo svg {
    height: 100%;
    width: auto;
    display: block;
}
@media (max-width: 640px) {
    .portal-logo-duelo {
        height: 32px;
        margin-left: 8px;
        padding-left: 8px;
    }
}

.portal-nav {
    display: flex;
    align-items: center;
    gap: 1.5rem;
}
.portal-nav-item {
    color: var(--c-text-brand);
    font-weight: 600;
    font-size: 1.05rem;
    text-decoration: none;
    transition: color .15s ease;
}
.portal-nav-item:hover {
    color: var(--c-primary-light);
    text-decoration: none;
}

/* ── Hamburguer / botón mobile ──────────────────────────────────────
   Solo visible en mobile (≤900px, regla más abajo). Las tres líneas
   se transforman en una X cuando `aria-expanded="true"` — el JS
   alterna ese atributo + la clase `is-open` en la nav. */
.portal-navbar-hamburguer {
    display: none;     /* visible solo en mobile vía @media */
    background: transparent;
    border: 0;
    padding: 8px;
    width: 44px;
    height: 44px;
    cursor: pointer;
    position: relative;
    z-index: 102;      /* arriba del overlay para poder cerrarlo */
    flex-direction: column;
    justify-content: space-between;
    align-items: center;
}
.portal-navbar-hamburguer-line {
    display: block;
    width: 100%;
    height: 3px;
    background: var(--c-primary);
    border-radius: 2px;
    transition: transform .25s ease, opacity .15s ease, background .15s ease;
    transform-origin: center;
}
/* Estado abierto: líneas en blanco (sobre el fondo primary del overlay)
   y reorganizadas como una X. Los valores de translate están
   calculados para el alto del botón (44px) menos padding (8px×2). */
.portal-navbar-hamburguer[aria-expanded="true"] .portal-navbar-hamburguer-line {
    background: var(--c-bg);
}
.portal-navbar-hamburguer[aria-expanded="true"] .portal-navbar-hamburguer-line:nth-child(1) {
    transform: translateY(10px) rotate(45deg);
}
.portal-navbar-hamburguer[aria-expanded="true"] .portal-navbar-hamburguer-line:nth-child(2) {
    opacity: 0;
}
.portal-navbar-hamburguer[aria-expanded="true"] .portal-navbar-hamburguer-line:nth-child(3) {
    transform: translateY(-10px) rotate(-45deg);
}

/* Body pierde scroll cuando el overlay del nav está abierto.
   Evita doble scroll y el "fondo se mueve detrás del menú". */
body.portal-body--nav-open {
    overflow: hidden;
}

/* Cuando el menú está abierto, fijamos la navbar al tope del viewport
   para que el botón X (el hamburguer transformado, anclado dentro de
   .portal-navbar) quede SIEMPRE visible. Sin esta regla, si el
   visitante ya había scrolleado la página antes de tocar el menú, al
   poner `overflow:hidden` en el body el `position: sticky` de la
   navbar pierde su contexto y se comporta como `static`, quedando
   anclada arriba del documento (off-screen). Con `position: fixed`
   forzamos su posición independientemente del scroll previo. */
body.portal-body--nav-open .portal-navbar {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 102;
}


/* ═══════════════════════════════════════════════════════════════════
   Main y Zonas
   ═══════════════════════════════════════════════════════════════════ */

.portal-main { min-height: 60vh; }

/* Banner "MODO PREVIEW" — solo aparece cuando el operador abre la
   página desde el listado de pagebuilder en lugar de la URL pública.
   Sticky arriba, color de advertencia, no se confunde con el chrome
   real del portal. */
.portal-preview-banner {
    position: sticky;
    top: 0;
    z-index: 1000;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .55rem;
    background: #f59e0b;            /* amarillo "warning" inconfundible */
    color: #111;
    padding: .55rem 1rem;
    font-size: .9rem;
    box-shadow: 0 .15rem .55rem rgba(0, 0, 0, .15);
    flex-wrap: wrap;
}
.portal-preview-banner i.bi-eye-fill { font-size: 1.1rem; }
.portal-preview-banner-cerrar {
    margin-left: .5rem;
    color: #111;
    opacity: .7;
    text-decoration: none;
}
.portal-preview-banner-cerrar:hover {
    opacity: 1;
    color: #111;
}

/* Botón inline del banner (ej: "Vista celular" / "Vista escritorio"). */
.portal-preview-banner-btn {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    margin-left: .5rem;
    padding: .2rem .65rem;
    background: rgba(0, 0, 0, .12);
    color: #111;
    border-radius: 999px;
    font-size: .82rem;
    font-weight: 600;
    text-decoration: none;
    transition: background .12s ease, transform .08s ease;
}
.portal-preview-banner-btn:hover {
    background: rgba(0, 0, 0, .22);
    color: #111;
    text-decoration: none;
    transform: translateY(-1px);
}
.portal-preview-banner-btn i { font-size: .95rem; }

/* ── Vista celular del preview ──────────────────────────────────────
   El shell oscurece el fondo y centra un "teléfono" con la página
   adentro. El frame tiene marco redondeado + notch decorativo para que
   se lea visualmente como un mock de móvil, no como un iframe pelado.
   El iframe en sí es lo que hace que las media queries CSS reaccionen
   al ancho mobile real (390px) — no alcanza con achicar un div, el
   viewport del navegador seguiría siendo grande. */
body.portal-body--vista-celular {
    background: #1f1f1f;
    min-height: 100vh;
    margin: 0;
}
.portal-preview-celular-stage {
    display: flex;
    justify-content: center;
    padding: 28px 16px 40px;
    /* Si el iframe es más alto que el viewport disponible, queremos que
       el stage scrollee — no que el frame se corte. */
    min-height: calc(100vh - 60px);
}
.portal-preview-celular-frame {
    position: relative;
    width: 390px;       /* iPhone 14 / 15 estándar */
    max-width: 100%;
    height: min(844px, calc(100vh - 100px));
    background: #000;
    border-radius: 36px;
    border: 10px solid #111;
    overflow: hidden;
    box-shadow: 0 24px 60px rgba(0, 0, 0, .45),
                0 0 0 2px #2a2a2a inset;
}
.portal-preview-celular-frame iframe {
    width: 100%;
    height: 100%;
    border: 0;
    background: #fff;
    display: block;
}
.portal-preview-celular-notch {
    position: absolute;
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 120px;
    height: 22px;
    background: #111;
    border-bottom-left-radius: 14px;
    border-bottom-right-radius: 14px;
    z-index: 2;
    pointer-events: none;
}

@media (max-width: 480px) {
    /* Si el operador abre el preview celular desde un celular real, el
       frame mock excede el viewport — sacamos el marco para que la
       página se vea full-screen sin las decoraciones de teléfono. */
    .portal-preview-celular-stage { padding-inline: 0; }
    .portal-preview-celular-frame {
        width: 100%;
        border-radius: 0;
        border: 0;
        height: calc(100vh - 60px);
    }
    .portal-preview-celular-notch { display: none; }
}

.portal-zona { padding-block: var(--portal-zona-pad, 2.5rem); }
.portal-zona--hero { padding: 0; }

/* ── Ritmo y escala tipográfica por página ────────────────────────
   `.portal-pagina` (wrapper de toda la página) fija las variables que
   gobiernan el aire de las secciones y la separación entre bloques.
   Los modificadores `.portal-espaciado--*` las ajustan. La fuente es
   siempre la global; solo cambian tamaños y espacios (jerarquía, no
   fragmentación). */
.portal-pagina {
    --portal-zona-pad: 2.5rem;   /* padding vertical de cada franja */
    --portal-zona-gap: 1.5rem;   /* separación entre bloques de una zona */
}
.portal-espaciado--compacta { --portal-zona-pad: 1.5rem; --portal-zona-gap: 1rem; }
.portal-espaciado--normal   { --portal-zona-pad: 2.5rem; --portal-zona-gap: 1.5rem; }
.portal-espaciado--aireada  { --portal-zona-pad: 4rem;   --portal-zona-gap: 2.25rem; }

/* Escala de títulos "display": aplica solo a los encabezados del texto
   libre (donde el operador escribe los títulos de sección). Se scopea
   bajo el wrapper para ganar especificidad sobre las reglas base de
   estilosBloques sin tocarlas. Modo "normal" no agrega reglas → los
   tamaños base quedan intactos. Usa clamp() para escalar con el
   viewport (impacto en desktop sin romper en mobile). */
.portal-titulos--grande .portal-texto-titulo   { font-size: clamp(1.75rem, 1rem + 1.7vw, 2.15rem); }
.portal-titulos--grande .portal-texto-cuerpo h1 { font-size: clamp(2rem, 1rem + 2.2vw, 2.6rem); }
.portal-titulos--grande .portal-texto-cuerpo h2 { font-size: clamp(1.55rem, 1rem + 1.4vw, 1.95rem); }
.portal-titulos--grande .portal-texto-cuerpo h3 { font-size: clamp(1.25rem, 1rem + .8vw, 1.5rem); }

.portal-titulos--gigante .portal-texto-titulo   { font-size: clamp(2.1rem, 1rem + 3.2vw, 3.4rem); line-height: 1.1; }
.portal-titulos--gigante .portal-texto-cuerpo h1 { font-size: clamp(2.4rem, 1rem + 4vw, 4rem); line-height: 1.08; }
.portal-titulos--gigante .portal-texto-cuerpo h2 { font-size: clamp(1.9rem, 1rem + 2.6vw, 2.8rem); line-height: 1.12; }
.portal-titulos--gigante .portal-texto-cuerpo h3 { font-size: clamp(1.45rem, 1rem + 1.4vw, 1.9rem); }

/* ── Grilla interna de cada zona (bloques lado a lado) ─────────────
   Cada zona es un grid de 12 columnas. Cada bloque va envuelto en
   `.portal-bloque-wrapper` y declara cuántas columnas ocupa mediante
   el modificador `--ancho-<valor>`. Dos bloques con ancho=half (6 col
   cada uno) llenan una fila lado a lado; un dos-tercios + un tercio
   también funcionan. El default es ancho completo (12 col).

   El hero conserva su comportamiento de "ancho full bleed" — el grid
   interno no agrega gap visual ni cambia su layout. Para el resto de
   zonas, las wrappers determinan la composición. */
/* Cada zona es una FRANJA full-width. El contenido se acota dentro de
   `.portal-zona-inner`; la grilla de 12 columnas vive en
   `.portal-zona-contenido`. El hero no lleva inner (su bloque se
   auto-acota). */
.portal-zona-inner {
    max-width: 1400px;
    margin-inline: auto;
    padding-inline: 1.5rem;
    width: 100%;
}
@media (max-width: 480px) { .portal-zona-inner { padding-inline: 1rem; } }
@media (max-width: 410px) { .portal-zona-inner { padding-inline: .75rem; } }

/* Grilla de 12 columnas para los bloques de la zona. `bloque.ancho`
   decide el span (reglas `.portal-bloque-wrapper--ancho-*` más abajo). */
.portal-zona-contenido {
    display: grid;
    grid-template-columns: repeat(12, 1fr);
    gap: var(--portal-zona-gap, 1.5rem);
    min-width: 0;
}

/* Zona con lateral: el inner se parte en contenido (1fr) + columna
   lateral (320px). El contenido conserva su grilla de 12 col adentro. */
.portal-zona-inner--con-lateral {
    display: grid;
    grid-template-columns: 1fr 320px;
    gap: 2rem;
    align-items: start;
}
.portal-zona-lateral {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
    min-width: 0;
    align-self: start;
}
/* En tablet/mobile el lateral cae al pie del contenido. */
@media (max-width: 900px) {
    .portal-zona-inner--con-lateral { grid-template-columns: 1fr; }
}
.portal-bloque-wrapper {
    /* Default: ocupa toda la fila. Cuando el operador no toca el ancho,
       el comportamiento es idéntico al de antes (stack vertical). */
    grid-column: span 12;
    min-width: 0;       /* permite que el contenido encoja sin desbordar */
}
.portal-bloque-wrapper--ancho-full       { grid-column: span 12; }
.portal-bloque-wrapper--ancho-two-thirds { grid-column: span 8; }
.portal-bloque-wrapper--ancho-half       { grid-column: span 6; }
.portal-bloque-wrapper--ancho-third      { grid-column: span 4; }

/* Alineación horizontal — solo aplica cuando ancho != full.
   "izquierda" mantiene el comportamiento default (auto-placement de la
   grilla, flow left-to-right). "centro" y "derecha" hacen placement
   explícito sobre la grilla 12-col, así un 1/3 puede quedar centrado
   (cols 5-8), un 2/3 con margen simétrico (cols 3-10), un 1/2 a la
   derecha (cols 7-12), etc.

   ⚠️ Combinar dos bloques con la misma alineación-no-izquierda en la
   misma zona puede pisarlos (terminan en las mismas columnas).
   Como la izquierda sigue siendo auto-flow, los layouts existentes
   no cambian. */
.portal-bloque-wrapper--align-centro.portal-bloque-wrapper--ancho-two-thirds { grid-column: 3 / span 8; }
.portal-bloque-wrapper--align-centro.portal-bloque-wrapper--ancho-half       { grid-column: 4 / span 6; }
.portal-bloque-wrapper--align-centro.portal-bloque-wrapper--ancho-third      { grid-column: 5 / span 4; }

.portal-bloque-wrapper--align-derecha.portal-bloque-wrapper--ancho-two-thirds { grid-column: 5 / span 8; }
.portal-bloque-wrapper--align-derecha.portal-bloque-wrapper--ancho-half       { grid-column: 7 / span 6; }
.portal-bloque-wrapper--align-derecha.portal-bloque-wrapper--ancho-third      { grid-column: 9 / span 4; }

/* En tablet portrait y mobile, todos los anchos colapsan a 1 columna.
   Mejor lectura en pantalla angosta y evita bloques de 33% que no
   muestran nada útil. */
@media (max-width: 768px) {
    .portal-zona-contenido {
        grid-template-columns: 1fr;
        gap: 1.25rem;
    }
    .portal-bloque-wrapper,
    .portal-bloque-wrapper--ancho-full,
    .portal-bloque-wrapper--ancho-two-thirds,
    .portal-bloque-wrapper--ancho-half,
    .portal-bloque-wrapper--ancho-third,
    /* Las variantes con alineación explícita también colapsan a ancho
       completo — la alineación es una decisión de layout horizontal que
       no tiene sentido en una sola columna. */
    .portal-bloque-wrapper--align-centro.portal-bloque-wrapper--ancho-two-thirds,
    .portal-bloque-wrapper--align-centro.portal-bloque-wrapper--ancho-half,
    .portal-bloque-wrapper--align-centro.portal-bloque-wrapper--ancho-third,
    .portal-bloque-wrapper--align-derecha.portal-bloque-wrapper--ancho-two-thirds,
    .portal-bloque-wrapper--align-derecha.portal-bloque-wrapper--ancho-half,
    .portal-bloque-wrapper--align-derecha.portal-bloque-wrapper--ancho-third {
        grid-column: 1 / -1;
    }
}

/* ── Apilado de zonas ─────────────────────────────────────────────
   Las zonas son franjas full-width apiladas verticalmente dentro de
   `.portal-main` (sin grilla global boxed). El fondo de cada franja
   llega de borde a borde de forma natural; el contenido se acota con
   `.portal-zona-inner`. El hero no lleva inner: su bloque maneja el
   ancho (fondo full-width + texto acotado internamente).

   Antes esto era un grid de 2 columnas donde un lateral podía cruzar
   varias zonas; se reemplazó por franjas + lateral anclado a su zona
   (ver `.portal-zona-inner--con-lateral`). */
.portal-zona--hero { margin-top: 0; }

/* En mobile bajamos el respiro vertical de las franjas (el flujo es
   vertical y cada bloque ya trae sus márgenes). El hero queda en 0. */
@media (max-width: 900px) {
    .portal-zona:not(.portal-zona--hero) {
        padding-block: 1.25rem;
    }
}


/* ═══════════════════════════════════════════════════════════════════
   Fondos de sección (color / imagen por zona)
   ───────────────────────────────────────────────────────────────────
   El operador puede pintar el fondo de cada zona (destacados,
   contenido, servicios) desde el editor. La página pública setea, en
   el `<section>` de la zona, las variables `--zona-*` con los valores
   elegidos y le agrega estas clases:

     .portal-zona--con-fondo    → la zona tiene fondo (color o imagen).
     .portal-zona--tiene-imagen → el fondo es imagen (activa el velo).
     .portal-zona--texto-claro  → texto claro (fondo oscuro).
     .portal-zona--texto-oscuro → texto oscuro (fondo claro).

   El fondo pinta de borde a borde (full-bleed) rompiendo el container
   boxed; el contenido se re-acota con padding para seguir alineado
   con el resto de la página.
   ═══════════════════════════════════════════════════════════════════ */

.portal-zona--con-fondo {
    position: relative;
    background-color: var(--zona-bg-color, transparent);
    background-image: var(--zona-bg-image, none);
    /* Escala elegida por el operador (llenar / entera / original). */
    background-size: var(--zona-bg-size, cover);
    /* Encuadre elegido por el operador; por defecto, SIEMPRE centrada. */
    background-position: var(--zona-bg-pos, center);
    background-repeat: no-repeat;
}
/* Velo oscuro sobre la imagen — mejora la legibilidad del texto claro.
   La opacidad la maneja `--zona-overlay` (fracción 0..1) que pone el
   template. Sin imagen, no hay velo. */
.portal-zona--con-fondo.portal-zona--tiene-imagen::before {
    content: "";
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, var(--zona-overlay, 0));
    pointer-events: none;
    z-index: 0;
}
/* El contenido de la zona queda por encima del velo. */
.portal-zona--con-fondo > * {
    position: relative;
    z-index: 1;
}

/* Full-bleed sin trucos: la sección YA ocupa todo el ancho (es una
   franja apilada en `.portal-main`), así que el fondo llega a los bordes
   por sí solo. El contenido queda acotado por `.portal-zona-inner`. */

/* ── Tono de texto según el fondo ─────────────────────────────────
   Reasignamos los tokens de texto del sistema para que TODO el texto
   "suelto" de la sección (títulos, párrafos, prosa de texto libre)
   contraste con el fondo. Las tarjetas con superficie propia se
   resetean más abajo para que conserven su texto oscuro. */
.portal-zona--texto-claro {
    --c-text:            var(--c-text-light);
    --c-text-strong:     var(--c-text-light);
    --c-text-soft:       rgba(242, 242, 242, .82);
    --c-text-faint:      rgba(242, 242, 242, .70);
    --c-muted:           rgba(242, 242, 242, .70);
    --c-text-brand:      var(--c-text-light);
    color:               var(--c-text-light);
}
.portal-zona--texto-oscuro {
    --c-text:            var(--c-text-on-light);
    --c-text-strong:     var(--c-text-on-light);
    --c-text-brand:      var(--c-primary);
    color:               var(--c-text-on-light);
}

/* Reset de tarjetas/superficies: cualquier elemento con fondo CLARO
   propio dentro de una sección con texto claro vuelve al tema normal
   (superficie clara + texto oscuro). Sin esto, el texto de las tarjetas
   heredaría el texto claro de la sección y quedaría claro-sobre-claro.
   Va con `:where()` (especificidad 0) para no pelearle a las reglas
   propias de cada bloque.

   ── Lista canónica de superficies claras ──
   Es el conjunto de selectores-RAÍZ de `estilosBloques.css` que pintan
   una superficie clara (var(--c-bg-card) / var(--c-bg) /
   var(--c-bg-subtle) / #fff). Los descendientes de una raíz cubierta
   HEREDAN el reset — solo hay que listar la raíz.

   Para auditarla cuando se agregan bloques: parsear los bloques CSS de
   estilosBloques.css buscando `background(-color)` claro y comparar los
   selectores contra esta lista (un grep de una sola línea NO alcanza:
   pierde los bloques donde background no es la primera declaración).

   OJO: NO incluir superficies OSCURAS (ej. `.portal-kpi-card--accent-dark`,
   `.portal-buscador-hero--fondo-banda`, los fondos oscuros de
   `portal-texto--fondo-*`) — esas ya llevan texto claro por diseño y
   romperían si les forzamos texto oscuro. */
.portal-zona--texto-claro :where(
    .portal-acceso-modal-dialog,
    .portal-accesos--con-imagen .portal-acceso,
    .portal-accesos--franja-utilitaria .portal-accesos-grid,
    .portal-accesos--pildora-icono-izq .portal-acceso,
    .portal-acordeon-item, .portal-antes-despues-comparador,
    .portal-api-consulta--banda, .portal-api-consulta-tabla,
    .portal-api-consulta-tabla-wrap,
    .portal-api-consulta-tarjeta, .portal-autoridad, .portal-autoridad-fila,
    .portal-autoridades-orga-trigger, .portal-banner--compacto,
    .portal-banner-noticias--lateral, .portal-banner-noticias-dot,
    .portal-bloque-pendiente,
    .portal-buscador-hero--fondo-claro, .portal-buscador-hero-form,
    .portal-buscador-hero--fondo-ninguno .portal-buscador-hero-chip,
    .portal-buscar-limpiar, .portal-buscar-seccion,
    .portal-card, .portal-carrusel, .portal-carrusel-dot, .portal-contacto,
    .portal-contacto-gracias-box,
    .portal-cubo-noticias-btn, .portal-cubo-noticias-cara,
    .portal-doc-buscador, .portal-doc-card-inner, .portal-documento,
    .portal-documentos--sec-acordeon, .portal-documentos--sec-agrupado,
    .portal-documentos--sec-tabs, .portal-documentos-acordeon-item,
    .portal-documentos-tabs-nav, .portal-embed-rs-vacio,
    .portal-faq-item, .portal-formulario,
    .portal-formulario-control, .portal-formulario-gracias,
    .portal-galeria-item, .portal-grafico,
    .portal-ig-reels, .portal-ig-reels-card, .portal-ig-reels-card-play,
    .portal-kpi--minimalista, .portal-kpi--tarjeta, .portal-kpi-card,
    .portal-kpi-card--bg, .portal-mapa-geojson-loading,
    .portal-mapa-modal-card, .portal-mapa-modal-cerrar,
    .portal-material-card, .portal-material-categoria, .portal-material-tipo,
    .portal-material-toolbar, .portal-material-vacio,
    .portal-normativa, .portal-normativa-modal-cerrar, .portal-noticia,
    .portal-noticia-card, .portal-noticia-destacada,
    .portal-noticia-detalle-galeria-dot, .portal-noticias-buscador,
    .portal-noticias-buscador-clear, .portal-noticias-pag-btn,
    .portal-noticias-sin-datos, .portal-orga-chart,
    .portal-pasos--horizontal .portal-pasos-item, .portal-progreso,
    .portal-repositorio-bloque,
    .portal-repositorio-card, .portal-rss-carrusel-flecha,
    .portal-rss-error, .portal-rss-item, .portal-rss-vacio,
    .portal-tabla-comparativa--cards,
    .portal-tabla-comparativa--clasica, .portal-tabla-comparativa--destacada,
    .portal-tabla-comparativa-tabla, .portal-tabla-comparativa-wrap,
    .portal-tabla-wrap,
    .portal-tabs--cards, .portal-tabs-boton, .portal-tabs-panel,
    .portal-texto--fondo-primary-suave, .portal-texto--fondo-accent-suave,
    .portal-timeline--horizontal, .portal-timeline-badge--futuro,
    .portal-timeline-contenido, .portal-timeline-hito--estado-futuro,
    .portal-timeline-marcador, .portal-timeline-modal-cerrar,
    .portal-timeline-modal-dialog,
    .portal-timeline-popover, .portal-timeline-popover-cerrar,
    .portal-timeline-vacio,
    .portal-visor-documento-fallback, .portal-visor-documento-frame,
    .portal-visor-documento-tab
):not(.portal-bloque-wrapper--sin-fondo *):not(.portal-bloque-lateral--sin-fondo *) {
    --c-text:            var(--c-text-on-light);
    --c-text-strong:     #333333;
    --c-text-soft:       #555555;
    --c-text-faint:      #666666;
    --c-muted:           #6c757d;
    --c-text-brand:      var(--c-primary);
    color:               var(--c-text-on-light);
}


/* ═══════════════════════════════════════════════════════════════════
   Fondo tipo vidrio (glass) opt-in por bloque (config `fondo_transparente`)
   ───────────────────────────────────────────────────────────────────
   El operador puede pedir que un bloque no pinte su superficie sólida y
   en su lugar se vea como una "glass card": superficie translúcida +
   desenfoque sutil del fondo de la sección + borde tenue. Se resuelve de
   forma AGNÓSTICA al tipo de bloque: los tokens de superficie y borde
   pasan a valores translúcidos (cualquier bloque que pinte con
   var(--c-bg-card)/var(--c-border*) hereda el look sin tocar su template).
   El desenfoque `backdrop-filter` va aparte, en las superficies card-like
   (no hereda por variable). Sobre secciones de texto claro, el texto
   sigue el tono claro de la sección (el reset de contraste se desactiva
   dentro de `--sin-fondo`, ver el `:not()` de más arriba).
   ═══════════════════════════════════════════════════════════════════ */
.portal-bloque-wrapper--sin-fondo,
.portal-bloque-lateral--sin-fondo {
    --c-bg-card:        rgba(255, 255, 255, .14);
    --c-bg-subtle:      rgba(255, 255, 255, .08);
    --c-bg:             rgba(255, 255, 255, .14);
    --c-border:         rgba(255, 255, 255, .42);
    --c-border-light:   rgba(255, 255, 255, .30);
    --c-border-lighter: rgba(255, 255, 255, .22);
}
/* Efecto vidrio con más "cuerpo": refracción (backdrop saturate+brightness
   + capa diagonal de brillo) y profundidad (sombra proyectada + reflejo
   interno en el borde superior). El desenfoque va en la propia superficie
   translúcida; lista card-like ampliable, se excluye el flip (transform 3D
   rompe el blur). Variable local `--pb-glass-blur` para poder afinarlo. */
.portal-bloque-wrapper--sin-fondo,
.portal-bloque-lateral--sin-fondo { --pb-glass-blur: 9px; }
.portal-bloque-wrapper--sin-fondo :is(.portal-card:not(.portal-card--flip), .portal-material-card, .portal-doc-card-inner, .portal-documento, .portal-kpi-card, .portal-kpi--tarjeta, .portal-kpi--minimalista, .portal-faq-item, .portal-acordeon-item, .portal-noticia-card, .portal-normativa, .portal-repositorio-card, .portal-pasos-item, .portal-timeline-contenido, .portal-tabs-panel, .portal-contacto, .portal-formulario, .portal-tabla-comparativa--cards),
.portal-bloque-lateral--sin-fondo :is(.portal-card:not(.portal-card--flip), .portal-material-card, .portal-doc-card-inner, .portal-documento, .portal-kpi-card, .portal-kpi--tarjeta, .portal-kpi--minimalista, .portal-faq-item, .portal-acordeon-item, .portal-noticia-card, .portal-normativa, .portal-repositorio-card, .portal-pasos-item, .portal-timeline-contenido, .portal-tabs-panel, .portal-contacto, .portal-formulario, .portal-tabla-comparativa--cards) {
    -webkit-backdrop-filter: blur(var(--pb-glass-blur)) saturate(1.7) brightness(1.06);
    backdrop-filter: blur(var(--pb-glass-blur)) saturate(1.7) brightness(1.06);
    background-image: linear-gradient(135deg, rgba(255, 255, 255, .18), rgba(255, 255, 255, .03) 46%, rgba(255, 255, 255, 0) 72%);
    box-shadow:
        0 10px 34px rgba(0, 0, 0, .30),
        inset 0 1px 0 rgba(255, 255, 255, .45),
        inset 0 0 0 1px rgba(255, 255, 255, .10);
}
/* Los controles de formulario dentro de una glass card NO deben heredar
   el texto claro de la sección: su superficie propia es clara y quedaban
   ilegibles (ej. el select de categoría con las opciones casi invisibles).
   Les damos una superficie clara sólida + texto oscuro. */
.portal-bloque-wrapper--sin-fondo :is(select, textarea, input:not([type="checkbox"]):not([type="radio"])),
.portal-bloque-lateral--sin-fondo :is(select, textarea, input:not([type="checkbox"]):not([type="radio"])) {
    color: var(--c-text-on-light);
    background-color: rgba(255, 255, 255, .92);
    border-color: rgba(255, 255, 255, .55);
}
/* Sobre secciones de texto claro, el texto del bloque transparente sigue
   el tono claro de la sección de forma natural: el reset de contraste de
   abajo se DESACTIVA para todo lo que cuelga de un wrapper `--sin-fondo`
   (ver el `:not()` en su selector). No re-iluminamos el subárbol a mano
   —eso pisaba el color propio de chips/badges/botones (ej. el badge de
   categoría quedaba con texto casi blanco sobre su fondo claro)—; cada
   componente con color propio se respeta y sólo el texto “suelto”
   hereda el tono claro de la sección. */


/* ═══════════════════════════════════════════════════════════════════
   Hero (parte del shell, no un bloque opcional)
   3 variantes: imagen-overlay, split, degradado
   ═══════════════════════════════════════════════════════════════════ */

.portal-hero {
    position: relative;
    color: var(--c-text-light);
    overflow: hidden;
    display: flex;
    align-items: center;
}

.portal-hero-contenido {
    position: relative;
    z-index: 2;
    width: 100%;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    /* Padding propio del hero — independiente del .portal-container.
       Escalamos con el viewport con `clamp()`: en pantallas chicas
       damos 1.5 rem (24 px) de aire, en desktop ancho llegamos a
       3 rem (48 px) para que el título y el CTA no queden "colgados"
       del borde de la página (feedback recurrente: el hero se veía
       pegado al filo del viewport en monitores grandes). El valor
       vw hace la transición continua entre esos dos extremos.
       Ganamos especificidad (0,1,0 → tied con .portal-container pero
       declarado después, así esta gana en la cascada). */
    padding-inline: clamp(1.5rem, 4vw, 3rem);
}
/* En mobile reducimos un pelín para no sentir que el hero está
   "metido hacia adentro" cuando los otros bloques tienen padding
   más chico. 1.25rem es el sweet spot: respira pero acompaña la
   compresión general del layout. `clamp()` de arriba ya lo cubre
   por vw en esos anchos, pero mantenemos el override explícito por
   robustez ante viewports muy chicos donde vw pierde precisión. */
@media (max-width: 480px) {
    .portal-hero-contenido {
        padding-inline: 1.25rem;
    }
}
.portal-hero-text {
    /* Wrapper de título + subtítulo. Necesario para que en el tamaño
       compacto el CTA pueda salir al costado del texto (flex sibling). */
    display: flex;
    flex-direction: column;
}

/* La base usa :where() para que su especificidad sea (0,1,1) en vez de
   (0,2,1). Si dejamos `.portal-hero h1.portal-hero-titulo` calificado con
   el tag h1, gana sobre los modificadores `.portal-hero-split--bg
   .portal-hero-titulo` (0,2,0) y el color del split queda pisado. Con
   :where(), los modificadores pueden ganarle sin pelear especificidad. */
:where(.portal-hero) h1.portal-hero-titulo {
    font-weight: 800;
    line-height: 1.1;
    margin: 0 0 1rem;
    color: var(--c-text-light);
    /* Respetar Enter / tabs / espacios múltiples del editor — el
       operador puede forzar saltos de línea o sangría en el título
       y se ven reflejados en el portal. `pre-wrap` mantiene esos
       whitespaces literales pero permite que el browser haga
       wrap automático cuando se queda sin ancho. */
    white-space: pre-wrap;
}
:where(.portal-hero) p.portal-hero-subtitulo {
    margin: 0 0 1.5rem;
    max-width: 60ch;
    opacity: .95;
    color: var(--c-text-light);
    white-space: pre-wrap;
}
/* CTA del hero: visualmente es un botón. La regla de enlaces genérica de
   theme-config.css ya vive en `:where(a:not(.btn)...)` (especificidad
   0,0,0) por lo que NO nos pisa el inline `style="color: ..."` que
   escribe el template a partir del picker del editor. El color inline
   siempre gana; los valores acá son el fallback cuando el bloque no
   trae `cta_text_color` (páginas viejas, defaults). Antes usábamos
   `!important` para vencer aquella regla; sacarlo permite que el color
   elegido por el operador se aplique realmente. */
.portal-hero-cta {
    display: inline-block;
    background: var(--c-primary);
    color: var(--c-text-light);
    padding: .75rem 1.5rem;
    border-radius: 4px;
    font-weight: 600;
    text-decoration: none;
    transition: background .15s ease;
    border: 0;
    flex-shrink: 0;
}
.portal-hero-cta:hover {
    background: var(--c-primary-dark);
    text-decoration: none;
}

/* Alineación del CTA dentro del hero. El contenedor default
   (`.portal-hero-contenido` y `.portal-hero-split-text`) es
   flex-direction: column con align-items: flex-start, así que
   `align-self` sobre el CTA lo mueve horizontalmente. En el tamaño
   "sm" el layout pasa a row y el align-self deja de tener sentido
   horizontal — ahí sobreescribimos para preservar el comportamiento
   original (CTA al costado del texto, empujado a la derecha). */
.portal-hero-cta--align-left   { align-self: flex-start; }
.portal-hero-cta--align-center { align-self: center; }
.portal-hero-cta--align-right  { align-self: flex-end; }
.portal-hero--tam-sm .portal-hero-cta--align-left,
.portal-hero--tam-sm .portal-hero-cta--align-center,
.portal-hero--tam-sm .portal-hero-cta--align-right {
    align-self: auto;
}


/* ── Tamaños del hero (lg / md / sm) ──────────────────────────────
   Aplicables a las tres variantes (imagen-overlay, split, degradado).
   `lg` es el default; `md` recorta ~25% de altura y baja la fuente;
   `sm` mueve el CTA al costado del texto en lugar de debajo. */

.portal-hero--tam-lg { min-height: 480px; }
.portal-hero--tam-lg .portal-hero-contenido { padding-block: 3.25rem; }
.portal-hero--tam-lg h1.portal-hero-titulo { font-size: clamp(2.1rem, 5vw, 3.5rem); }
.portal-hero--tam-lg p.portal-hero-subtitulo { font-size: clamp(1rem, 1.5vw, 1.25rem); }

.portal-hero--tam-md { min-height: 340px; }
.portal-hero--tam-md .portal-hero-contenido { padding-block: 2.25rem; }
.portal-hero--tam-md h1.portal-hero-titulo { font-size: clamp(1.65rem, 3.5vw, 2.4rem); }
.portal-hero--tam-md p.portal-hero-subtitulo { font-size: clamp(.92rem, 1.3vw, 1.05rem); }

.portal-hero--tam-sm { min-height: 220px; }
.portal-hero--tam-sm .portal-hero-contenido {
    padding-block: 1.5rem;
    /* Layout horizontal: texto a la izquierda, CTA a la derecha. */
    flex-direction: row;
    align-items: center;
    gap: 1.75rem;
    flex-wrap: wrap;
}
.portal-hero--tam-sm .portal-hero-text {
    flex: 1 1 auto;
    min-width: 220px;
}
.portal-hero--tam-sm h1.portal-hero-titulo {
    font-size: clamp(1.3rem, 2.5vw, 1.85rem);
    margin: 0 0 .25rem;
}
.portal-hero--tam-sm p.portal-hero-subtitulo {
    font-size: clamp(.85rem, 1.2vw, .98rem);
    margin: 0;
    max-width: 50ch;
}
.portal-hero--tam-sm .portal-hero-cta {
    margin-left: auto;   /* empuja a la derecha cuando hay espacio */
    padding: .6rem 1.2rem;
    font-size: .9rem;
}
@media (max-width: 600px) {
    .portal-hero--tam-sm .portal-hero-cta { margin-left: 0; }
}
@media (max-width: 480px) {
    .portal-hero--tam-sm .portal-hero-contenido {
        flex-direction: column;
        align-items: flex-start;
        gap: 1rem;
    }
    .portal-hero--tam-sm .portal-hero-text { min-width: 0; }
    .portal-hero--tam-sm .portal-hero-cta { width: 100%; text-align: center; }
}

/* Variante 1: imagen-overlay */
.portal-hero--imagen-overlay {
    background-image: var(--portal-hero-imagen,
        linear-gradient(135deg, var(--c-primary), var(--c-primary-light)));
    background-size: cover;
    background-position: center;
    background-color: var(--c-primary);
}
.portal-hero-overlay {
    position: absolute; inset: 0;
    background: linear-gradient(
        to right,
        color-mix(in srgb, var(--c-primary-dark) 78%, transparent) 0%,
        color-mix(in srgb, var(--c-primary) 55%, transparent) 80%
    );
    z-index: 1;
}

/* Variante 2: split */
.portal-hero--split {
    display: grid;
    grid-template-columns: 1fr 1fr;
    background: var(--c-bg-card);
    color: var(--c-text);
}
.portal-hero-split-text {
    display: flex;
    flex-direction: column;
    justify-content: center;
}
/* Colores default del split (modo "bg"). Envueltos en :where() para
   que cualquier modificador `.portal-hero-split--<color>` los pueda
   pisar sin pelear con la especificidad de la selección elemento+clase. */
:where(.portal-hero--split) h1.portal-hero-titulo  { color: var(--c-text-brand); }
:where(.portal-hero--split) p.portal-hero-subtitulo { color: var(--c-text-soft); }
.portal-hero-split-image {
    /* Gradiente fallback que se ve si NO hay imagen cargada — cuando
       el operador todavía no eligió foto, el slot muestra un gradiente
       institucional en vez de un bloque vacío. Con imagen, el <img>
       interno lo tapa por completo. */
    background-color: var(--c-primary-lighter);
    background-image: linear-gradient(135deg, var(--c-primary-light), var(--c-primary));
    /* overflow hidden: el <img> puede tener transform: scale (zoom del
       encuadre) que la haga sobresalir del slot — recortamos al borde
       del bloque para que el zoom = recorte cerrado, no = imagen
       desbordada sobre el texto adyacente. */
    overflow: hidden;
    position: relative;
}
.portal-hero-split-image-img {
    /* Llena el slot completo. object-fit: cover + object-position
       (vía foco_estilos) replica el comportamiento del antiguo
       background-size: cover; background-position: X% Y% — pero
       suma soporte de transform: scale para que el zoom del modal
       de encuadre tenga efecto real en el portal. */
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* Tamaños aplicados sobre split — padding fluido con clamp para que el
   texto no quede pegado al borde en viewports anchos (el hero es
   full-bleed, sin .portal-container que lo restrinja). El piso del
   clamp asegura padding mínimo legible en mobile y el techo evita que
   en pantallas 4K el texto quede achicado contra el centro. */
.portal-hero--split.portal-hero--tam-lg .portal-hero-split-text { padding: 3rem clamp(2rem, 6vw, 6rem); }
.portal-hero--split.portal-hero--tam-lg .portal-hero-split-image { min-height: 300px; }
.portal-hero--split.portal-hero--tam-md .portal-hero-split-text { padding: 2.25rem clamp(1.5rem, 5vw, 4rem); }
.portal-hero--split.portal-hero--tam-md .portal-hero-split-image { min-height: 240px; }
.portal-hero--split.portal-hero--tam-sm .portal-hero-split-text { padding: 1.5rem clamp(1.25rem, 4vw, 2.5rem); }
.portal-hero--split.portal-hero--tam-sm .portal-hero-split-image { min-height: 180px; }

/* ── Color del lado de texto en split ──────────────────────────────
   Tres modificadores que pisan el fondo del .portal-hero-split-text y
   el color de los textos. Como el bloque `.portal-hero-split-text` no
   tiene fondo propio (lo hereda del wrapper `.portal-hero--split` que
   pinta `--c-bg-card`), basta con setear el background en el modifier
   directamente sobre el inner para que coexista con la mitad de
   imagen (que siempre se ve como imagen, sin importar el color del
   texto). */
.portal-hero-split--bg .portal-hero-split-text {
    background: var(--c-bg-card);
}
.portal-hero-split--bg .portal-hero-titulo    { color: var(--c-text-brand); }
.portal-hero-split--bg .portal-hero-subtitulo { color: var(--c-text-soft); }

.portal-hero-split--primary .portal-hero-split-text {
    background: var(--c-primary);
}
.portal-hero-split--primary .portal-hero-titulo,
.portal-hero-split--primary .portal-hero-subtitulo {
    color: var(--c-text-light);
}

.portal-hero-split--grad-primary .portal-hero-split-text {
    background: linear-gradient(135deg,
        var(--c-primary-light) 0%,
        var(--c-primary) 55%,
        var(--c-primary-dark) 100%);
}
.portal-hero-split--grad-primary .portal-hero-titulo,
.portal-hero-split--grad-primary .portal-hero-subtitulo {
    color: var(--c-text-light);
}

@media (max-width: 768px) {
    .portal-hero--split { grid-template-columns: 1fr; }
    .portal-hero--split .portal-hero-split-image { min-height: 220px; order: -1; }
}

/* Variante 3: degradado */
.portal-hero--degradado {
    background: linear-gradient(135deg, var(--c-primary) 0%, var(--c-primary-light) 100%);
    color: var(--c-text-light);
}


/* ═══════════════════════════════════════════════════════════════════
   Estado: site no listo (tenant sin home publicada)
   ═══════════════════════════════════════════════════════════════════ */

.portal-no-listo {
    text-align: center;
    padding: 5rem 1rem;
}
.portal-no-listo-icono {
    font-size: 4rem;
    color: var(--c-warning);
    margin-bottom: 1rem;
}
.portal-no-listo h1 {
    color: var(--c-text-brand);
    margin-bottom: .5rem;
}
.portal-no-listo p { color: var(--c-text-soft); max-width: 50ch; margin: 0 auto; }


/* ═══════════════════════════════════════════════════════════════════
   Topbar de contacto (configurable por tenant, opcional)
   ═══════════════════════════════════════════════════════════════════ */

.portal-topbar {
    background: var(--c-primary-dark, #0d2740);
    color: rgba(255,255,255,.85);
    font-size: .82rem;
}
.portal-topbar-inner {
    padding: .4rem 0;
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    justify-content: flex-end;
}
.portal-topbar-item {
    /* `inherit` para que cuando el operador setea color del texto del
       topbar como style inline en .portal-topbar, los items lo hereden
       sin pelear especificidad. */
    color: inherit;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    opacity: .9;
}
.portal-topbar-item:hover {
    color: inherit;
    opacity: 1;
    text-decoration: none;
}


/* ═══════════════════════════════════════════════════════════════════
   Dropdown del navbar y botón CTA destacado
   ═══════════════════════════════════════════════════════════════════ */

.portal-nav-dropdown {
    position: relative;
    display: inline-block;
}
.portal-nav-dropdown > .portal-nav-item i {
    font-size: .7rem;
    margin-left: .15rem;
    opacity: .7;
    transition: transform .18s ease;
}
.portal-nav-dropdown:hover > .portal-nav-item i,
.portal-nav-dropdown:focus-within > .portal-nav-item i {
    transform: rotate(180deg);
    opacity: 1;
}
.portal-nav-dropdown-menu {
    position: absolute;
    /* `top: calc(100% + 10px)` separa visualmente el menú del link,
       y un ::before invisible cubre ese gap para que el hover NO se
       pierda al bajar el mouse (problema original: con margin-top
       el gap quedaba fuera del área hoverable y el menú se cerraba). */
    top: calc(100% + 10px);
    left: 0;
    background: var(--c-bg-card);
    border: 1px solid var(--c-border-light);
    border-top: 3px solid var(--c-primary);
    border-radius: 0 0 8px 8px;
    box-shadow: 0 12px 28px rgba(0, 0, 0, .12);
    padding: .4rem 0;
    min-width: 240px;
    z-index: 100;
    /* Visibilidad por opacidad + visibility — permite transición suave
       sin perder accesibilidad (display:none rompía la animación). */
    opacity: 0;
    visibility: hidden;
    transform: translateY(-4px);
    transition: opacity .18s ease, transform .18s ease, visibility .18s;
}
/* Puente invisible que cubre el gap visual entre el link y el menú —
   sin esto, mover el mouse hacia el menú lo cerraba al cruzar los 10px. */
.portal-nav-dropdown-menu::before {
    content: '';
    position: absolute;
    top: -13px;
    left: 0;
    right: 0;
    height: 13px;
    background: transparent;
}
.portal-nav-dropdown:hover .portal-nav-dropdown-menu,
.portal-nav-dropdown:focus-within .portal-nav-dropdown-menu {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}
.portal-nav-dropdown-item {
    display: block;
    padding: .6rem 1.1rem;
    color: var(--c-text);
    text-decoration: none;
    font-size: .95rem;
    font-weight: 500;
    border-left: 3px solid transparent;
    transition: background .15s ease, color .15s ease, border-color .15s ease, padding-left .15s ease;
}
.portal-nav-dropdown-item:hover,
.portal-nav-dropdown-item:focus-visible {
    background: rgba(var(--c-primary-rgb, 19, 48, 77), .06);
    color: var(--c-primary);
    border-left-color: var(--c-primary);
    padding-left: 1.25rem;
    text-decoration: none;
    outline: none;
}

/* CTA del navbar: mismo criterio que .portal-hero-cta — la regla de
   enlaces genérica de theme-config.css ya está envuelta en `:where()`
   (especificidad 0,0,0), así que NO hace falta `!important` sobre el
   color. Sacarlo permite que un futuro picker de color de texto del
   CTA (o un override por tenant vía CSS variable) pueda aplicarse
   sin trabas. */
.portal-nav-cta {
    background: var(--c-primary);
    color: var(--c-text-light);
    padding: .45rem 1rem;
    border-radius: 4px;
    font-weight: 600;
    font-size: .92rem;
    transition: background .15s ease;
}
.portal-nav-cta:hover {
    background: var(--c-primary-dark);
    text-decoration: none;
}


/* ═══════════════════════════════════════════════════════════════════
   Footer institucional (columnas configurables por tenant)
   ═══════════════════════════════════════════════════════════════════ */

.portal-footer-banda {
    /* Banda decorativa que va por ENCIMA del strip de color del footer.
       Pensada para piezas tipo silueta institucional con cielo
       transparente o claro.

       Usamos `aspect-ratio: 8/1` (aprox. panorama típico de siluetas
       institucionales) + `background-size: contain` para que la imagen
       se vea COMPLETA en el ancho del viewport. La altura se calcula
       sola en proporción: en pantalla 1920px da ~240px, en mobile
       400px da ~50px. Si la imagen real tiene otro aspect, queda algo
       de aire arriba/abajo — preferible a recortarla con `cover`. */
    width: 100%;
    aspect-ratio: 8 / 1;
    margin-top: 3rem;
    background-position: center bottom;
    background-repeat: no-repeat;
    background-size: contain;
    background-color: transparent;
}
.portal-footer {
    background: var(--c-primary);
    color: rgba(255,255,255,.92);
    padding-block: 2rem 1.25rem;
    margin-top: 3rem;
    position: relative;
    overflow: hidden;
}
/* Si hay banda decorativa antes del footer, no duplicamos el margin-top
   superior del footer (la banda ya separa del contenido de la página). */
.portal-footer-banda + .portal-footer { margin-top: 0; }
.portal-footer-bg {
    position: absolute;
    inset: 0;
    background-size: cover;
    background-position: center bottom;
    opacity: .18;
    pointer-events: none;
}
.portal-footer-inner {
    position: relative;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 1.5rem 2rem;
    align-items: flex-start;
    margin-bottom: 1.25rem;
}
.portal-footer-col {
    min-width: 0;
}
.portal-footer-titulo {
    font-size: .95rem;
    color: var(--c-text-light);
    margin: 0 0 .55rem 0;
    font-weight: 700;
    letter-spacing: .02em;
}
.portal-footer-line-img,
.portal-footer-link-img {
    /* Items en modo "imagen" dentro de columnas tipo contacto y links.
       Se acotan en altura para que convivan con otros items y no
       desproporcionen la columna. `object-fit: contain` preserva la
       proporción aunque la imagen sea ancha. */
    display: block;
    max-width: 100%;
    max-height: 40px;
    height: auto;
    width: auto;
    object-fit: contain;
}
.portal-footer-link--img {
    /* Cuando el item de la columna `links` es una imagen, sacamos el
       padding/decoración del link de texto para que la imagen quede
       visualmente limpia. */
    display: inline-block;
    padding: 0;
    border: 0;
}
.portal-footer-inst-nombre {
    display: block;
    font-size: 1.05rem;
    margin-bottom: .35rem;
    color: var(--c-text-light);
    font-weight: 700;
}
.portal-footer-meta {
    color: var(--c-border);
    font-size: .88rem;
    margin: 0;
    line-height: 1.55;
}
.portal-footer-line {
    color: var(--c-border);
    font-size: .92rem;
    line-height: 1.5;
    margin-bottom: .55rem;
    display: flex;
    align-items: center;
    gap: .55rem;
}
.portal-footer-line:last-child { margin-bottom: 0; }
.portal-footer-line > .bi,
.portal-footer-line .portal-footer-line-link > .bi {
    font-size: 1.15rem;
    flex-shrink: 0;
    line-height: 1;
}
.portal-footer-line-link {
    color: rgba(255,255,255,.85);
    text-decoration: none;
    border-bottom: 1px dotted rgba(255,255,255,.35);
    display: inline-flex;
    align-items: center;
    gap: .55rem;
}
.portal-footer-line-link:hover {
    color: var(--c-text-light);
    border-bottom-color: rgba(255,255,255,.7);
    text-decoration: none;
}
.portal-footer-link {
    display: block;
    color: rgba(255,255,255,.85);
    text-decoration: none;
    font-size: .88rem;
    padding: .15rem 0;
    transition: color .15s ease;
}
.portal-footer-link:hover {
    color: var(--c-text-light);
    text-decoration: underline;
}

/* ── Estilo de los links del footer (FooterConfig.links_color /
      .links_underline) ────────────────────────────────────────────
   Los modificadores se aplican al elemento <footer> entero y se
   propagan a cualquier link descendiente (lista de links + líneas
   linkeables de la columna contacto). De esta manera la elección del
   operador rige para todo el footer, no para una sola columna.

   `--links-sutil` es el comportamiento default (claro translúcido);
   `--links-accent` usa el color institucional para que los enlaces
   resalten. `--links-underline` los muestra subrayados siempre.
*/
.portal-footer--links-accent .portal-footer-link,
.portal-footer--links-accent .portal-footer-line-link {
    color: var(--c-accent);
    border-bottom-color: transparent;
}
.portal-footer--links-accent .portal-footer-link:hover,
.portal-footer--links-accent .portal-footer-line-link:hover {
    color: var(--c-accent-dark, var(--c-accent));
    filter: brightness(1.15);
}
.portal-footer--links-underline .portal-footer-link,
.portal-footer--links-underline .portal-footer-line-link {
    text-decoration: underline;
    text-underline-offset: 3px;
    /* La columna `contacto` usa border-bottom para subrayar; lo
       desactivamos cuando el operador eligió subrayado de texto
       para no tener dos líneas. */
    border-bottom: 0;
}
.portal-footer-logos {
    display: flex;
    flex-wrap: wrap;
    gap: .65rem;
    align-items: center;
}
.portal-footer-logo-mini {
    height: 32px;
    width: auto;
    background: rgba(255,255,255,.92);
    padding: .2rem .35rem;
    border-radius: 4px;
}
.portal-footer-redes {
    display: flex;
    gap: .55rem;
}
.portal-footer-red {
    width: 36px; height: 36px;
    border-radius: 50%;
    background: rgba(255,255,255,.15);
    color: var(--c-text-light);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1rem;
    text-decoration: none;
    transition: background .15s ease;
}
.portal-footer-red:hover {
    background: rgba(255,255,255,.28);
    color: var(--c-text-light);
    text-decoration: none;
}
.portal-footer-copyright {
    position: relative;
    border-top: 1px solid rgba(255,255,255,.18);
    padding-top: .85rem;
    text-align: center;
    color: rgba(255,255,255,.65);
    font-size: .82rem;
}


/* ═══════════════════════════════════════════════════════════════════
   Responsivo
   ═══════════════════════════════════════════════════════════════════ */

@media (max-width: 900px) {
    /* Hamburguer visible — la nav se transforma en overlay full-screen */
    .portal-navbar-hamburguer { display: flex; }

    /* Nav pasa de inline a overlay fixed que cubre toda la pantalla.
       Por default está fuera de pantalla (translateX 100%) y entra
       deslizando desde la derecha cuando recibe la clase `is-open`. */
    .portal-nav {
        position: fixed;
        inset: 0;
        background: var(--c-primary);
        color: var(--c-text-light);
        flex-direction: column;
        /* `safe center` evita que cuando el contenido supere el alto del
           overlay el primer item quede recortado arriba (es lo que pasa
           con `center` puro: el desborde sale por ambos extremos y el
           inicio se va de pantalla). En modo `safe` el centrado solo se
           aplica si entra; si no, cae a `start`. */
        justify-content: safe center;
        align-items: center;
        gap: 1.25rem;
        padding: 5rem 2rem 3rem;
        margin: 0;
        z-index: 101;       /* debajo del hamburguer (102) que está sobre todo */
        transform: translateX(100%);
        transition: transform .3s cubic-bezier(.4, 0, .2, 1);
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
    }
    .portal-nav.is-open {
        transform: translateX(0);
    }

    /* Items en grande, blancos sobre el fondo primary. */
    .portal-nav .portal-nav-item,
    .portal-nav .portal-nav-item:visited {
        color: var(--c-text-light) !important;
        font-size: 1.4rem;
        font-weight: 700;
        text-align: center;
        padding: .25rem 0;
    }
    .portal-nav .portal-nav-item:hover,
    .portal-nav .portal-nav-item:focus-visible {
        color: var(--c-text-light) !important;
        opacity: .85;
    }
    /* Dropdowns: en mobile el "padre" sigue siendo navegable y los
       hijos se listan abajo, indentados, en tipografía más chica. */
    .portal-nav-dropdown {
        text-align: center;
    }
    .portal-nav-dropdown-menu {
        position: static;
        background: transparent;
        box-shadow: none;
        border: 0;
        border-top: 0;
        border-radius: 0;
        padding: .5rem 0 0;
        margin: 0;
        min-width: 0;
        display: flex;
        flex-direction: column;
        gap: .35rem;
        /* Anulamos la animación opacity/transform del desktop — en mobile
           el menú se muestra siempre (es un overlay vertical, no un popover). */
        opacity: 1;
        visibility: visible;
        transform: none;
        transition: none;
    }
    .portal-nav-dropdown-menu::before { display: none; }
    .portal-nav-dropdown-item,
    .portal-nav-dropdown-item:visited {
        color: var(--c-text-light);
        opacity: .85;
        font-size: 1.05rem;
        font-weight: 500;
        text-align: center;
    }
    .portal-nav-dropdown-item:hover {
        color: var(--c-text-light);
        opacity: 1;
    }
    /* CTA dentro del overlay: pieza destacada, fondo claro sobre el
       primary del menú para mantener el contraste invertido. */
    .portal-nav .portal-nav-cta {
        background: var(--c-bg);
        color: var(--c-text-brand);
        padding: .75rem 1.5rem;
        border-radius: 999px;
        font-weight: 700;
        font-size: 1rem;
        margin-top: 1rem;
    }

    /* En mobile pisamos `min-height` SOLO para los heros mediano y
       grande — el "compacto" (tam-sm) debe conservar SU propia altura
       chica también en mobile, sino el operador elige compacto y el
       celular lo agranda igual. */
    .portal-hero--tam-md,
    .portal-hero--tam-lg { min-height: 360px; }
    /* En mobile la banda mantiene su aspect-ratio (8:1) pero igualamos
       el margin-top al resto de las zonas. La altura sale calculada
       del ancho del viewport. */
    .portal-footer-banda { margin-top: 2rem; }
    .portal-topbar-inner { justify-content: center; }
}

@media (max-width: 768px) {
    /* Reglas extra para pantallas chiquitas — ya las tenía el portal
       pero las dejamos acotadas a no-navbar para no pisar la lógica
       del overlay de arriba. */
}

/* ─── Botón flotante "Ir arriba" ─────────────────────────────────
   Aparece cuando el visitante scrolleó más allá del umbral. El widget
   de accesibilidad ocupa la esquina inferior izquierda; este vive en
   la derecha para no chocar. Fade-in/out controlado por la clase
   `.is-visible` que setea el JS en cada scroll. */
.portal-ir-arriba {
    position: fixed;
    right: 1.25rem;
    bottom: 1.25rem;
    z-index: 9990;
    width: 48px;
    height: 48px;
    border-radius: 50%;
    border: 0;
    background: var(--c-primary, #13304D);
    color: var(--c-text-light, #FFFFFF);
    cursor: pointer;
    box-shadow: 0 .5rem 1.1rem rgba(0, 0, 0, .22);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1.25rem;
    line-height: 1;
    /* Estado por default = invisible y no clickeable. El JS toggle la
       clase `.is-visible` cuando el scroll supera el umbral. Usamos
       opacity + pointer-events (NO display:none) para que la transición
       de fade pueda correr en ambos sentidos. */
    opacity: 0;
    transform: translateY(8px);
    pointer-events: none;
    transition: opacity .18s ease, transform .18s ease,
                background .15s ease, box-shadow .15s ease;
}
.portal-ir-arriba.is-visible {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
}
.portal-ir-arriba:hover,
.portal-ir-arriba:focus-visible {
    background: var(--c-primary-dark, #0e2238);
    box-shadow: 0 .75rem 1.4rem rgba(0, 0, 0, .28);
    outline: none;
}
/* Cuando el botón queda visualmente por encima del footer (que está
   pintado con --c-primary), invertimos la pareja de colores: pasamos a
   fondo claro (--c-bg) + ícono primary. Espejo del mismo trato que
   `.a11y-widget.is-over-footer .a11y-fab` para mantener consistencia
   visual entre los dos FABs flotantes. */
.portal-ir-arriba.is-over-footer {
    background: var(--c-bg, #FFFFFF);
    color: var(--c-primary, #13304D);
}
.portal-ir-arriba.is-over-footer:hover,
.portal-ir-arriba.is-over-footer:focus-visible {
    background: var(--c-bg, #FFFFFF);
    color: var(--c-primary, #13304D);
    filter: brightness(.96);
}
@media (prefers-reduced-motion: reduce) {
    .portal-ir-arriba { transition: none; }
}


/* ════════════════════════════════════════════════════════════════════
   Recursos descargables (enlace inline → modal)

   Enlaces insertados desde CKEditor con el botón 📎 Recurso
   (`<a href="#pb-recurso=1,2,3">…</a>`). En el portal público
   `portal-recursos.js` les agrega la clase `.portal-recurso-link`
   y al click abre `_modal_recursos.html` con la lista de descargas.
   ════════════════════════════════════════════════════════════════════ */

/* ── Estilo del enlace inline ──────────────────────────────────────
   Pill con ícono de descarga a la izquierda del texto. Se ve
   visiblemente distinto a un enlace común para señalarle al lector
   que NO navega a otra página sino que abre un panel con descargas. */
.portal-recurso-link {
    display: inline-flex;
    align-items: center;
    gap: .4em;
    padding: .1em .55em;
    border-radius: 999px;
    background: color-mix(in srgb, var(--c-primary, #4e2d8c) 8%, var(--c-bg-card, #fff));
    color: var(--c-primary, #4e2d8c);
    border: 1px solid color-mix(in srgb, var(--c-primary, #4e2d8c) 25%, transparent);
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    transition: background .15s ease, border-color .15s ease;
}
.portal-recurso-link::before {
    content: '\F30A';                       /* bi-download */
    font-family: 'bootstrap-icons';
    font-weight: normal;
    font-size: .9em;
}
.portal-recurso-link:hover,
.portal-recurso-link:focus-visible {
    background: color-mix(in srgb, var(--c-primary, #4e2d8c) 16%, var(--c-bg-card, #fff));
    border-color: color-mix(in srgb, var(--c-primary, #4e2d8c) 45%, transparent);
    text-decoration: none;
}

/* ── Modal ──────────────────────────────────────────────────────── */
/* IMPORTANTE: el `display: flex` siguiente le gana al `display: none`
   que el user-agent stylesheet aplica al atributo HTML `[hidden]`.
   Sin esta regla explícita el modal se renderiza visible al cargar
   cualquier página del portal y queda colgado en "Cargando…" hasta
   que el operador lo cierra. La regla también aplica a los hijos
   del modal (.portal-recursos-lista tiene display:flex y arrancaría
   visible aunque el portal-recursos.js le ponga el atributo). */
.portal-recursos-modal[hidden],
.portal-recursos-modal [hidden] {
    display: none !important;
}
.portal-recursos-modal {
    position: fixed;
    inset: 0;
    /* Stack arriba de cualquier otro modal del portal — caso típico:
       el operador abre un acceso directo (z-index 10000) y desde el
       texto del modal hay un link a recursos. Sin esto, el modal de
       recursos quedaba debajo del modal de acceso. */
    z-index: 10010;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
}
.portal-recursos-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(15, 23, 42, .55);
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
}
.portal-recursos-dialog {
    position: relative;
    z-index: 1;
    width: min(540px, 100%);
    max-height: min(80vh, 720px);
    display: flex;
    flex-direction: column;
    background: var(--c-bg-card, #fff);
    border-radius: 14px;
    box-shadow: 0 20px 60px rgba(0, 0, 0, .35);
    overflow: hidden;
    animation: portal-recursos-aparece .18s ease-out;
}
@keyframes portal-recursos-aparece {
    from { opacity: 0; transform: translateY(8px) scale(.98); }
    to   { opacity: 1; transform: none; }
}

/* Bloquear scroll de la página de fondo mientras el modal está abierto */
body.portal-recursos-open {
    overflow: hidden;
}

.portal-recursos-header {
    display: flex;
    align-items: center;
    gap: .75rem;
    padding: 1rem 1.25rem;
    background: var(--c-primary, #4e2d8c);
    color: var(--c-bg-card, #fff);
    border-bottom: 1px solid rgba(255, 255, 255, .12);
}
.portal-recursos-titulo {
    flex: 1;
    margin: 0;
    font-size: 1.05rem;
    font-weight: 700;
    line-height: 1.3;
    overflow: hidden;
    text-overflow: ellipsis;
    /* Hasta 2 líneas, el resto se trunca */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}
.portal-recursos-cerrar {
    border: none;
    background: transparent;
    color: rgba(255, 255, 255, .85);
    font-size: 1.5rem;
    line-height: 1;
    padding: .15rem .4rem;
    border-radius: 6px;
    cursor: pointer;
    transition: background .15s ease, color .15s ease;
}
.portal-recursos-cerrar:hover,
.portal-recursos-cerrar:focus-visible {
    background: rgba(255, 255, 255, .15);
    color: #fff;
    outline: none;
}

.portal-recursos-body {
    flex: 1;
    overflow-y: auto;
    padding: .85rem 1.25rem 1.25rem;
}

/* Estados (cargando / error / vacío) */
.portal-recursos-cargando,
.portal-recursos-error,
.portal-recursos-vacio {
    text-align: center;
    color: var(--c-muted, #64748b);
    font-size: .92rem;
    padding: 2rem 1rem;
}
.portal-recursos-error {
    color: #b91c1c;
}
.portal-recursos-spinner {
    display: inline-block;
    width: 16px;
    height: 16px;
    border: 2px solid color-mix(in srgb, var(--c-primary, #4e2d8c) 30%, transparent);
    border-top-color: var(--c-primary, #4e2d8c);
    border-radius: 50%;
    vertical-align: -3px;
    margin-right: .5rem;
    animation: portal-recursos-spin .8s linear infinite;
}
@keyframes portal-recursos-spin {
    to { transform: rotate(360deg); }
}

/* Lista de descargas */
.portal-recursos-lista {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: .4rem;
}
/* Item: row con info a la izq y dos acciones (Abrir / Descargar) a la der. */
.portal-recursos-item {
    display: flex;
    align-items: center;
    gap: .85rem;
    padding: .7rem .85rem;
    border-radius: 10px;
    background: color-mix(in srgb, var(--c-primary, #4e2d8c) 4%, var(--c-bg-card, #fff));
    border: 1px solid color-mix(in srgb, var(--c-primary, #4e2d8c) 14%, transparent);
    color: var(--c-text, #1e293b);
}
.portal-recursos-item-info {
    display: flex;
    align-items: center;
    gap: .85rem;
    flex: 1;
    min-width: 0;
}
.portal-recursos-item-icono {
    font-size: 1.55rem;
    color: var(--c-primary, #4e2d8c);
    flex-shrink: 0;
}
.portal-recursos-item-cuerpo {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: .15rem;
}
.portal-recursos-item-titulo {
    font-weight: 600;
    font-size: .95rem;
    color: var(--c-text-strong, #0f172a);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.portal-recursos-item-meta {
    font-size: .78rem;
    color: var(--c-muted, #64748b);
}

/* Acciones: pill buttons "Abrir" y "Descargar" */
.portal-recursos-item-acciones {
    display: flex;
    gap: .35rem;
    flex-shrink: 0;
}
.portal-recursos-item-accion-btn {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    padding: .35rem .7rem;
    border-radius: 999px;
    font-size: .8rem;
    font-weight: 600;
    text-decoration: none;
    border: 1px solid color-mix(in srgb, var(--c-primary, #4e2d8c) 25%, transparent);
    background: var(--c-bg-card, #fff);
    color: var(--c-primary, #4e2d8c);
    transition: background .15s ease, border-color .15s ease, color .15s ease;
    white-space: nowrap;
}
.portal-recursos-item-accion-btn:hover,
.portal-recursos-item-accion-btn:focus-visible {
    background: var(--c-primary, #4e2d8c);
    border-color: var(--c-primary, #4e2d8c);
    color: var(--c-bg, #fff);
    text-decoration: none;
    outline: none;
}
.portal-recursos-item-accion-btn i { font-size: .9rem; }

/* Mobile: dialog ocupa más alto, padding más ajustado */
@media (max-width: 480px) {
    .portal-recursos-modal { padding: 0; }
    .portal-recursos-dialog {
        width: 100%;
        max-height: 100vh;
        height: 100vh;
        border-radius: 0;
    }
    .portal-recursos-item-titulo { white-space: normal; }
}

/* ══════════════════════════════════════════════════════════════════
   Repositorio Documental — portal público
   Alineado con el estilo de Noticias (portal-noticias-*): mismo
   sistema de header + buscador + chips + cards + paginación + empty
   state. Los estilos comparten spacing, tipografías y tokens de
   color con el resto del portal para que el módulo se sienta parte
   del sistema y no como un anexo con su propio look.
   ══════════════════════════════════════════════════════════════════ */

.portal-repositorio-pagina {
    padding: 2.5rem 0 3.5rem;
    min-height: 60vh;
}

.portal-repositorio-pagina-header {
    margin-bottom: 2rem;
    text-align: center;
}
.portal-repositorio-pagina-titulo {
    font-size: 2.4rem;
    font-weight: 700;
    color: var(--c-text-strong);
    margin: 0 0 .35rem;
    line-height: 1.15;
}
.portal-repositorio-pagina-subtitulo {
    color: var(--c-muted);
    font-size: 1rem;
    margin: 0 0 1.5rem;
}

/* Buscador central estilo Noticias */
.portal-repositorio-buscador {
    display: flex;
    align-items: center;
    max-width: 620px;
    margin: 0 auto 1rem;
    background: var(--c-bg-card);
    border: 1px solid var(--c-border);
    border-radius: 999px;
    overflow: hidden;
    box-shadow: 0 1px 3px rgba(0, 0, 0, .04);
    transition: border-color .15s ease, box-shadow .15s ease;
}
.portal-repositorio-buscador:focus-within {
    border-color: var(--c-primary);
    box-shadow: 0 2px 10px rgba(0, 0, 0, .08);
}
.portal-repositorio-buscador-input {
    flex: 1;
    border: 0;
    outline: 0;
    padding: .7rem 1.1rem;
    background: transparent;
    color: var(--c-text);
    font-size: .95rem;
}
.portal-repositorio-buscador-input::placeholder { color: var(--c-muted); }
.portal-repositorio-buscador-btn,
.portal-repositorio-buscador-clear {
    border: 0;
    background: transparent;
    color: var(--c-muted);
    padding: 0 1rem;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    text-decoration: none;
    transition: color .15s ease;
}
.portal-repositorio-buscador-btn { color: var(--c-primary); font-size: 1.1rem; }
.portal-repositorio-buscador-btn:hover,
.portal-repositorio-buscador-clear:hover { color: var(--c-text-strong); }

/* Contenedor de filtros — agrupa el label y los selects en un bloque visual */
.portal-repositorio-zona-filtros {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: .6rem;
    max-width: 680px;
    margin: 0 auto 1.1rem;
    padding: .5rem .85rem;
    background: color-mix(in srgb, var(--c-primary) 6%, var(--c-bg-card));
    border: 1px solid color-mix(in srgb, var(--c-primary) 18%, var(--c-border));
    border-radius: 14px;
}
.portal-repositorio-filtros-label {
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--c-primary);
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    white-space: nowrap;
    flex-shrink: 0;
}

/* Filtros inline */
.portal-repositorio-filtros {
    display: flex;
    justify-content: center;
    flex-wrap: wrap;
    gap: .5rem;
    margin-bottom: 0;
}
.portal-repositorio-filtro { display: inline-flex; }
.portal-repositorio-filtro-select {
    background: var(--c-bg-card);
    border: 1px solid var(--c-border);
    color: var(--c-text);
    padding: .35rem 2rem .35rem .7rem;
    border-radius: 999px;
    font-size: .82rem;
    cursor: pointer;
    appearance: none;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23888'><path d='M4.427 6.427a.6.6 0 0 1 .849 0L8 9.15l2.724-2.723a.6.6 0 1 1 .849.849l-3.148 3.148a.6.6 0 0 1-.849 0L4.427 7.276a.6.6 0 0 1 0-.849z'/></svg>");
    background-repeat: no-repeat;
    background-position: right .55rem center;
    background-size: 14px 14px;
}
.portal-repositorio-filtro-select:hover { border-color: var(--c-primary); }
.portal-repositorio-filtro-select:focus { outline: 2px solid var(--c-primary); outline-offset: 1px; }

.portal-repositorio-pagina-resultado {
    color: var(--c-muted);
    font-size: .88rem;
    margin: .5rem 0 0;
}
.portal-repositorio-pagina-resultado-link {
    color: var(--c-primary);
    text-decoration: none;
    margin-left: .35rem;
}
.portal-repositorio-pagina-resultado-link:hover { text-decoration: underline; }

/* Chips */
.portal-repositorio-chip {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    padding: .22rem .6rem;
    background: color-mix(in srgb, var(--c-primary) 10%, var(--c-bg-card));
    color: var(--c-primary);
    font-size: .68rem;
    font-weight: 700;
    letter-spacing: .05em;
    text-transform: uppercase;
    border-radius: 999px;
    text-decoration: none;
}
.portal-repositorio-chip--link:hover {
    background: color-mix(in srgb, var(--c-primary) 18%, var(--c-bg-card));
    text-decoration: none;
}
.portal-repositorio-chip--destacado {
    background: var(--c-primary);
    color: var(--c-bg-card);
}

/* Grid + cards */
.portal-repositorio-pagina-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 1.15rem;
    margin-bottom: 2rem;
}
.portal-repositorio-card {
    background: var(--c-bg-card);
    border: 1px solid var(--c-border);
    border-radius: 12px;
    overflow: hidden;
    transition: border-color .15s ease, transform .15s ease, box-shadow .15s ease;
}
.portal-repositorio-card:hover {
    border-color: var(--c-primary);
    transform: translateY(-2px);
    box-shadow: 0 6px 20px rgba(0, 0, 0, .07);
}
.portal-repositorio-card--destacado { border-top: 3px solid var(--c-primary); }
.portal-repositorio-card-link {
    display: block;
    color: inherit;
    text-decoration: none;
    height: 100%;
}
.portal-repositorio-card-link:hover { color: inherit; text-decoration: none; }
.portal-repositorio-card-icono {
    background: color-mix(in srgb, var(--c-primary) 6%, var(--c-bg-card));
    color: var(--c-primary);
    padding: .9rem 1rem;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.7rem;
    border-bottom: 1px solid var(--c-border);
}
.portal-repositorio-card-body {
    padding: .9rem 1.1rem 1rem;
    display: flex;
    flex-direction: column;
    gap: .45rem;
    min-height: 132px;
}
.portal-repositorio-card-header {
    display: flex;
    gap: .35rem;
    flex-wrap: wrap;
}
.portal-repositorio-card-titulo {
    font-size: 1rem;
    font-weight: 700;
    color: var(--c-text-strong);
    margin: 0;
    line-height: 1.35;
}
.portal-repositorio-card-subtitulo {
    font-size: .84rem;
    color: var(--c-muted);
    margin: 0;
    line-height: 1.45;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.portal-repositorio-card-meta {
    margin-top: auto;
    padding-top: .65rem;
    border-top: 1px dashed var(--c-border);
    display: flex;
    justify-content: space-between;
    gap: .6rem;
    font-size: .76rem;
    color: var(--c-muted);
    flex-wrap: wrap;
}
.portal-repositorio-card-meta i { margin-right: .2rem; }
.portal-repositorio-card-dep {
    max-width: 55%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Paginación estilo Noticias */
.portal-repositorio-pag {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 1.5rem;
    margin: 2rem 0 1rem;
}
.portal-repositorio-pag-btn {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .55rem 1.1rem;
    border: 1px solid var(--c-border);
    border-radius: 999px;
    color: var(--c-text-strong);
    background: var(--c-bg-card);
    text-decoration: none;
    font-size: .88rem;
    transition: border-color .15s ease, color .15s ease, background .15s ease;
}
.portal-repositorio-pag-btn:hover:not(.is-disabled) {
    border-color: var(--c-primary);
    color: var(--c-primary);
    text-decoration: none;
}
.portal-repositorio-pag-btn.is-disabled {
    color: var(--c-muted);
    background: transparent;
    cursor: not-allowed;
    opacity: .55;
}
.portal-repositorio-pag-info { color: var(--c-muted); font-size: .88rem; }

/* Empty state */
.portal-repositorio-pagina-vacia {
    text-align: center;
    padding: 4rem 1rem;
    color: var(--c-muted);
}
.portal-repositorio-pagina-vacia i {
    display: block;
    font-size: 3rem;
    color: var(--c-border);
    margin-bottom: .8rem;
}
.portal-repositorio-pagina-vacia-link {
    display: inline-block;
    margin-top: 1rem;
    color: var(--c-primary);
    text-decoration: none;
    font-weight: 600;
}
.portal-repositorio-pagina-vacia-link:hover { text-decoration: underline; }

/* ── Detalle ─────────────────────────────────────────────── */
.portal-repositorio-detalle { padding: 2rem 0 3.5rem; }
.portal-repositorio-detalle-contenedor { max-width: 860px; }

.portal-repositorio-detalle-volver {
    display: inline-flex;
    align-items: center;
    color: var(--c-muted);
    text-decoration: none;
    font-size: .88rem;
    margin-bottom: 1.5rem;
    transition: color .15s ease;
}
.portal-repositorio-detalle-volver:hover { color: var(--c-primary); }

.portal-repositorio-detalle-header {
    margin-bottom: 1.5rem;
    padding-bottom: 1.2rem;
    border-bottom: 1px solid var(--c-border);
}
.portal-repositorio-detalle-chips {
    display: flex;
    gap: .35rem;
    flex-wrap: wrap;
    margin-bottom: .8rem;
}
.portal-repositorio-detalle-titulo {
    font-size: 2.1rem;
    font-weight: 700;
    color: var(--c-text-strong);
    margin: 0 0 .5rem;
    line-height: 1.2;
}
.portal-repositorio-detalle-bajada {
    font-size: 1.05rem;
    color: var(--c-text);
    margin: 0 0 1rem;
    line-height: 1.5;
}
.portal-repositorio-detalle-meta {
    display: flex;
    gap: 1.2rem;
    flex-wrap: wrap;
    color: var(--c-muted);
    font-size: .86rem;
}
.portal-repositorio-detalle-meta i { margin-right: .25rem; }

.portal-repositorio-detalle-resumen {
    background: color-mix(in srgb, var(--c-primary) 5%, var(--c-bg-card));
    border-left: 3px solid var(--c-primary);
    border-radius: 6px;
    padding: 1rem 1.2rem;
    margin: 0 0 1.5rem;
}
.portal-repositorio-detalle-resumen p {
    margin: 0;
    font-size: 1rem;
    line-height: 1.55;
    color: var(--c-text-strong);
}

/* Cuerpo del documento — HTML importado de los docs "sin PDF" (texto).
   Prosa legible + tablas (cuadros tarifarios) con scroll horizontal. */
.portal-repositorio-detalle-cuerpo {
    margin: 0 0 2rem;
    font-size: 1rem;
    line-height: 1.7;
    color: var(--c-text-strong);
    overflow-wrap: break-word;
}
.portal-repositorio-detalle-cuerpo p { margin: 0 0 1rem; }
.portal-repositorio-detalle-cuerpo a {
    color: var(--c-primary);
    text-decoration: underline;
}
.portal-repositorio-detalle-cuerpo h1,
.portal-repositorio-detalle-cuerpo h2,
.portal-repositorio-detalle-cuerpo h3,
.portal-repositorio-detalle-cuerpo h4,
.portal-repositorio-detalle-cuerpo h5 {
    margin: 1.6rem 0 .6rem;
    line-height: 1.3;
    color: var(--c-text-strong);
}
.portal-repositorio-detalle-cuerpo ul,
.portal-repositorio-detalle-cuerpo ol { margin: 0 0 1rem 1.4rem; }
.portal-repositorio-detalle-cuerpo li { margin-bottom: .35rem; }
.portal-repositorio-detalle-cuerpo table {
    width: 100%;
    border-collapse: collapse;
    margin: 1rem 0 1.4rem;
    font-size: .92rem;
    display: block;
    overflow-x: auto;
}
.portal-repositorio-detalle-cuerpo th,
.portal-repositorio-detalle-cuerpo td {
    border: 1px solid var(--c-border, #dee2e6);
    padding: .45rem .6rem;
    text-align: left;
    vertical-align: top;
}
.portal-repositorio-detalle-cuerpo th { background: var(--c-bg-subtle, #f4f7f6); }
.portal-repositorio-detalle-cuerpo img { max-width: 100%; height: auto; }

/* CTA de descarga */
.portal-repositorio-detalle-descarga { margin-bottom: 2rem; }
.portal-repositorio-descarga-btn {
    display: flex;
    align-items: center;
    gap: .9rem;
    padding: 1rem 1.3rem;
    background: var(--c-primary);
    color: var(--c-bg-card);
    text-decoration: none;
    border-radius: 10px;
    transition: filter .15s ease, transform .15s ease, box-shadow .15s ease;
    box-shadow: 0 2px 8px rgba(0, 0, 0, .08);
}
.portal-repositorio-descarga-btn:hover {
    filter: brightness(1.05);
    transform: translateY(-1px);
    color: var(--c-bg-card);
    text-decoration: none;
    box-shadow: 0 4px 14px rgba(0, 0, 0, .12);
}
.portal-repositorio-descarga-btn > i:first-child { font-size: 1.8rem; flex-shrink: 0; }
.portal-repositorio-descarga-btn-texto {
    display: flex;
    flex-direction: column;
    gap: .1rem;
    flex: 1;
    min-width: 0;
}
.portal-repositorio-descarga-btn-texto strong { font-size: 1rem; line-height: 1.25; }
.portal-repositorio-descarga-btn-texto small { font-size: .78rem; opacity: .82; }
.portal-repositorio-descarga-btn-flecha { font-size: 1.1rem; opacity: .85; }

/* Tags al pie */
.portal-repositorio-detalle-tags {
    display: flex;
    align-items: center;
    gap: .5rem;
    flex-wrap: wrap;
    margin-bottom: 2rem;
}
.portal-repositorio-detalle-tags-label {
    font-size: .75rem;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--c-muted);
    font-weight: 600;
    margin-right: .3rem;
}
.portal-repositorio-detalle-tags-lista {
    display: flex;
    gap: .3rem;
    flex-wrap: wrap;
}
.portal-repositorio-tag {
    background: var(--c-secondary-bg);
    color: var(--c-text);
    padding: .18rem .55rem;
    border-radius: 999px;
    font-size: .74rem;
}

/* Relacionados */
.portal-repositorio-detalle-relacionados { margin-top: 2.5rem; }
.portal-repositorio-detalle-relacionados-titulo {
    font-size: 1.15rem;
    color: var(--c-text-strong);
    margin: 0 0 .8rem;
    padding-bottom: .5rem;
    border-bottom: 1px solid var(--c-border);
}
.portal-repositorio-detalle-relacionados-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: .8rem;
}
.portal-repositorio-relacionado-card {
    display: flex;
    align-items: center;
    gap: .7rem;
    padding: .7rem .85rem;
    background: var(--c-bg-card);
    border: 1px solid var(--c-border);
    border-radius: 8px;
    color: inherit;
    text-decoration: none;
    transition: border-color .15s ease, transform .15s ease;
}
.portal-repositorio-relacionado-card:hover {
    border-color: var(--c-primary);
    transform: translateY(-1px);
    text-decoration: none;
    color: inherit;
}
.portal-repositorio-relacionado-card-icono {
    width: 36px;
    height: 36px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: color-mix(in srgb, var(--c-primary) 10%, var(--c-bg-card));
    color: var(--c-primary);
    border-radius: 8px;
    font-size: 1.1rem;
    flex-shrink: 0;
}
.portal-repositorio-relacionado-card-body {
    display: flex;
    flex-direction: column;
    min-width: 0;
    flex: 1;
}
.portal-repositorio-relacionado-card-titulo {
    font-size: .88rem;
    color: var(--c-text-strong);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.portal-repositorio-relacionado-card-body time {
    font-size: .74rem;
    color: var(--c-muted);
}

@media (max-width: 640px) {
    .portal-repositorio-pagina-titulo { font-size: 1.9rem; }
    .portal-repositorio-detalle-titulo { font-size: 1.6rem; }
    .portal-repositorio-descarga-btn { padding: .8rem 1rem; }
    .portal-repositorio-descarga-btn > i:first-child { font-size: 1.4rem; }
    .portal-repositorio-pag { gap: .5rem; flex-wrap: wrap; }
}

