/* ════════════════════════════════════════════════════════════════════
   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; }

/* 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 {
    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. */
    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%;
}

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: 2.5rem; }
.portal-zona--hero { padding: 0; }

/* ── 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. */
.portal-zona {
    display: grid;
    grid-template-columns: repeat(12, 1fr);
    gap: 1.5rem;
}
.portal-zona--hero {
    /* El hero sigue siendo de "una columna ancha". Saltamos la grilla
       de 12 col porque el bloque hero ya maneja su propio ancho. */
    display: block;
}
.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 {
        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;
    }
}

/* ── Grid global de la página ────────────────────────────────────
   2 columnas: main (1fr) + sidebar (~320px). Cada zona principal
   ocupa una fila numerada (hero=1, destacados=2, contenido=3,
   servicios=4). Los bloques en zona `lateral` viven en la col 2
   con `grid-row` inline (desde el template) y pueden abarcar
   una o varias filas contiguas.

   El hero, por su naturaleza ancha, ocupa las dos columnas.
   En mobile el grid colapsa a 1 col y los laterales fluyen al pie.
*/
.portal-pagina-grid {
    display: grid;
    grid-template-columns: 1fr 320px;
    grid-template-rows: auto auto auto auto;
    gap: 2rem;
    padding-block: 2rem;
}
/* Posicionamiento explícito de cada zona principal. Damos el grid-row
   por nombre de zona para que el CSS sea ortogonal del orden de los
   {% if %} en el template — y para evitar bugs si una zona no se
   renderiza (las demás siguen en su fila correcta). */
.portal-pagina-grid__hero        {
    grid-column: 1 / -1;
    grid-row: 1;
    padding: 0;
    /* Rompe el `max-width: 1200px` del .portal-container para que el
       hero ocupe TODO el viewport. Margins negativos simétricos:
         - `50% - 50vw` saca el hero del container centrado (sale 360px
           por lado en pantalla 1920px con container 1200px).
         - `- 1.25rem` compensa el `padding: 0 1.25rem` del .portal-container
           para que el hero llegue al borde EXACTO del viewport.
       El padding-top del grid (`padding-block: 2rem`) se anula con margin-top
       negativo para pegar el hero a la navbar sin gap.
       El contenido interno del hero (`.portal-hero-contenido`) tiene su
       propio `.portal-container` para mantener el TEXTO acotado a 1200px
       aunque el fondo sea ancho. */
    margin-left: calc(50% - 50vw - 1.25rem);
    margin-right: calc(50% - 50vw - 1.25rem);
    margin-top: -2rem;
}
.portal-pagina-grid__destacados  { grid-column: 1;       grid-row: 2; }
.portal-pagina-grid__contenido   { grid-column: 1;       grid-row: 3; }
.portal-pagina-grid__servicios   { grid-column: 1;       grid-row: 4; }

/* Cuando una zona principal NO está cubierta por ningún bloque lateral,
   se le suma la clase `--full` desde el template (vía `zonas_con_lateral`
   calculado en la view) y ocupa las DOS columnas. Sin esta regla, la
   zona se quedaría en col 1 y dejaría un hueco vacío de 320px a la
   derecha — feo cuando la página no usa sidebar en algunas zonas. */
.portal-pagina-grid__zona--full {
    grid-column: 1 / -1;
}

/* Bloques del lateral: van en col 2. `grid-row` lo setea el template
   inline según `lateral_desde` / `lateral_hasta` del modelo. */
.portal-pagina-grid__lateral {
    grid-column: 2;
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
    /* Permitir que el contenido del aside se ancle al inicio de su slot
       cuando abarca varias filas (no estirarse vertical infinito). */
    align-self: start;
}

/* En mobile colapsamos a 1 columna: cada bloque lateral cae al final
   del flujo natural (en `grid-row: auto`) para que no rompa el orden
   ni intente abarcar filas del main. */
@media (max-width: 900px) {
    .portal-pagina-grid {
        grid-template-columns: 1fr;
        grid-template-rows: none;
    }
    .portal-pagina-grid__hero,
    .portal-pagina-grid__destacados,
    .portal-pagina-grid__contenido,
    .portal-pagina-grid__servicios,
    .portal-pagina-grid__lateral {
        grid-column: 1 !important;
        grid-row: auto !important;
    }
}


/* ═══════════════════════════════════════════════════════════════════
   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;
}
.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. theme-config.css tiene una regla
   `a:not(.btn):not(.nav-link):not(.dropdown-item):not(.btn-volver)` con
   especificidad (0,4,1) que pinta TODOS los enlaces que no llevan esas
   clases con `--theme-primary`. Como el CTA del hero no tiene la clase
   `.btn`, esa regla lo afectaba y le sacaba el blanco. Forzamos color
   con !important; el origen del bug está documentado para quien venga
   después. */
.portal-hero-cta {
    display: inline-block;
    background: var(--c-primary);
    color: var(--c-text-light) !important;
    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);
    color: var(--c-text-light) !important;
    text-decoration: none;
}


/* ── 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;
}
.portal-nav-dropdown-menu {
    position: absolute;
    top: 100%;
    left: 0;
    background: var(--c-bg-card);
    border: 1px solid var(--c-border-light);
    border-radius: 6px;
    box-shadow: 0 8px 24px rgba(0,0,0,.08);
    padding: .35rem 0;
    min-width: 200px;
    display: none;
    z-index: 100;
    margin-top: 6px;
}
.portal-nav-dropdown:hover .portal-nav-dropdown-menu,
.portal-nav-dropdown:focus-within .portal-nav-dropdown-menu {
    display: block;
}
.portal-nav-dropdown-item {
    display: block;
    padding: .5rem 1rem;
    color: var(--c-text);
    text-decoration: none;
    font-size: .92rem;
    font-weight: 500;
}
.portal-nav-dropdown-item:hover {
    background: rgba(0,0,0,.04);
    color: var(--c-text-brand);
    text-decoration: none;
}

.portal-nav-cta {
    background: var(--c-primary);
    color: var(--c-text-light) !important;
    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);
    color: var(--c-text-light) !important;
    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: .88rem;
    line-height: 1.6;
}
.portal-footer-line-link {
    color: rgba(255,255,255,.85);
    text-decoration: none;
    border-bottom: 1px dotted rgba(255,255,255,.35);
}
.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;
        padding: .5rem 0 0;
        margin: 0;
        display: flex;
        flex-direction: column;
        gap: .35rem;
    }
    .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;
    }

    .portal-hero { 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;
}
@media (prefers-reduced-motion: reduce) {
    .portal-ir-arriba { transition: none; }
}

