/**
 * UPAEP uikit — Panorama (mega menú)
 * ---------------------------------------------------------------------------
 * Barra de navegación con paneles de contenido rico (mega menú) y tres
 * superficies móviles sobre el MISMO nodo: collapse (acordeón en la barra),
 * offcanvas (panel lateral fijo) y bottomSheet (hoja inferior arrastrable).
 *
 * Reparto de responsabilidades: TODA la lógica visual vive aquí; el JS solo
 * añade/quita clases (`is-mobile`, `__surface-*`, `is-show`, `is-hiding`) y
 * escribe variables (`--upaep-panorama-panel-x`, `--upaep-panorama-sheet-y`).
 *
 * Cargar DESPUÉS de style.css:
 *   <link rel="stylesheet" href="../style.css">
 *   <link rel="stylesheet" href="panorama.css">
 * ---------------------------------------------------------------------------
 */

/* ── Bloque raíz: variables configurables (fallback a roles --ui-* y tokens) ── */
.upaep__panorama {
    /*? Barra */
    --upaep-panorama-bar-bg: var(--ui-surface, var(--upaep-color-white));
    --upaep-panorama-bar-padding: .5rem 1rem;
    --upaep-panorama-bar-gap: 1rem;
    --upaep-panorama-bar-min-height: 3.5rem;
    --upaep-panorama-bar-shadow: var(--ui-shadow, var(--upaep-box-shadow));
    --upaep-panorama-bar-radius: 0;
    --upaep-panorama-bar-border-size: 0;
    --upaep-panorama-bar-border-color: var(--ui-border, var(--upaep-color-border-grey));
    --upaep-panorama-zindex: var(--upaep-zindex-dropdown);

    /*? Marca */
    --upaep-panorama-brand-fz: var(--upaep-fz20);
    --upaep-panorama-brand-fw: var(--upaep-fw-semibold);
    --upaep-panorama-brand-color: var(--ui-text-strong, var(--upaep-color-black));
    --upaep-panorama-brand-gap: .5rem;
    --upaep-panorama-brand-logo-height: 2.25rem;

    /*? Items de la barra (trigger y link simple) */
    --upaep-panorama-items-gap: .25rem;
    --upaep-panorama-item-padding: .625rem .75rem;
    --upaep-panorama-item-gap: .375rem;
    --upaep-panorama-item-fz: var(--upaep-fz16);
    --upaep-panorama-item-fw: var(--upaep-fw-medium);
    --upaep-panorama-item-color: var(--ui-text-on-surface, var(--upaep-color-dark-grey));
    --upaep-panorama-item-bg: transparent;
    --upaep-panorama-item-radius: .5rem;
    --upaep-panorama-item-icon-size: 1.125em;
    --upaep-panorama-item-hover-color: var(--ui-primary, var(--upaep-color-primary));
    --upaep-panorama-item-hover-bg: var(--ui-surface-hover, #dee2e6);
    --upaep-panorama-item-open-color: var(--ui-primary, var(--upaep-color-primary));
    --upaep-panorama-item-open-bg: var(--ui-surface-hover, #dee2e6);
    --upaep-panorama-item-active-color: var(--ui-primary, var(--upaep-color-primary));
    --upaep-panorama-item-active-bg: transparent;
    --upaep-panorama-item-active-fw: var(--upaep-fw-semibold);

    /*? Indicador del item activo/abierto (subrayado) */
    --upaep-panorama-indicator-size: 3px;
    --upaep-panorama-indicator-color: var(--ui-primary, var(--upaep-color-primary));
    --upaep-panorama-indicator-radius: 3px 3px 0 0;
    --upaep-panorama-indicator-inset: .5rem;

    /*? Caret (chevron) del trigger */
    --upaep-panorama-caret-size: .8em;
    --upaep-panorama-caret-stroke: 2;
    --upaep-panorama-caret-rotate: 180deg;

    /*? Panel */
    --upaep-panorama-panel-bg: var(--ui-surface, var(--upaep-color-white));
    --upaep-panorama-panel-color: var(--ui-text-strong, var(--upaep-color-black));
    --upaep-panorama-panel-padding: 1.5rem;
    --upaep-panorama-panel-radius: 0 0 .75rem .75rem;
    --upaep-panorama-panel-shadow: 0 1.5rem 2.5rem -1rem rgba(0, 0, 0, .22);
    --upaep-panorama-panel-border-size: 1px;
    --upaep-panorama-panel-border-color: var(--ui-border-subtle, #dcdcdc);
    /*? 0 = el panel queda pegado al borde inferior de la barra (aspecto habitual
       de un mega menú, coherente con el radio `0 0 .75rem .75rem`). Con un valor
       mayor se despega, pero deja una franja de página visible entre ambos y el
       puntero la cruza al bajar: súbelo solo junto con `closeDelay`. */
    --upaep-panorama-panel-offset: 0px;
    --upaep-panorama-panel-maxwidth: min(1280px, 92vw);
    /*? Techo del CONTENIDO del panel, centrado. En `full` evita que un mega menú
       a lo ancho del viewport estire las columnas hasta el borde; en `container`
       viene sin techo (el panel ya mide lo que la barra). */
    --upaep-panorama-panel-content-maxwidth: none;
    --upaep-panorama-panel-maxheight: min(70vh, 40rem);
    /*? Ancho del viewport SIN la barra de scroll. Lo escribe el JS con
       `documentElement.clientWidth`: `100vw` la incluye y un panel `full` de
       100vw provoca scroll horizontal en cuanto la página tiene barra. */
    --upaep-panorama-vw: 100vw;
    --upaep-panorama-panel-width: max-content;
    --upaep-panorama-panel-x: 0px;
    --upaep-panorama-panel-columns: 4;
    --upaep-panorama-panel-colmin: 12rem;
    --upaep-panorama-panel-column-gap: 2rem;
    --upaep-panorama-panel-row-gap: 1.25rem;
    --upaep-panorama-panel-in: a_panoramaIn;
    --upaep-panorama-panel-out: a_panoramaOut;
    --upaep-panorama-panel-duration: .22s;
    --upaep-panorama-panel-out-duration: .18s;
    --upaep-panorama-panel-easing: cubic-bezier(.32, .72, .34, 1);
    --upaep-panorama-panel-slide: -.5rem;
    /*? Duraciones de las animaciones tipo offcanvas (`data-animation="slide-*"`):
       recorren toda la pantalla, así que necesitan más tiempo que el fundido. */
    --upaep-panorama-panel-slide-duration: .34s;
    --upaep-panorama-panel-slide-out-duration: .28s;
    --upaep-panorama-panel-zoom-from: .96;

    /*? Pie del panel */
    /*? Encabezado del panel (solo visible en `data-width="screen"`) */
    --upaep-panorama-panel-header-padding: 1rem clamp(1.5rem, 5vw, 4rem);
    --upaep-panorama-panel-header-border-size: 1px;
    --upaep-panorama-panel-header-border-color: var(--ui-divider, #dddddd);
    --upaep-panorama-panel-title-fz: var(--upaep-fz18);
    --upaep-panorama-panel-title-fw: var(--upaep-fw-semibold);
    --upaep-panorama-panel-title-color: var(--ui-text-strong, var(--upaep-color-black));

    --upaep-panorama-panel-footer-bg: var(--ui-surface-hover, #dee2e6);
    --upaep-panorama-panel-footer-padding: .875rem 1.5rem;
    --upaep-panorama-panel-footer-gap: 1rem;
    --upaep-panorama-panel-footer-fz: var(--upaep-fz14);

    /*? Bloque: grupo / columna */
    --upaep-panorama-group-gap: .125rem;
    /*? Ancho mínimo de columna cuando un grupo ocupa varias columnas del panel
       y su lista se reparte. */
    --upaep-panorama-list-colmin: 13rem;
    --upaep-panorama-group-title-fz: var(--upaep-fz12);
    --upaep-panorama-group-title-fw: var(--upaep-fw-bold);
    --upaep-panorama-group-title-color: var(--ui-text-on-surface, var(--upaep-color-dark-grey));
    --upaep-panorama-group-title-transform: uppercase;
    --upaep-panorama-group-title-spacing: .08em;
    --upaep-panorama-group-title-margin: 0 0 .5rem;
    --upaep-panorama-group-title-border-size: 0;
    --upaep-panorama-group-title-border-color: var(--ui-divider, #dddddd);

    /*? Bloque: entrada (link enriquecido) */
    --upaep-panorama-entry-padding: .5rem;
    --upaep-panorama-entry-gap: .625rem;
    --upaep-panorama-entry-radius: .5rem;
    --upaep-panorama-entry-bg: transparent;
    --upaep-panorama-entry-hover-bg: var(--ui-surface-hover, #dee2e6);
    --upaep-panorama-entry-label-fz: var(--upaep-fz15);
    --upaep-panorama-entry-label-fw: var(--upaep-fw-medium);
    --upaep-panorama-entry-label-color: var(--ui-text-strong, var(--upaep-color-black));
    --upaep-panorama-entry-hover-label-color: var(--ui-primary, var(--upaep-color-primary));
    --upaep-panorama-entry-desc-fz: var(--upaep-fz13);
    --upaep-panorama-entry-desc-color: var(--ui-text-on-surface, var(--upaep-color-dark-grey));
    --upaep-panorama-entry-desc-lines: 2;
    --upaep-panorama-entry-icon-size: 2rem;
    --upaep-panorama-entry-icon-glyph: 1.125rem;
    --upaep-panorama-entry-icon-radius: .5rem;
    --upaep-panorama-entry-icon-bg: var(--ui-surface-hover, #dee2e6);
    --upaep-panorama-entry-icon-color: var(--ui-primary, var(--upaep-color-primary));
    --upaep-panorama-entry-arrow-size: .9rem;
    --upaep-panorama-entry-arrow-color: var(--ui-primary, var(--upaep-color-primary));

    /*? Bloque: pestañas verticales */
    --upaep-panorama-tabs-width: 15rem;
    --upaep-panorama-tabs-gap: 1.5rem;
    /*? Anchos de los niveles anidados (columnas encadenadas). */
    --upaep-panorama-tabs-width-nested: 13rem;
    --upaep-panorama-tabs-width-deep: 11rem;
    --upaep-panorama-tabs-gap-nested: 1.25rem;
    --upaep-panorama-tabs-list-gap: .125rem;
    --upaep-panorama-tabs-divider-size: 1px;
    --upaep-panorama-tabs-divider-color: var(--ui-divider, #dddddd);
    --upaep-panorama-tab-padding: .5rem .625rem;
    --upaep-panorama-tab-gap: .625rem;
    --upaep-panorama-tab-radius: .5rem;
    --upaep-panorama-tab-fz: var(--upaep-fz15);
    --upaep-panorama-tab-fw: var(--upaep-fw-medium);
    --upaep-panorama-tab-color: var(--ui-text-strong, var(--upaep-color-black));
    --upaep-panorama-tab-bg: transparent;
    --upaep-panorama-tab-hover-color: var(--ui-primary, var(--upaep-color-primary));
    --upaep-panorama-tab-hover-bg: var(--ui-surface-hover, #dee2e6);
    --upaep-panorama-tab-active-color: var(--ui-primary, var(--upaep-color-primary));
    --upaep-panorama-tab-active-bg: var(--ui-surface-hover, #dee2e6);
    --upaep-panorama-tab-active-fw: var(--upaep-fw-semibold);
    --upaep-panorama-tab-marker-size: 3px;
    --upaep-panorama-tab-marker-color: var(--ui-primary, var(--upaep-color-primary));
    --upaep-panorama-tab-desc-fz: var(--upaep-fz12);
    --upaep-panorama-tab-desc-color: var(--ui-text-on-surface, var(--upaep-color-dark-grey));
    --upaep-panorama-tab-icon-size: 1.125rem;
    --upaep-panorama-tab-arrow-size: .85rem;
    --upaep-panorama-tabpane-columns: 2;
    --upaep-panorama-tabpane-colmin: 11rem;
    --upaep-panorama-tabpane-column-gap: 1.5rem;
    --upaep-panorama-tabpane-row-gap: 1rem;

    /*? Bloque: submenús (subitems) */
    --upaep-panorama-sublist-indent: .875rem;
    --upaep-panorama-sublist-gap: .125rem;
    --upaep-panorama-sublist-margin: .125rem 0 .25rem;
    --upaep-panorama-sublist-rail-size: 1px;
    --upaep-panorama-sublist-rail-color: var(--ui-divider, #dddddd);
    --upaep-panorama-sublist-maxheight: 100vh;
    /*? La escribe el JS con la altura real de la sublista (igual que el
       acordeón de la barra): animar hasta el tope hace que la transición se
       consuma en los primeros milisegundos y parezca un salto. */
    --upaep-panorama-sublist-h: var(--upaep-panorama-sublist-maxheight);
    --upaep-panorama-sublist-duration: .25s;
    --upaep-panorama-subentry-fz: var(--upaep-fz14);
    --upaep-panorama-caret-toggle-size: .8rem;

    /*? Bloque: tarjeta */
    --upaep-panorama-card-bg: var(--ui-surface-hover, #dee2e6);
    --upaep-panorama-card-radius: .75rem;
    --upaep-panorama-card-gap: .5rem;
    --upaep-panorama-card-body-padding: .875rem 1rem 1rem;
    --upaep-panorama-card-media-ratio: 16 / 9;
    --upaep-panorama-card-media-maxheight: none;
    --upaep-panorama-card-title-fz: var(--upaep-fz16);
    --upaep-panorama-card-title-fw: var(--upaep-fw-semibold);
    --upaep-panorama-card-title-color: var(--ui-text-strong, var(--upaep-color-black));
    --upaep-panorama-card-text-fz: var(--upaep-fz13);
    --upaep-panorama-card-text-color: var(--ui-text-on-surface, var(--upaep-color-dark-grey));
    --upaep-panorama-card-shadow: none;
    --upaep-panorama-card-hover-shadow: var(--ui-shadow, var(--upaep-box-shadow));

    /*? Bloque: banner / promo */
    --upaep-panorama-banner-bg: var(--ui-primary, var(--upaep-color-primary));
    --upaep-panorama-banner-color: var(--ui-on-primary, var(--upaep-color-white));
    --upaep-panorama-banner-padding: 1rem 1.25rem;
    --upaep-panorama-banner-radius: .75rem;
    --upaep-panorama-banner-gap: 1rem;
    --upaep-panorama-banner-title-fz: var(--upaep-fz16);
    --upaep-panorama-banner-title-fw: var(--upaep-fw-semibold);
    --upaep-panorama-banner-text-fz: var(--upaep-fz13);
    --upaep-panorama-banner-icon-size: 2rem;
    --upaep-panorama-banner-body-min: 11rem;

    /*? Bloque: destacado (modificador __featured) */
    --upaep-panorama-featured-bg: var(--ui-surface-hover, #dee2e6);
    --upaep-panorama-featured-padding: 1rem;
    --upaep-panorama-featured-radius: .75rem;
    --upaep-panorama-featured-accent-size: 0px;
    --upaep-panorama-featured-accent-color: var(--ui-primary, var(--upaep-color-primary));

    /*? Bloque: etiquetas / chips */
    --upaep-panorama-tags-gap: .5rem;
    --upaep-panorama-tag-padding: .3rem .7rem;
    --upaep-panorama-tag-fz: var(--upaep-fz13);
    --upaep-panorama-tag-radius: 999px;
    --upaep-panorama-tag-bg: var(--ui-surface-hover, #dee2e6);
    --upaep-panorama-tag-color: var(--ui-text-strong, var(--upaep-color-black));
    --upaep-panorama-tag-border-size: 1px;
    --upaep-panorama-tag-border-color: transparent;
    --upaep-panorama-tag-hover-bg: var(--ui-primary, var(--upaep-color-primary));
    --upaep-panorama-tag-hover-color: var(--ui-on-primary, var(--upaep-color-white));

    /*? Bloque: encabezado / texto / separador */
    --upaep-panorama-heading-fz: var(--upaep-fz20);
    --upaep-panorama-heading-fw: var(--upaep-fw-semibold);
    --upaep-panorama-heading-color: var(--ui-text-strong, var(--upaep-color-black));
    --upaep-panorama-heading-margin: 0 0 .25rem;
    --upaep-panorama-text-fz: var(--upaep-fz14);
    --upaep-panorama-text-color: var(--ui-text-on-surface, var(--upaep-color-dark-grey));
    --upaep-panorama-divider-size: 1px;
    --upaep-panorama-divider-color: var(--ui-divider, #dddddd);
    --upaep-panorama-divider-margin: .25rem 0;

    /*? Insignia (badge) de una entrada o de un item */
    --upaep-panorama-badge-fz: var(--upaep-fz11, .6875rem);
    --upaep-panorama-badge-fw: var(--upaep-fw-bold);
    --upaep-panorama-badge-padding: .1rem .45rem;
    --upaep-panorama-badge-radius: 999px;
    --upaep-panorama-badge-bg: var(--ui-accent, var(--upaep-color-red));
    --upaep-panorama-badge-color: var(--ui-on-accent, var(--upaep-color-white));

    /*? Botón de menú (hamburguesa) y botón de cierre */
    --upaep-panorama-toggle-size: 2.5rem;
    --upaep-panorama-toggle-glyph: 1.25rem;
    --upaep-panorama-toggle-color: var(--ui-text-strong, var(--upaep-color-black));
    --upaep-panorama-toggle-bg: transparent;
    --upaep-panorama-toggle-hover-bg: var(--ui-surface-hover, #dee2e6);
    --upaep-panorama-toggle-radius: .5rem;
    --upaep-panorama-toggle-gap: .5rem;
    --upaep-panorama-toggle-label-fz: var(--upaep-fz14);

    /*? Superficies móviles (collapse / offcanvas / bottomSheet) */
    --upaep-panorama-overlay-bg: var(--ui-surface, var(--upaep-color-white));
    --upaep-panorama-overlay-width: 22rem;
    --upaep-panorama-overlay-maxwidth: 92vw;
    --upaep-panorama-overlay-shadow: 0 0 2.5rem rgba(0, 0, 0, .28);
    --upaep-panorama-overlay-radius: 0;
    --upaep-panorama-overlay-padding: .75rem;
    --upaep-panorama-overlay-gap: .25rem;
    --upaep-panorama-overlay-zindex: var(--upaep-zindex-offcanvas);
    --upaep-panorama-overlay-duration: .3s;
    --upaep-panorama-overlay-easing: cubic-bezier(.32, .72, .34, 1);
    --upaep-panorama-sheet-height: 85dvh;
    --upaep-panorama-sheet-radius: 1rem 1rem 0 0;
    --upaep-panorama-sheet-y: 0px;
    --upaep-panorama-handle-width: 2.5rem;
    --upaep-panorama-handle-height: .25rem;
    --upaep-panorama-handle-color: var(--ui-border, var(--upaep-color-border-grey));
    --upaep-panorama-handle-margin: .5rem auto;
    /*? Holgura invisible alrededor del asa, para que sea agarrable con el dedo. */
    --upaep-panorama-handle-hit: .75rem;
    /*? Superficie fullscreen (el menú ocupa la pantalla en móvil) */
    --upaep-panorama-fullscreen-padding: 1rem;
    --upaep-panorama-fullscreen-maxwidth: 40rem;
    --upaep-panorama-fullscreen-slide: 1rem;

    --upaep-panorama-collapse-maxheight: 75vh;
    /*? La escribe el JS (`measureCollapse`) con la altura real del contenido;
       aquí solo queda el valor de reserva por si el JS no llegara a medir. */
    --upaep-panorama-collapse-h: var(--upaep-panorama-collapse-maxheight);
    --upaep-panorama-collapse-header-padding: .5rem .25rem .75rem;
    --upaep-panorama-collapse-title-fz: var(--upaep-fz18);
    --upaep-panorama-collapse-title-fw: var(--upaep-fw-semibold);
    --upaep-panorama-panel-mobile-maxheight: 200vh;
    --upaep-panorama-panel-mobile-padding: .25rem .5rem 1rem;
    --upaep-panorama-panel-mobile-duration: .28s;

    /*? Ancho `screen` (panel a pantalla completa) */
    --upaep-panorama-screen-padding: clamp(1.5rem, 5vw, 4rem);
    --upaep-panorama-screen-columns: 3;
    --upaep-panorama-screen-close-size: 2.25rem;
    --upaep-panorama-screen-close-color: var(--ui-text-strong, var(--upaep-color-black));

    /*? Variante `__inline` (el panel vive en un hueco reservado bajo la barra) */
    --upaep-panorama-inline-height: 22rem;

    /*? Foco */
    --upaep-panorama-focus-ring: 2px solid var(--ui-primary, var(--upaep-color-primary));
    --upaep-panorama-focus-offset: 2px;

    position: relative;
    display: block;
    font-family: var(--ui-font-base, var(--upaep-ff-fira-sans));
    color: var(--upaep-panorama-item-color);
}

/*? Backdrop compartido del uikit: aquí solo se fija su capa. */
.upaep__backdrop.__panorama {
    z-index: var(--upaep-zindex-offcanvas-backdrop);
}

/*? Foco visible uniforme para todo lo interactivo del componente. */
.upaep__panorama :is(a, button, [tabindex]):focus-visible {
    outline: var(--ui-focus-ring-width, 2px) solid var(--ui-focus-ring-color, currentColor);
    outline-offset: var(--ui-focus-ring-offset, 2px);
}

/* ── Tamaño de los SVG inline ────────────────────────────────────────────────
   Los iconos que emite el componente llevan solo `viewBox`, sin width/height:
   sin esta regla base un SVG suelto se estira hasta ocupar todo el espacio
   disponible (un icono de item se comía la barra entera). Va ANTES que las
   reglas de cada parte a propósito: comparten especificidad (0,1,1) y gana la
   última, así cada parte con caja propia sigue mandando sobre su icono.
   `flex: 0 0 auto` evita además que un SVG dentro de un botón se deforme. */
.upaep__panorama svg {
    width: 1em;
    height: 1em;
    flex: 0 0 auto;
}

/* ── Barra ─────────────────────────────────────────────────────────────────── */
.upaep__panorama-bar {
    min-height: var(--upaep-panorama-bar-min-height);
    padding: var(--upaep-panorama-bar-padding);
    gap: var(--upaep-panorama-bar-gap);
    background: var(--upaep-panorama-bar-bg);
    box-shadow: var(--upaep-panorama-bar-shadow);
    border-radius: var(--upaep-panorama-bar-radius);
    border-bottom: var(--upaep-panorama-bar-border-size) solid var(--upaep-panorama-bar-border-color);
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: nowrap;
}

.upaep__panorama-brand {
    gap: var(--upaep-panorama-brand-gap);
    font-size: var(--upaep-panorama-brand-fz);
    font-weight: var(--upaep-panorama-brand-fw);
    color: var(--upaep-panorama-brand-color);
    display: inline-flex;
    align-items: center;
    flex: 0 0 auto;
    text-decoration: none;

    & img,
    & svg {
        height: var(--upaep-panorama-brand-logo-height);
        width: auto;
        flex: 0 0 auto;
    }
}

/*? La zona colapsable desaparece como caja en escritorio: sus hijos (lista de
   items y acciones) pasan a ser hijos directos del flex de la barra. En móvil
   vuelve a ser una caja real (columna) y se convierte en la superficie. */
.upaep__panorama-collapse {
    display: contents;
}

/*? Partes que solo existen en la superficie móvil. */
.upaep__panorama-handle,
.upaep__panorama-collapse-header {
    display: none;
}

.upaep__panorama-items {
    gap: var(--upaep-panorama-items-gap);
    margin: 0;
    padding: 0;
    list-style: none;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    flex: 1 1 auto;
}

.upaep__panorama-item {
    /*? static a propósito: el bloque de contención del panel es .upaep__panorama,
       no el <li>, para que el panel pueda medir el ancho de la barra o del viewport. */
    position: static;
    display: flex;
    align-items: center;
}

.upaep__panorama-actions {
    gap: var(--upaep-panorama-toggle-gap);
    display: flex;
    align-items: center;
    flex: 0 0 auto;
}

/* ── Item de la barra: trigger (con panel) y link simple ───────────────────── */
.upaep__panorama-trigger,
.upaep__panorama-link {
    padding: var(--upaep-panorama-item-padding);
    gap: var(--upaep-panorama-item-gap);
    font-family: inherit;
    font-size: var(--upaep-panorama-item-fz);
    font-weight: var(--upaep-panorama-item-fw);
    color: var(--upaep-panorama-item-color);
    background: var(--upaep-panorama-item-bg);
    border-radius: var(--upaep-panorama-item-radius);
    position: relative;
    display: inline-flex;
    align-items: center;
    border: 0;
    cursor: pointer;
    text-decoration: none;
    white-space: nowrap;
    transition: color .15s ease, background-color .15s ease;

    /*? Indicador: se dibuja siempre y se revela con scaleX para poder animarlo. */
    &::after {
        content: "";
        height: var(--upaep-panorama-indicator-size);
        background: var(--upaep-panorama-indicator-color);
        border-radius: var(--upaep-panorama-indicator-radius);
        position: absolute;
        inset: auto var(--upaep-panorama-indicator-inset) 0;
        transform: scaleX(0);
        transition: transform .18s var(--upaep-panorama-panel-easing);
    }

    &:hover {
        color: var(--upaep-panorama-item-hover-color);
        background: var(--upaep-panorama-item-hover-bg);
    }

    &.is-active {
        font-weight: var(--upaep-panorama-item-active-fw);
        color: var(--upaep-panorama-item-active-color);
        background: var(--upaep-panorama-item-active-bg);

        &::after {
            transform: scaleX(1);
        }
    }

    &[aria-expanded="true"] {
        color: var(--upaep-panorama-item-open-color);
        background: var(--upaep-panorama-item-open-bg);

        &::after {
            transform: scaleX(1);
        }

        .upaep__panorama-caret {
            transform: rotate(var(--upaep-panorama-caret-rotate));
        }
    }

    &.is-disabled,
    &[aria-disabled="true"] {
        opacity: .5;
        pointer-events: none;
    }
}

.upaep__panorama-caret {
    width: var(--upaep-panorama-caret-size);
    height: var(--upaep-panorama-caret-size);
    flex: 0 0 auto;
    display: inline-flex;
    transition: transform .18s var(--upaep-panorama-panel-easing);

    /*? stroke-width es propiedad CSS en SVG: así el grosor sigue siendo
       configurable por variable aunque el path venga inline desde el JS. */
    & svg {
        width: 100%;
        height: 100%;
        stroke-width: var(--upaep-panorama-caret-stroke);
    }
}

/*? Icono propio de un item de la barra (SVG hijo directo del trigger/link). */
.upaep__panorama-trigger > svg,
.upaep__panorama-link > svg {
    width: var(--upaep-panorama-item-icon-size);
    height: var(--upaep-panorama-item-icon-size);
}

.upaep__panorama-badge {
    padding: var(--upaep-panorama-badge-padding);
    font-size: var(--upaep-panorama-badge-fz);
    font-weight: var(--upaep-panorama-badge-fw);
    color: var(--upaep-panorama-badge-color);
    background: var(--upaep-panorama-badge-bg);
    border-radius: var(--upaep-panorama-badge-radius);
    line-height: 1.6;
    flex: 0 0 auto;
}

/* ── Botón hamburguesa / cierre ────────────────────────────────────────────── */
.upaep__panorama-toggle,
.upaep__panorama-close {
    min-width: var(--upaep-panorama-toggle-size);
    min-height: var(--upaep-panorama-toggle-size);
    gap: var(--upaep-panorama-toggle-gap);
    color: var(--upaep-panorama-toggle-color);
    background: var(--upaep-panorama-toggle-bg);
    border-radius: var(--upaep-panorama-toggle-radius);
    font-family: inherit;
    font-size: var(--upaep-panorama-toggle-label-fz);
    display: none;
    align-items: center;
    justify-content: center;
    border: 0;
    cursor: pointer;
    padding: 0 .5rem;
    flex: 0 0 auto;

    &:hover {
        background: var(--upaep-panorama-toggle-hover-bg);
    }

    & svg {
        width: var(--upaep-panorama-toggle-glyph);
        height: var(--upaep-panorama-toggle-glyph);
    }
}

/*? El cierre solo tiene sentido dentro de la superficie móvil. */
.upaep__panorama.is-mobile .upaep__panorama-toggle {
    display: inline-flex;
}

/*? Hamburguesa ↔ aspa: el estado lo dicta `aria-expanded`, que ya mantiene el
   JS, así que el cambio de icono es puro CSS y no puede desincronizarse. */
.upaep__panorama-toggle-icon {
    width: var(--upaep-panorama-toggle-glyph);
    height: var(--upaep-panorama-toggle-glyph);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;

    & svg {
        width: 100%;
        height: 100%;
    }

    &.__close {
        display: none;
    }
}

.upaep__panorama-toggle[aria-expanded="true"] {
    .upaep__panorama-toggle-icon.__open {
        display: none;
    }

    .upaep__panorama-toggle-icon.__close {
        display: inline-flex;
    }
}

/*? Bloqueo del scroll del documento mientras hay un overlay abierto. Mismo
   patrón que el bottomSheet del kit (clase en el <body>). `scrollbar-gutter`
   mantiene reservado el hueco de la barra para que la página no dé un salto
   lateral al ocultarla. Solo propiedades estándar de scrollbar. */
body.upaep__panorama-lock {
    overflow: hidden;
    scrollbar-gutter: stable;
}

/* ── Panel (escritorio) ────────────────────────────────────────────────────── */
.upaep__panorama-panel {
    width: var(--upaep-panorama-panel-width);
    max-width: var(--upaep-panorama-panel-maxwidth);
    color: var(--upaep-panorama-panel-color);
    background: var(--upaep-panorama-panel-bg);
    border-radius: var(--upaep-panorama-panel-radius);
    box-shadow: var(--upaep-panorama-panel-shadow);
    border: var(--upaep-panorama-panel-border-size) solid var(--upaep-panorama-panel-border-color);
    z-index: var(--upaep-panorama-zindex);
    position: absolute;
    inset: calc(100% + var(--upaep-panorama-panel-offset)) auto auto var(--upaep-panorama-panel-x);
    overflow: hidden;
    /*? Red de seguridad contra el scroll horizontal: se hereda a todo el
       contenido, así una palabra larga (un chip, una etiqueta) se parte en vez
       de empujar la caja y desbordar el panel. */
    overflow-wrap: break-word;
    display: flex;
    flex-direction: column;
    /*? Oculto pero animable: la salida NO se corta con display/visibility
       inmediata; is-hiding la deja visible hasta que acaba la animación. */
    visibility: hidden;
    opacity: 0;

    &.is-show {
        visibility: visible;
        opacity: 1;
        animation: var(--upaep-panorama-panel-in) var(--upaep-panorama-panel-duration) var(--upaep-panorama-panel-easing) both;
    }

    &.is-hiding {
        visibility: visible;
        animation: var(--upaep-panorama-panel-out) var(--upaep-panorama-panel-out-duration) var(--upaep-panorama-panel-easing) both;
    }
}

/*? Anchos del panel. `container` (default) = ancho de la barra;
   `full` = ancho del viewport; `auto` = por contenido, anclado al item. */
.upaep__panorama-panel[data-width="container"],
.upaep__panorama-panel:not([data-width]) {
    --upaep-panorama-panel-width: auto;
    --upaep-panorama-panel-x: 0px;
    right: 0;
    max-width: none;
}

/*? Si el consumidor acota el panel con `max-width`, queda centrado en la barra
   en lugar de pegado al borde izquierdo. SOLO mientras el panel FLOTA: en
   cuanto pasa a estático (móvil, `__static`) es un flex item, y un margen
   automático en el eje transversal ANULA el stretch — el panel colapsaba a su
   contenido mínimo (16px: solo su relleno) con 250px de margen a cada lado, y
   el mega menú móvil quedaba ilegible en el ancho por defecto. */
.upaep__panorama:not(.is-mobile):not(.__static) .upaep__panorama-panel[data-width="container"],
.upaep__panorama:not(.is-mobile):not(.__static) .upaep__panorama-panel:not([data-width]) {
    margin-inline: auto;
}

/*? `full` = a lo ancho del viewport. El ancho sale de --upaep-panorama-vw (sin
   la barra de scroll) para no provocar scroll horizontal, y el CONTENIDO se
   acota y centra con --upaep-panorama-panel-content-maxwidth: el fondo va de
   borde a borde pero el texto no se estira hasta las esquinas. */
.upaep__panorama-panel[data-width="full"] {
    --upaep-panorama-panel-width: var(--upaep-panorama-vw);
    --upaep-panorama-panel-x: calc(50% - var(--upaep-panorama-vw) / 2);
    --upaep-panorama-panel-content-maxwidth: var(--upaep-panorama-panel-maxwidth);
    max-width: none;
    border-radius: 0;
    border-inline: 0;
}

/*? `auto` es un desplegable corto, no un mega panel: con el relleno grande del
   panel ancho quedaba una caja casi vacía. Aquí se aprieta (siguen siendo
   variables: una instancia puede volver a soltarlo). */
/* ── Ancho `screen`: el panel a pantalla completa ─────────────────────────────
   El patrón del mega menú del portal: el panel tapa toda la ventana, con su
   propio botón de cierre. No lleva backdrop porque no queda fondo que oscurecer;
   el JS sí aplica trampa de foco y bloqueo de scroll, porque es un diálogo. */
.upaep__panorama-panel[data-width="screen"] {
    --upaep-panorama-panel-maxheight: 100%;
    /*? A pantalla completa el contenido usa TODO el ancho (solo el relleno lo
       separa del borde). Acotarlo y centrarlo dejaba dos franjas vacías enormes
       a los lados en una pantalla ancha, y el panel no se leía como «pantalla
       completa» sino como una caja centrada. Quien quiera acotarlo tiene la
       variable a mano. */
    --upaep-panorama-panel-content-maxwidth: none;
    --upaep-panorama-panel-padding: var(--upaep-panorama-screen-padding);
    --upaep-panorama-panel-columns: var(--upaep-panorama-screen-columns);
    width: auto;
    max-width: none;
    height: 100dvh;
    position: fixed;
    inset: 0;
    margin: 0;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    z-index: var(--upaep-panorama-overlay-zindex);
    /*? Entrada más suave que la del panel flotante: aquí no hay "deslizar desde
       la barra", el panel aparece en el sitio. */
    --upaep-panorama-panel-slide: 0rem;

}

/*? El encabezado propio del panel solo existe cuando el panel ES pantalla
   completa, es decir SOLO en escritorio. En móvil un panel `screen` es un
   acordeón dentro del menú, que ya trae su propia aspa en el encabezado del
   overlay: sin este `:not` aparecían DOS botones de cierre a la vez en todas
   las superficies móviles. */
.upaep__panorama:not(.is-mobile) .upaep__panorama-panel[data-width="screen"] > .upaep__panorama-panel-header {
    display: flex;
}

/*? Encabezado del panel a pantalla completa: título a la izquierda y cierre a
   la derecha, alineados con la misma caja que el contenido (mismo `max-width`
   centrado y mismo relleno lateral). El cierre va DENTRO del encabezado y no
   flotando en una esquina: un aspa suelta sobre el vacío no se lee como parte
   del panel, y el resto de superficies del componente ya traen encabezado.
   `width: 100%` junto al margen automático es obligatorio (un margen auto en el
   eje transversal anula el stretch del flex y colapsaría la caja). */
.upaep__panorama-panel-header {
    width: 100%;
    max-width: var(--upaep-panorama-panel-content-maxwidth);
    margin-inline: auto;
    padding: var(--upaep-panorama-panel-header-padding);
    gap: 1rem;
    border-bottom: var(--upaep-panorama-panel-header-border-size) solid var(--upaep-panorama-panel-header-border-color);
    display: none;
    align-items: center;
    justify-content: space-between;
    flex: 0 0 auto;
}

.upaep__panorama-panel-title {
    font-size: var(--upaep-panorama-panel-title-fz);
    font-weight: var(--upaep-panorama-panel-title-fw);
    color: var(--upaep-panorama-panel-title-color);
    line-height: 1.3;
    min-width: 0;
}

.upaep__panorama-panel-close {
    width: var(--upaep-panorama-screen-close-size);
    height: var(--upaep-panorama-screen-close-size);
    color: var(--upaep-panorama-screen-close-color);
    background: transparent;
    border: 0;
    border-radius: .5rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    cursor: pointer;

    &:hover {
        background: var(--ui-surface-hover, #dee2e6);
    }

    & svg {
        width: 100%;
        height: 100%;
    }
}

.upaep__panorama-panel[data-width="auto"] {
    --upaep-panorama-panel-width: max-content;
    --upaep-panorama-panel-padding: .625rem;
    --upaep-panorama-panel-row-gap: .5rem;
    --upaep-panorama-panel-column-gap: 1rem;
    --upaep-panorama-panel-radius: .5rem;
    /*? Antes el tope era 22rem, pensado para una lista de enlaces. Con contenido
       rico (varias columnas o pestañas) ese tope estrujaba el texto hasta
       partirlo letra a letra y sacaba una barra de scroll dentro del panel.
       `max-content` sigue haciendo que un desplegable corto mida poco; el tope
       solo evita que uno enorme se coma la pantalla. */
    --upaep-panorama-panel-maxwidth: min(52rem, 92vw);
}

/*? Un panel de pestañas necesita ancho por definición (lista + contenido). En
   `auto` se le da un suelo generoso: si no, `max-content` puede resolverse a un
   ancho en el que la columna de la lista no deja sitio al contenido. */
.upaep__panorama-panel[data-width="auto"]:has(.upaep__panorama-block.__tabs) {
    --upaep-panorama-panel-maxwidth: min(64rem, 92vw);
    --upaep-panorama-panel-padding: 1.25rem;
    min-width: min(46rem, 92vw);
}

.upaep__panorama-panel-inner {
    padding: var(--upaep-panorama-panel-padding);
    column-gap: var(--upaep-panorama-panel-column-gap);
    row-gap: var(--upaep-panorama-panel-row-gap);
    max-height: var(--upaep-panorama-panel-maxheight);
    /*? `width: 100%` es obligatorio junto al `margin-inline: auto`: el panel es
       un flex column y un margen automático en el eje transversal ANULA el
       stretch, así que sin él el contenido colapsaba a `fit-content` (unos
       100px) sin importar lo ancho que fuera el panel. */
    width: 100%;
    max-width: var(--upaep-panorama-panel-content-maxwidth);
    margin-inline: auto;
    display: grid;
    /*? `data-columns` es un MÁXIMO, no un número fijo. Patrón RAM acotado: cada
       columna mide al menos `max(colmin, ancho/N)`, así que mientras quepan N
       columnas de `colmin` salen N, y en cuanto el panel se estrecha auto-fit
       mete menos. Con `repeat(N, 1fr)` las columnas se comprimían hasta solapar
       el texto en cuanto la ventana se hacía pequeña.
       El `min(100%, colmin)` NO es decorativo: sin él, en una caja más estrecha
       que `colmin` la pista conserva su mínimo, la rejilla desborda y aparece
       SCROLL HORIZONTAL en toda la página. */
    grid-template-columns: repeat(auto-fit, minmax(max(
        min(100%, var(--upaep-panorama-panel-colmin)),
        (100% - (var(--upaep-panorama-panel-columns) - 1) * var(--upaep-panorama-panel-column-gap)) / var(--upaep-panorama-panel-columns)
    ), 1fr));
    align-content: start;
    /*? `hidden auto` explícito, no solo `overflow-y: auto`: cuando un eje es
       auto/scroll el otro NO puede quedarse en `visible` —el navegador lo
       computa como `auto`— y aparecía una barra de scroll horizontal dentro del
       panel. Aquí el eje horizontal nunca debe scrollear. */
    overflow: hidden auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
    scrollbar-color: var(--ui-border, var(--upaep-color-border-grey)) var(--ui-surface-hover, #dee2e6);
    scrollbar-gutter: stable;
}

/*? Columnas declarativas: puro CSS, así el markup funciona sin JS. */
/*? El PANEL es el contenedor de consulta de sus bloques, así que un bloque puede
   adaptarse al ancho REAL del panel y no al del viewport.
   ⚠ Va en el panel y NO en `-panel-inner`, que es donde estaba: en Safari
   (WebKit) la contención de `container-type: inline-size` sobre una rejilla
   `repeat(auto-fit, …)` hace que todas las pistas se den por vacías y se
   colapsen a 0px — medido en WebKit 18.2: `0px 0px 0px 0px`, y el contenido del
   panel quedaba de 0px de ancho, con el texto partido letra a letra. Chrome no
   lo hace. El contenedor tiene que ser un ancestro que NO sea esa rejilla.
   Se excluye `data-width="auto"` a propósito: `container-type: inline-size`
   impide que un elemento se dimensione por su contenido en el eje inline, y
   `auto` es precisamente `width: max-content`. Ahí manda el respaldo por
   media query más el suelo de ancho de los paneles con pestañas. */
.upaep__panorama-panel:not([data-width="auto"]) {
    container-type: inline-size;
    container-name: panoramaPanel;
}

.upaep__panorama-panel[data-columns="1"] { --upaep-panorama-panel-columns: 1; }
.upaep__panorama-panel[data-columns="2"] { --upaep-panorama-panel-columns: 2; }
.upaep__panorama-panel[data-columns="3"] { --upaep-panorama-panel-columns: 3; }
.upaep__panorama-panel[data-columns="4"] { --upaep-panorama-panel-columns: 4; }
.upaep__panorama-panel[data-columns="5"] { --upaep-panorama-panel-columns: 5; }
.upaep__panorama-panel[data-columns="6"] { --upaep-panorama-panel-columns: 6; }

/*? `auto`: rejilla fluida por ancho mínimo de columna. */
.upaep__panorama-panel[data-columns="auto"] .upaep__panorama-panel-inner {
    grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--upaep-panorama-panel-colmin)), 1fr));
}

.upaep__panorama-panel-footer {
    padding: var(--upaep-panorama-panel-footer-padding);
    gap: var(--upaep-panorama-panel-footer-gap);
    background: var(--upaep-panorama-panel-footer-bg);
    font-size: var(--upaep-panorama-panel-footer-fz);
    border-top: var(--upaep-panorama-panel-border-size) solid var(--upaep-panorama-panel-border-color);
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    flex: 0 0 auto;
}

/* ── Animaciones del panel (nombres prefijados: el scope de keyframes es global) ── */
@keyframes a_panoramaIn {
    from {
        opacity: 0;
        transform: translateY(var(--upaep-panorama-panel-slide));
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}

@keyframes a_panoramaOut {
    from {
        opacity: 1;
        transform: translateY(0);
    }

    to {
        opacity: 0;
        transform: translateY(var(--upaep-panorama-panel-slide));
    }
}

/* ── Animaciones alternativas del panel (`data-animation`) ───────────────────
   Cada valor solo RE-APUNTA las variables de animación y su duración; el motor
   es el mismo (`is-show` entra, `is-hiding` sale) y el temporizador del cierre
   lee `--upaep-panorama-panel-out-duration`, así que se ajusta solo.
   Los slides no animan la opacidad: entran desde fuera como un offcanvas.
   Útiles sobre todo con `data-width="screen"`, pero valen para cualquier panel. */
.upaep__panorama-panel[data-animation="fade"] {
    --upaep-panorama-panel-in: a_panoramaIn;
    --upaep-panorama-panel-out: a_panoramaOut;
}

.upaep__panorama-panel[data-animation="slide-end"] {
    --upaep-panorama-panel-in: a_panoramaSlideEndIn;
    --upaep-panorama-panel-out: a_panoramaSlideEndOut;
    --upaep-panorama-panel-duration: var(--upaep-panorama-panel-slide-duration);
    --upaep-panorama-panel-out-duration: var(--upaep-panorama-panel-slide-out-duration);
}

.upaep__panorama-panel[data-animation="slide-start"] {
    --upaep-panorama-panel-in: a_panoramaSlideStartIn;
    --upaep-panorama-panel-out: a_panoramaSlideStartOut;
    --upaep-panorama-panel-duration: var(--upaep-panorama-panel-slide-duration);
    --upaep-panorama-panel-out-duration: var(--upaep-panorama-panel-slide-out-duration);
}

.upaep__panorama-panel[data-animation="slide-up"] {
    --upaep-panorama-panel-in: a_panoramaSlideUpIn;
    --upaep-panorama-panel-out: a_panoramaSlideUpOut;
    --upaep-panorama-panel-duration: var(--upaep-panorama-panel-slide-duration);
    --upaep-panorama-panel-out-duration: var(--upaep-panorama-panel-slide-out-duration);
}

.upaep__panorama-panel[data-animation="slide-down"] {
    --upaep-panorama-panel-in: a_panoramaSlideDownIn;
    --upaep-panorama-panel-out: a_panoramaSlideDownOut;
    --upaep-panorama-panel-duration: var(--upaep-panorama-panel-slide-duration);
    --upaep-panorama-panel-out-duration: var(--upaep-panorama-panel-slide-out-duration);
}

.upaep__panorama-panel[data-animation="zoom"] {
    --upaep-panorama-panel-in: a_panoramaZoomIn;
    --upaep-panorama-panel-out: a_panoramaZoomOut;
}

@keyframes a_panoramaSlideEndIn {
    from { opacity: 1; transform: translateX(100%); }
    to { opacity: 1; transform: translateX(0); }
}

@keyframes a_panoramaSlideEndOut {
    from { opacity: 1; transform: translateX(0); }
    to { opacity: 1; transform: translateX(100%); }
}

@keyframes a_panoramaSlideStartIn {
    from { opacity: 1; transform: translateX(-100%); }
    to { opacity: 1; transform: translateX(0); }
}

@keyframes a_panoramaSlideStartOut {
    from { opacity: 1; transform: translateX(0); }
    to { opacity: 1; transform: translateX(-100%); }
}

@keyframes a_panoramaSlideUpIn {
    from { opacity: 1; transform: translateY(100%); }
    to { opacity: 1; transform: translateY(0); }
}

@keyframes a_panoramaSlideUpOut {
    from { opacity: 1; transform: translateY(0); }
    to { opacity: 1; transform: translateY(100%); }
}

@keyframes a_panoramaSlideDownIn {
    from { opacity: 1; transform: translateY(-100%); }
    to { opacity: 1; transform: translateY(0); }
}

@keyframes a_panoramaSlideDownOut {
    from { opacity: 1; transform: translateY(0); }
    to { opacity: 1; transform: translateY(-100%); }
}

@keyframes a_panoramaZoomIn {
    from { opacity: 0; transform: scale(var(--upaep-panorama-panel-zoom-from)); }
    to { opacity: 1; transform: scale(1); }
}

@keyframes a_panoramaZoomOut {
    from { opacity: 1; transform: scale(1); }
    to { opacity: 0; transform: scale(var(--upaep-panorama-panel-zoom-from)); }
}

/* ═══════════════════════════════════════════════════════════════════════════
   BLOQUES DE CONTENIDO DEL PANEL
   Un solo bloque `.upaep__panorama-block` + modificador de tipo (`.__group`,
   `.__card`, …). Así un tipo nuevo hereda el span, el gap y la rejilla gratis.
   ═══════════════════════════════════════════════════════════════════════════ */
.upaep__panorama-block {
    min-width: 0;
    grid-column: span 1;
}

/*? Span declarativo (sin JS). `full` ocupa la fila completa. */
.upaep__panorama-block[data-span="1"] { grid-column: span 1; }
.upaep__panorama-block[data-span="2"] { grid-column: span 2; }
.upaep__panorama-block[data-span="3"] { grid-column: span 3; }
.upaep__panorama-block[data-span="4"] { grid-column: span 4; }
.upaep__panorama-block[data-span="5"] { grid-column: span 5; }
.upaep__panorama-block[data-span="6"] { grid-column: span 6; }
.upaep__panorama-block[data-span="full"] { grid-column: 1 / -1; }

/*? Modificador combinable: cualquier bloque puede ir destacado.
   `align-self: start` a propósito: una caja con fondo estirada a la altura de
   la columna más alta deja un bloque de color medio vacío. */
.upaep__panorama-block.__featured {
    padding: var(--upaep-panorama-featured-padding);
    background: var(--upaep-panorama-featured-bg);
    border-radius: var(--upaep-panorama-featured-radius);
    border-inline-start: var(--upaep-panorama-featured-accent-size) solid var(--upaep-panorama-featured-accent-color);
    align-self: start;
}

/* ── Tipo: grupo / columna de links ────────────────────────────────────────── */
.upaep__panorama-block.__group,
.upaep__panorama-block.__links {
    gap: var(--upaep-panorama-group-gap);
    display: flex;
    flex-direction: column;
}

.upaep__panorama-group-title {
    margin: var(--upaep-panorama-group-title-margin);
    padding-bottom: calc(var(--upaep-panorama-group-title-border-size) + .25rem);
    font-size: var(--upaep-panorama-group-title-fz);
    font-weight: var(--upaep-panorama-group-title-fw);
    color: var(--upaep-panorama-group-title-color);
    text-transform: var(--upaep-panorama-group-title-transform);
    letter-spacing: var(--upaep-panorama-group-title-spacing);
    border-bottom: var(--upaep-panorama-group-title-border-size) solid var(--upaep-panorama-group-title-border-color);
    line-height: 1.4;
}

/*? Las entradas miden por su CONTENIDO, no por la columna: en un panel ancho
   (sobre todo a pantalla completa, donde una columna pasa de 500px) el fondo de
   hover de un enlace corto se convertía en una barra que cruzaba media pantalla.
   Con `align-items: flex-start` cada `<li>` se encoge a su contenido y el
   resaltado envuelve al texto.
   Para recuperar el comportamiento anterior en un bloque concreto está el
   modificador `.__fill-entries`. */
.upaep__panorama-list {
    gap: var(--upaep-panorama-group-gap);
    margin: 0;
    padding: 0;
    list-style: none;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
}

.upaep__panorama-block.__fill-entries > .upaep__panorama-list {
    align-items: stretch;
    justify-items: stretch;
}

/*? Un grupo que ocupa VARIAS columnas reparte sus enlaces en columnas, en vez
   de dejar una sola lista a todo lo ancho: si no, cada entrada se estira al
   ancho del bloque y su fondo de hover abarca la fila entera —el enlace parece
   una barra—. `auto-fill` (no `auto-fit`) es lo que hace falta: las pistas
   vacías se conservan, así que con pocos enlaces cada uno mantiene su ancho de
   columna y el sobrante queda a la derecha. Solo la lista PROPIA del bloque
   (`>`), para no tocar las de un bloque de pestañas anidado. */
.upaep__panorama-block[data-span]:not([data-span="1"]) > .upaep__panorama-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--upaep-panorama-list-colmin)), 1fr));
    column-gap: var(--upaep-panorama-panel-column-gap);
    align-content: start;
    /*? Igual que en la lista de una columna: la entrada no llena su pista. */
    justify-items: start;
}

/* ── Entrada (link enriquecido: icono + etiqueta + descripción + badge) ────── */
.upaep__panorama-entry {
    /*? Nunca desborda su columna aunque mida por contenido. */
    max-width: 100%;
    padding: var(--upaep-panorama-entry-padding);
    gap: var(--upaep-panorama-entry-gap);
    background: var(--upaep-panorama-entry-bg);
    border-radius: var(--upaep-panorama-entry-radius);
    display: flex;
    align-items: flex-start;
    text-decoration: none;
    transition: background-color .15s ease;

    &:hover {
        background: var(--upaep-panorama-entry-hover-bg);

        .upaep__panorama-entry-label {
            color: var(--upaep-panorama-entry-hover-label-color);
        }

        .upaep__panorama-entry-arrow {
            opacity: 1;
            transform: translateX(0);
        }
    }
}

.upaep__panorama-entry-icon {
    width: var(--upaep-panorama-entry-icon-size);
    height: var(--upaep-panorama-entry-icon-size);
    color: var(--upaep-panorama-entry-icon-color);
    background: var(--upaep-panorama-entry-icon-bg);
    border-radius: var(--upaep-panorama-entry-icon-radius);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;

    & svg {
        width: var(--upaep-panorama-entry-icon-glyph);
        height: var(--upaep-panorama-entry-icon-glyph);
    }
}

.upaep__panorama-entry-body {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: .1rem;
    flex: 1 1 auto;
}

.upaep__panorama-entry-label {
    font-size: var(--upaep-panorama-entry-label-fz);
    font-weight: var(--upaep-panorama-entry-label-fw);
    color: var(--upaep-panorama-entry-label-color);
    line-height: 1.35;
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    flex-wrap: wrap;
    transition: color .15s ease;
}

.upaep__panorama-entry-desc {
    font-size: var(--upaep-panorama-entry-desc-fz);
    color: var(--upaep-panorama-entry-desc-color);
    line-height: 1.4;
    /*? Recorte a N líneas sin cortar palabras a mano. */
    display: -webkit-box;
    -webkit-line-clamp: var(--upaep-panorama-entry-desc-lines);
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.upaep__panorama-entry-arrow {
    width: var(--upaep-panorama-entry-arrow-size);
    height: var(--upaep-panorama-entry-arrow-size);
    color: var(--upaep-panorama-entry-arrow-color);
    align-self: center;
    flex: 0 0 auto;
    opacity: 0;
    transform: translateX(-.25rem);
    transition: opacity .15s ease, transform .15s ease;

    & svg {
        width: 100%;
        height: 100%;
    }
}

/* ── Tipo: pestañas verticales ───────────────────────────────────────────────
   Columna de categorías a la izquierda que cambia el contenido de la derecha
   sin cerrar el panel. Solo un pane visible (`display:none` en los demás, así
   tampoco entran en el orden de tabulación).                                  */
.upaep__panorama-block.__tabs {
    gap: var(--upaep-panorama-tabs-gap);
    display: grid;
    /*? La columna de pestañas es un ANCHO MÁXIMO, no fijo: con `15rem` a secas,
       en un panel estrecho se quedaba con 240px y empujaba el contenido fuera
       de la caja (scroll horizontal). Con `minmax(0, …)` cede espacio. */
    grid-template-columns: minmax(0, var(--upaep-panorama-tabs-width)) minmax(0, 1fr);
    align-items: start;
    min-width: 0;
}

/*? Apilado de las pestañas cuando la CAJA es estrecha, no el viewport: un panel
   puede ser angosto dentro de una ventana ancha (`data-width="auto"`, o una
   barra metida en una columna). Se consulta el contenedor declarado en el
   panel; la media query de abajo queda como respaldo para el caso en que el
   panel no pueda ser contenedor (ver la nota de `container-type`).
   45rem = los 42rem de contenido que se medían en el cuerpo + su relleno por
   defecto (1.5rem por lado), ahora que se consulta el panel completo. */
@container panoramaPanel (max-width: 45rem) {
    .upaep__panorama-block.__tabs {
        grid-template-columns: minmax(0, 1fr);
        gap: .75rem;
    }

    .upaep__panorama-tabs-list {
        padding-inline-end: 0;
        margin-inline-end: 0;
        padding-bottom: .5rem;
        border-inline-end: 0;
        border-bottom: var(--upaep-panorama-tabs-divider-size) solid var(--upaep-panorama-tabs-divider-color);
    }
}

/*? Respaldo por viewport para el "escritorio estrecho". */
@media (max-width: 64rem) {
    .upaep__panorama-block.__tabs {
        grid-template-columns: minmax(0, 1fr);
        gap: .75rem;
    }

    .upaep__panorama-tabs-list {
        padding-inline-end: 0;
        margin-inline-end: 0;
        padding-bottom: .5rem;
        border-inline-end: 0;
        border-bottom: var(--upaep-panorama-tabs-divider-size) solid var(--upaep-panorama-tabs-divider-color);
    }
}

/*? El divisor va CENTRADO en el hueco entre columnas: se empuja media separación
   con padding y se recupera con el mismo margen negativo. Antes se usaba la
   separación completa, así que la línea acababa pegada al contenido de la
   derecha y este se veía apretado contra ella. */
.upaep__panorama-tabs-list {
    gap: var(--upaep-panorama-tabs-list-gap);
    padding-inline-end: calc(var(--upaep-panorama-tabs-gap) / 2);
    border-inline-end: var(--upaep-panorama-tabs-divider-size) solid var(--upaep-panorama-tabs-divider-color);
    margin-inline-end: calc(var(--upaep-panorama-tabs-gap) / -2);
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.upaep__panorama-tab {
    padding: var(--upaep-panorama-tab-padding);
    gap: var(--upaep-panorama-tab-gap);
    font-family: inherit;
    font-size: var(--upaep-panorama-tab-fz);
    font-weight: var(--upaep-panorama-tab-fw);
    color: var(--upaep-panorama-tab-color);
    background: var(--upaep-panorama-tab-bg);
    border-radius: var(--upaep-panorama-tab-radius);
    position: relative;
    display: flex;
    align-items: center;
    text-align: start;
    border: 0;
    cursor: pointer;
    transition: color .15s ease, background-color .15s ease;

    /*? Marcador lateral: se revela con scaleY para poder animarlo. */
    &::before {
        content: "";
        width: var(--upaep-panorama-tab-marker-size);
        background: var(--upaep-panorama-tab-marker-color);
        border-radius: 999px;
        position: absolute;
        inset: .35rem auto .35rem 0;
        transform: scaleY(0);
        transition: transform .18s var(--upaep-panorama-panel-easing);
    }

    &:hover {
        color: var(--upaep-panorama-tab-hover-color);
        background: var(--upaep-panorama-tab-hover-bg);
    }

    &.is-active {
        font-weight: var(--upaep-panorama-tab-active-fw);
        color: var(--upaep-panorama-tab-active-color);
        background: var(--upaep-panorama-tab-active-bg);

        &::before {
            transform: scaleY(1);
        }

        .upaep__panorama-tab-arrow {
            opacity: 1;
            transform: translateX(0);
        }
    }
}

.upaep__panorama-tab-icon {
    width: var(--upaep-panorama-tab-icon-size);
    height: var(--upaep-panorama-tab-icon-size);
    flex: 0 0 auto;

    & svg {
        width: 100%;
        height: 100%;
    }
}

.upaep__panorama-tab-body {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: .05rem;
    flex: 1 1 auto;
}

.upaep__panorama-tab-label {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    flex-wrap: wrap;
    line-height: 1.35;
}

.upaep__panorama-tab-desc {
    font-size: var(--upaep-panorama-tab-desc-fz);
    font-weight: var(--upaep-fw-regular);
    color: var(--upaep-panorama-tab-desc-color);
    line-height: 1.35;
}

.upaep__panorama-tab-arrow {
    width: var(--upaep-panorama-tab-arrow-size);
    height: var(--upaep-panorama-tab-arrow-size);
    flex: 0 0 auto;
    opacity: 0;
    transform: translateX(-.2rem);
    transition: opacity .15s ease, transform .15s ease;

    & svg {
        width: 100%;
        height: 100%;
    }
}

.upaep__panorama-tabs-panes {
    min-width: 0;
}

.upaep__panorama-tabpane {
    min-width: 0;

    &:not(.is-active) {
        display: none;
    }

    &.is-active {
        animation: var(--upaep-panorama-panel-in) var(--upaep-panorama-panel-duration) var(--upaep-panorama-panel-easing) both;
    }
}

.upaep__panorama-tabpane-inner {
    column-gap: var(--upaep-panorama-tabpane-column-gap);
    row-gap: var(--upaep-panorama-tabpane-row-gap);
    display: grid;
    /*? Mismo patrón acotado que el panel (incluido el `min(100%, colmin)`, que
       es lo que evita el desborde en cajas estrechas). */
    grid-template-columns: repeat(auto-fit, minmax(max(
        min(100%, var(--upaep-panorama-tabpane-colmin)),
        (100% - (var(--upaep-panorama-tabpane-columns) - 1) * var(--upaep-panorama-tabpane-column-gap)) / var(--upaep-panorama-tabpane-columns)
    ), 1fr));
    align-content: start;
    min-width: 0;
}

/* ── Pestañas anidadas (columnas encadenadas) ────────────────────────────────
   Una pestaña puede abrir otro nivel de pestañas. Como el bloque es un grid
   `lista | contenido`, al anidarlo salen columnas en cadena sin CSS nuevo: solo
   hay que quitarle el aire de "bloque suelto" y afinar el ancho por nivel.     */
.upaep__panorama-tabpane .upaep__panorama-block.__tabs {
    --upaep-panorama-tabs-width: var(--upaep-panorama-tabs-width-nested);
    --upaep-panorama-tabs-gap: var(--upaep-panorama-tabs-gap-nested);
    min-width: 0;

    /*? En una cadena de columnas, el último nivel NO debe estirarse: con
       `1fr` se llevaba todo el espacio sobrante y dos enlaces cortos acababan
       en una columna del doble de ancha que los menús que los preceden.
       `auto` la dimensiona por su contenido y `justify-content: start` deja el
       sobrante a la derecha, que es como se lee una cadena de menús. */
    grid-template-columns: minmax(0, var(--upaep-panorama-tabs-width)) minmax(0, auto);
    justify-content: start;
}

/*? A partir del tercer nivel la lista se estrecha otro poco: si no, tres
   columnas de menú dejan sin sitio al contenido final. */
.upaep__panorama-block.__tabs[data-level="3"],
.upaep__panorama-block.__tabs[data-level="4"] {
    --upaep-panorama-tabs-width: var(--upaep-panorama-tabs-width-deep);
}

/*? Revelado progresivo (drill-down): mientras no hay nada elegido en este nivel,
   la columna de contenido no ocupa sitio ni se dibuja el divisor. El nivel
   siguiente va apareciendo conforme se elige, en lugar de mostrarse todo. */
.upaep__panorama-block.__tabs[data-reveal="progressive"]:not(:has(> .upaep__panorama-tabs-panes > .upaep__panorama-tabpane.is-active)) {
    grid-template-columns: minmax(0, var(--upaep-panorama-tabs-width));

    & > .upaep__panorama-tabs-list {
        border-inline-end: 0;
        padding-inline-end: 0;
        margin-inline-end: 0;
    }

    & > .upaep__panorama-tabs-panes {
        display: none;
    }
}

.upaep__panorama-tabpane[data-columns="1"] { --upaep-panorama-tabpane-columns: 1; }
.upaep__panorama-tabpane[data-columns="2"] { --upaep-panorama-tabpane-columns: 2; }
.upaep__panorama-tabpane[data-columns="3"] { --upaep-panorama-tabpane-columns: 3; }
.upaep__panorama-tabpane[data-columns="4"] { --upaep-panorama-tabpane-columns: 4; }

/*? Sin pestaña marcada como activa el JS activa la primera; si el JS no llega,
   este respaldo evita que el bloque se vea vacío. */
.upaep__panorama:not(.is-initialized) .upaep__panorama-tabpane:first-child {
    display: block;
}

/* ── Submenús (subitems) ─────────────────────────────────────────────────────
   Una entrada con hijos es un BOTÓN que despliega su sublista en su sitio (no
   un flyout lateral: en móvil no tendría dónde caber). Se anidan sin límite.  */
button.upaep__panorama-entry {
    width: 100%;
    font-family: inherit;
    font-size: inherit;
    text-align: start;
    border: 0;
    cursor: pointer;
}

.upaep__panorama-entry-caret {
    width: var(--upaep-panorama-caret-toggle-size);
    height: var(--upaep-panorama-caret-toggle-size);
    color: var(--upaep-panorama-entry-desc-color);
    align-self: center;
    flex: 0 0 auto;
    transition: transform .18s var(--upaep-panorama-panel-easing);

    & svg {
        width: 100%;
        height: 100%;
    }
}

.upaep__panorama-entry.__toggle.is-open {
    .upaep__panorama-entry-caret {
        transform: rotate(var(--upaep-panorama-caret-rotate));
    }

    .upaep__panorama-entry-label {
        color: var(--upaep-panorama-entry-hover-label-color);
    }
}

.upaep__panorama-sublist {
    gap: var(--upaep-panorama-sublist-gap);
    margin: var(--upaep-panorama-sublist-margin);
    margin-inline-start: var(--upaep-panorama-sublist-indent);
    padding: 0;
    padding-inline-start: var(--upaep-panorama-sublist-indent);
    border-inline-start: var(--upaep-panorama-sublist-rail-size) solid var(--upaep-panorama-sublist-rail-color);
    list-style: none;
    display: flex;
    flex-direction: column;
    /*? Como en la lista de primer nivel: cada entrada mide por su contenido. */
    align-items: flex-start;
    /*? Igual que el acordeón móvil: la salida se anima y, cerrado, los enlaces
       NO quedan en el orden de tabulación. */
    max-height: 0;
    overflow: hidden;
    visibility: hidden;
    transition:
        max-height var(--upaep-panorama-sublist-duration) var(--upaep-panorama-panel-easing),
        visibility 0s linear var(--upaep-panorama-sublist-duration);

    /*? Abierta: se anima hasta la ALTURA MEDIDA que escribe el JS, acotada por
       el tope. Con el tope a pelo (100vh) la curva se agotaba en los primeros
       cuadros y el despliegue se veía como un salto seco. */
    &.is-show {
        max-height: min(
            var(--upaep-panorama-sublist-h, var(--upaep-panorama-sublist-maxheight)),
            var(--upaep-panorama-sublist-maxheight));
        visibility: visible;
        transition:
            max-height var(--upaep-panorama-sublist-duration) var(--upaep-panorama-panel-easing),
            visibility 0s;
    }

    /*? Terminada la entrada se suelta el tope, para que un submenú anidado
       pueda desplegarse dentro sin quedar recortado. */
    &.is-show.is-expanded {
        max-height: none;
        overflow: visible;
    }

    /*? Las entradas de un submenú pesan un poco menos que las de primer nivel. */
    .upaep__panorama-entry-label {
        font-size: var(--upaep-panorama-subentry-fz);
        font-weight: var(--upaep-fw-regular);
    }
}

/* ── Tipo: tarjeta ─────────────────────────────────────────────────────────── */
.upaep__panorama-block.__card {
    background: var(--upaep-panorama-card-bg);
    border-radius: var(--upaep-panorama-card-radius);
    box-shadow: var(--upaep-panorama-card-shadow);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    text-decoration: none;
    transition: box-shadow .18s ease;

    &:hover {
        box-shadow: var(--upaep-panorama-card-hover-shadow);
    }
}

.upaep__panorama-card-media {
    aspect-ratio: var(--upaep-panorama-card-media-ratio);
    max-height: var(--upaep-panorama-card-media-maxheight);
    width: 100%;
    display: block;
    object-fit: cover;
    background: var(--ui-surface-hover, #dee2e6);
}

.upaep__panorama-card-body {
    padding: var(--upaep-panorama-card-body-padding);
    gap: var(--upaep-panorama-card-gap);
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
}

.upaep__panorama-card-title {
    margin: 0;
    font-size: var(--upaep-panorama-card-title-fz);
    font-weight: var(--upaep-panorama-card-title-fw);
    color: var(--upaep-panorama-card-title-color);
    line-height: 1.3;
}

.upaep__panorama-card-text {
    margin: 0;
    font-size: var(--upaep-panorama-card-text-fz);
    color: var(--upaep-panorama-card-text-color);
    line-height: 1.45;
}

.upaep__panorama-card-cta {
    margin-top: auto;
    font-size: var(--upaep-panorama-card-text-fz);
    font-weight: var(--upaep-fw-semibold);
    color: var(--ui-primary, var(--upaep-color-primary));
    display: inline-flex;
    align-items: center;
    gap: .35rem;
}

/* ── Tipo: banner / promo ──────────────────────────────────────────────────── */
.upaep__panorama-block.__banner {
    padding: var(--upaep-panorama-banner-padding);
    gap: var(--upaep-panorama-banner-gap);
    color: var(--upaep-panorama-banner-color);
    background: var(--upaep-panorama-banner-bg);
    border-radius: var(--upaep-panorama-banner-radius);
    display: flex;
    align-items: center;
    /*? Envuelve en vez de estrujar: en una columna estrecha, icono + texto + CTA
       en una sola fila dejaban al texto tan angosto que se partía palabra a
       palabra. Con wrap y una base mínima, el CTA baja a su propia línea. */
    flex-wrap: wrap;
    text-decoration: none;
    min-width: 0;

    & .upaep__panorama-banner-icon {
        width: var(--upaep-panorama-banner-icon-size);
        height: var(--upaep-panorama-banner-icon-size);
        flex: 0 0 auto;

        & svg {
            width: 100%;
            height: 100%;
        }
    }

    & .upaep__panorama-banner-body {
        min-width: 0;
        display: flex;
        flex-direction: column;
        gap: .15rem;
        /*? Base mínima: por debajo de esto el CTA envuelve en lugar de robarle
           ancho al texto. */
        flex: 1 1 var(--upaep-panorama-banner-body-min);
    }

    & .upaep__panorama-banner-title {
        margin: 0;
        font-size: var(--upaep-panorama-banner-title-fz);
        font-weight: var(--upaep-panorama-banner-title-fw);
        line-height: 1.3;
    }

    & .upaep__panorama-banner-text {
        margin: 0;
        font-size: var(--upaep-panorama-banner-text-fz);
        line-height: 1.4;
        opacity: .9;
    }

    /*? El CTA hereda el color del banner (antes iba con un style inline). */
    & .upaep__panorama-card-cta {
        color: inherit;
    }
}

/* ── Tipo: etiquetas / chips ────────────────────────────────────────────────── */
.upaep__panorama-block.__tags {
    gap: var(--upaep-panorama-tags-gap);
    display: flex;
    flex-wrap: wrap;
    align-content: start;
    min-width: 0;

    /*? El título del bloque ocupa su propia línea. `width: 100%` en lugar de
       `flex: 1 0 100%`: con el flex-basis la caja se pasaba unos píxeles del
       contenedor y desbordaba el panel en cajas estrechas. */
    & > .upaep__panorama-group-title {
        width: 100%;
        max-width: 100%;
        min-width: 0;
    }
}

.upaep__panorama-tag {
    /*? Un chip es un flex item que no se encoge por sí solo: sin este techo,
       una etiqueta larga sacaba la fila entera fuera del panel. */
    max-width: 100%;
    padding: var(--upaep-panorama-tag-padding);
    font-size: var(--upaep-panorama-tag-fz);
    color: var(--upaep-panorama-tag-color);
    background: var(--upaep-panorama-tag-bg);
    border-radius: var(--upaep-panorama-tag-radius);
    border: var(--upaep-panorama-tag-border-size) solid var(--upaep-panorama-tag-border-color);
    text-decoration: none;
    line-height: 1.4;
    transition: color .15s ease, background-color .15s ease;

    &:hover {
        color: var(--upaep-panorama-tag-hover-color);
        background: var(--upaep-panorama-tag-hover-bg);
    }
}

/* ── Tipos simples: encabezado, texto, separador, botones, libre ───────────── */
.upaep__panorama-block.__heading {
    margin: var(--upaep-panorama-heading-margin);
    font-size: var(--upaep-panorama-heading-fz);
    font-weight: var(--upaep-panorama-heading-fw);
    color: var(--upaep-panorama-heading-color);
    line-height: 1.25;
}

.upaep__panorama-block.__text {
    margin: 0;
    font-size: var(--upaep-panorama-text-fz);
    color: var(--upaep-panorama-text-color);
    line-height: 1.5;
}

.upaep__panorama-block.__divider {
    height: var(--upaep-panorama-divider-size);
    margin: var(--upaep-panorama-divider-margin);
    background: var(--upaep-panorama-divider-color);
    border: 0;
    grid-column: 1 / -1;
}

.upaep__panorama-block.__buttons {
    gap: .5rem;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    align-content: start;
}

.upaep__panorama-block.__custom {
    min-width: 0;
}

/* ── Separador dentro de la barra ──────────────────────────────────────────── */
.upaep__panorama-separator {
    width: var(--upaep-panorama-divider-size);
    align-self: stretch;
    margin-inline: .25rem;
    background: var(--upaep-panorama-divider-color);
    border: 0;
}

/* ═══════════════════════════════════════════════════════════════════════════
   SUPERFICIES MÓVILES
   El nodo es SIEMPRE el mismo (.upaep__panorama-collapse): lo que cambia es el
   modificador `__surface-*` que pone el JS. Los paneles pasan a ser acordeones
   en su sitio (viven dentro de su <li>), así que las tres superficies comparten
   el mismo comportamiento interno.
   ═══════════════════════════════════════════════════════════════════════════ */
.upaep__panorama.is-mobile {
    /*? En una sola columna la imagen de una tarjeta a 16/9 se come la pantalla:
       se le pone techo (sigue siendo variable). */
    --upaep-panorama-card-media-maxheight: 9rem;

    .upaep__panorama-collapse {
        gap: var(--upaep-panorama-overlay-gap);
        background: var(--upaep-panorama-overlay-bg);
        display: flex;
        flex-direction: column;
    }

    .upaep__panorama-items {
        flex-direction: column;
        align-items: stretch;
        flex-wrap: nowrap;
        flex: 0 0 auto;
    }

    .upaep__panorama-item {
        flex-direction: column;
        align-items: stretch;
    }

    .upaep__panorama-trigger,
    .upaep__panorama-link {
        white-space: normal;
        text-align: start;

        /*? El caret se empuja al borde con un margen automático, NO con
           `justify-content: space-between`: con tres hijos (icono + etiqueta +
           caret) el reparto dejaba la etiqueta centrada, separada de su icono.
           Así icono y etiqueta van juntos a la izquierda y el caret al final. */
        .upaep__panorama-caret {
            margin-inline-start: auto;
        }

        /*? En vertical el subrayado inferior no cuadra: pasa a barra lateral. */
        &::after {
            width: var(--upaep-panorama-indicator-size);
            height: auto;
            inset: var(--upaep-panorama-indicator-inset) auto var(--upaep-panorama-indicator-inset) 0;
            transform: scaleY(0);
        }

        &.is-active::after,
        &[aria-expanded="true"]::after {
            transform: scaleY(1);
        }

        &[aria-expanded="true"] .upaep__panorama-caret {
            transform: rotate(var(--upaep-panorama-caret-rotate));
        }
    }

    .upaep__panorama-actions {
        padding: .5rem .25rem;
        flex-wrap: wrap;
    }

    /*? Acordeón: el panel deja de flotar y se abre en su sitio.
       `visibility` se retrasa a la duración de la transición: así la salida se
       anima completa y, cerrado, el panel NO queda en el orden de tabulación
       (con max-height:0 sus enlaces seguirían siendo focalizables). */
    .upaep__panorama-panel {
        width: auto;
        max-width: none;
        border: 0;
        border-radius: 0;
        box-shadow: none;
        background: transparent;
        position: static;
        inset: auto;
        /*? Red de seguridad: un panel estático NUNCA debe llevar margen
           automático en el eje transversal (anula el stretch del flex y lo
           colapsa a su contenido mínimo). */
        margin-inline: 0;
        /*? z-index: auto es OBLIGATORIO en un panel que ya no flota.
           `.upaep__panorama-item` es display:flex, y un flex item con z-index
           crea contexto de apilamiento AUNQUE sea position:static: el panel se
           quedaba compitiendo en la capa 1015 y, por orden de documento, tapaba
           el panel flotante de una barra anterior de la misma página (solo se
           veía su texto, porque aquí el fondo es transparente). */
        z-index: auto;
        max-height: 0;
        opacity: 1;
        overflow: hidden;
        visibility: hidden;
        transition:
            max-height var(--upaep-panorama-panel-mobile-duration) var(--upaep-panorama-panel-easing),
            visibility 0s linear var(--upaep-panorama-panel-mobile-duration);

        &.is-show,
        &.is-hiding {
            animation: none;
        }

        &.is-show {
            max-height: var(--upaep-panorama-panel-mobile-maxheight);
            visibility: visible;
            transition:
                max-height var(--upaep-panorama-panel-mobile-duration) var(--upaep-panorama-panel-easing),
                visibility 0s;
        }
    }

    .upaep__panorama-panel-inner {
        padding: var(--upaep-panorama-panel-mobile-padding);
        max-height: none;
        grid-template-columns: 1fr;
        overflow: visible;
    }

    /*? En una columna, el span no aplica. */
    .upaep__panorama-block[data-span] {
        grid-column: span 1;
    }

    /*? Pestañas verticales en una sola columna: la lista arriba y el contenido
       de la pestaña activa debajo. El riel lateral pasa a ser inferior. */
    .upaep__panorama-block.__tabs {
        grid-template-columns: minmax(0, 1fr);
        gap: .75rem;
    }

    .upaep__panorama-tabs-list {
        padding-inline-end: 0;
        margin-inline-end: 0;
        padding-bottom: .5rem;
        border-inline-end: 0;
        border-bottom: var(--upaep-panorama-tabs-divider-size) solid var(--upaep-panorama-tabs-divider-color);
    }

    .upaep__panorama-tabpane-inner {
        grid-template-columns: 1fr;
    }

    .upaep__panorama-panel-footer {
        padding: .75rem .5rem;
        background: transparent;
        border-top-width: var(--upaep-panorama-divider-size);
        border-top-color: var(--upaep-panorama-divider-color);
    }

    .upaep__panorama-separator {
        width: auto;
        height: var(--upaep-panorama-divider-size);
        margin: .25rem 0;
    }
}

/* ── Superficie: collapse (acordeón dentro de la barra, sin overlay) ───────── */
.upaep__panorama.is-mobile.__surface-collapse {
    .upaep__panorama-bar {
        flex-wrap: wrap;
    }

    /*? El menú se despliega DEBAJO de la barra (como cualquier navbar
       colapsable), pero el orden del DOM es marca → collapse → botón: al
       envolver, el botón caía a una tercera fila, debajo del menú abierto.
       `order` lo devuelve a la primera fila, junto a la marca. */
    .upaep__panorama-collapse {
        order: 1;
        width: 100%;
        flex-basis: 100%;
        max-height: 0;
        overflow: hidden;
        visibility: hidden;
        transition:
            max-height var(--upaep-panorama-overlay-duration) var(--upaep-panorama-overlay-easing),
            visibility 0s linear var(--upaep-panorama-overlay-duration);

        /*? Abierto se anima a la ALTURA MEDIDA (`--upaep-panorama-collapse-h`,
           que escribe el JS), acotada por el máximo configurado. Animar
           directamente al tope hacía que la curva se cortara a media entrada y
           que el cierre pareciera arrancar con retraso. */
        &.is-show {
            max-height: min(
                var(--upaep-panorama-collapse-h, var(--upaep-panorama-collapse-maxheight)),
                var(--upaep-panorama-collapse-maxheight));
            visibility: visible;
            transition:
                max-height var(--upaep-panorama-overlay-duration) var(--upaep-panorama-overlay-easing),
                visibility 0s;
        }

        /*? Al terminar la entrada se libera el tope: así un acordeón interno
           puede crecer y, si se pasa del máximo, aparece el scroll. */
        &.is-show.is-expanded {
            max-height: var(--upaep-panorama-collapse-maxheight);
            overflow-y: auto;
            overscroll-behavior: contain;
            scrollbar-width: thin;
            scrollbar-color: var(--ui-border, var(--upaep-color-border-grey)) var(--ui-surface-hover, #dee2e6);
        }
    }
}

/* ── Superficie: offcanvas (panel lateral fijo) ────────────────────────────── */
.upaep__panorama.is-mobile.__surface-offcanvas {
    .upaep__panorama-collapse {
        width: min(var(--upaep-panorama-overlay-width), var(--upaep-panorama-overlay-maxwidth));
        padding: var(--upaep-panorama-overlay-padding);
        box-shadow: var(--upaep-panorama-overlay-shadow);
        border-radius: var(--upaep-panorama-overlay-radius);
        z-index: var(--upaep-panorama-overlay-zindex);
        position: fixed;
        inset: 0 0 0 auto;
        overflow-y: auto;
        overscroll-behavior: contain;
        scrollbar-width: thin;
        scrollbar-color: var(--ui-border, var(--upaep-color-border-grey)) var(--ui-surface-hover, #dee2e6);
        transform: translateX(100%);
        /*? visibility retrasada a la duración: la salida se anima completa y,
           cerrado, el panel fuera de pantalla NO es tabulable. */
        visibility: hidden;
        transition:
            transform var(--upaep-panorama-overlay-duration) var(--upaep-panorama-overlay-easing),
            visibility 0s linear var(--upaep-panorama-overlay-duration);

        &.is-show {
            transform: translateX(0);
            visibility: visible;
            transition:
                transform var(--upaep-panorama-overlay-duration) var(--upaep-panorama-overlay-easing),
                visibility 0s;
        }
    }

    /*? Lado izquierdo: solo cambia el anclaje y el sentido de la salida.
       El modificador se llama __side-* y no __start/__end a propósito: esos ya
       son las variantes de alineación de los items de la barra y colisionarían
       (un offcanvas "a la derecha" alineaba la lista al final en móvil). */
    &.__side-start .upaep__panorama-collapse {
        inset: 0 auto 0 0;
        transform: translateX(-100%);

        &.is-show {
            transform: translateX(0);
        }
    }

    .upaep__panorama-collapse-header {
        padding: var(--upaep-panorama-collapse-header-padding);
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: .5rem;
        flex: 0 0 auto;
    }

    .upaep__panorama-close {
        display: inline-flex;
    }
}

/* ── Superficie: fullscreen (el menú ocupa la pantalla) ───────────────────────
   Es el offcanvas llevado al 100%: mismo nodo, mismo encabezado con UNA aspa.
   La usa `mobile: 'fullscreen'` y, en `mobile: 'auto'`, cualquier instancia con
   `width: 'screen'` — un menú que en escritorio tapa todo y en móvil se encoge a
   un cajón de 22rem no sería la misma pieza.                                   */
.upaep__panorama.is-mobile.__surface-fullscreen {
    .upaep__panorama-collapse {
        width: 100%;
        max-width: none;
        padding: var(--upaep-panorama-fullscreen-padding);
        border-radius: 0;
        box-shadow: none;
        z-index: var(--upaep-panorama-overlay-zindex);
        position: fixed;
        inset: 0;
        overflow-y: auto;
        overscroll-behavior: contain;
        scrollbar-width: thin;
        scrollbar-color: var(--ui-border, var(--upaep-color-border-grey)) var(--ui-surface-hover, #dee2e6);
        /*? Entra desde abajo con un fundido: a pantalla completa un deslizamiento
           lateral se lee como «otra página», y esto es el mismo menú creciendo. */
        opacity: 0;
        transform: translateY(var(--upaep-panorama-fullscreen-slide));
        visibility: hidden;
        transition:
            opacity var(--upaep-panorama-overlay-duration) var(--upaep-panorama-overlay-easing),
            transform var(--upaep-panorama-overlay-duration) var(--upaep-panorama-overlay-easing),
            visibility 0s linear var(--upaep-panorama-overlay-duration);

        &.is-show {
            opacity: 1;
            transform: translateY(0);
            visibility: visible;
            transition:
                opacity var(--upaep-panorama-overlay-duration) var(--upaep-panorama-overlay-easing),
                transform var(--upaep-panorama-overlay-duration) var(--upaep-panorama-overlay-easing),
                visibility 0s;
        }
    }

    .upaep__panorama-collapse-header {
        padding: var(--upaep-panorama-collapse-header-padding);
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: .5rem;
        flex: 0 0 auto;
    }

    .upaep__panorama-close {
        display: inline-flex;
    }

    /*? Con toda la pantalla disponible el contenido se centra y respira. */
    .upaep__panorama-items,
    .upaep__panorama-collapse-header,
    .upaep__panorama-actions {
        width: 100%;
        max-width: var(--upaep-panorama-fullscreen-maxwidth);
        margin-inline: auto;
    }
}

/* ── Superficie: bottomSheet (hoja inferior arrastrable) ───────────────────── */
.upaep__panorama.is-mobile.__surface-sheet {
    .upaep__panorama-collapse {
        height: var(--upaep-panorama-sheet-height);
        padding: 0 var(--upaep-panorama-overlay-padding) var(--upaep-panorama-overlay-padding);
        box-shadow: var(--upaep-panorama-overlay-shadow);
        border-radius: var(--upaep-panorama-sheet-radius);
        z-index: var(--upaep-panorama-overlay-zindex);
        position: fixed;
        inset: auto 0 0 0;
        transform: translateY(100%);
        visibility: hidden;
        transition:
            transform var(--upaep-panorama-overlay-duration) var(--upaep-panorama-overlay-easing),
            visibility 0s linear var(--upaep-panorama-overlay-duration);

        &.is-show {
            /*? El arrastre escribe --upaep-panorama-sheet-y; en reposo vale 0. */
            transform: translateY(var(--upaep-panorama-sheet-y));
            visibility: visible;
            transition:
                transform var(--upaep-panorama-overlay-duration) var(--upaep-panorama-overlay-easing),
                visibility 0s;
        }

        /*? Durante el arrastre no hay transición: el panel sigue al dedo. */
        &.is-dragging {
            transition: none;
        }
    }

    /*? Zona de arrastre. `touch-action: none` es imprescindible: sin él el
       navegador interpreta el gesto vertical como scroll, se apropia del
       puntero y dispara `pointercancel` a media pasada — el arrastre "a veces"
       no respondía por esto, no por la lógica. Se limita a asa y encabezado
       para que el cuerpo del sheet siga haciendo scroll con el dedo. */
    .upaep__panorama-handle,
    .upaep__panorama-collapse-header {
        touch-action: none;
    }

    /*? El asa es fina: se le da un área de toque cómoda con un pseudoelemento,
       sin cambiar su aspecto. */
    .upaep__panorama-handle {
        width: var(--upaep-panorama-handle-width);
        height: var(--upaep-panorama-handle-height);
        margin: var(--upaep-panorama-handle-margin);
        background: var(--upaep-panorama-handle-color);
        border-radius: 999px;
        display: block;
        position: relative;
        flex: 0 0 auto;

        &::after {
            content: "";
            position: absolute;
            inset: calc(var(--upaep-panorama-handle-hit) * -1) calc(var(--upaep-panorama-handle-hit) * -2);
        }
    }

    .upaep__panorama-collapse-header {
        padding: var(--upaep-panorama-collapse-header-padding);
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: .5rem;
        flex: 0 0 auto;
    }

    /*? Los controles del encabezado sí deben poder pulsarse. */
    .upaep__panorama-collapse-header :is(button, a) {
        touch-action: auto;
    }

    .upaep__panorama-close {
        display: inline-flex;
    }

    /*? Solo el cuerpo hace scroll; asa y encabezado quedan fijos por flex. */
    .upaep__panorama-items {
        overflow-y: auto;
        overscroll-behavior: contain;
        scrollbar-width: thin;
        scrollbar-color: var(--ui-border, var(--upaep-color-border-grey)) var(--ui-surface-hover, #dee2e6);
        flex: 1 1 auto;
    }
}

.upaep__panorama-collapse-title {
    font-size: var(--upaep-panorama-collapse-title-fz);
    font-weight: var(--upaep-panorama-collapse-title-fw);
    color: var(--ui-text-strong, var(--upaep-color-black));
}

/* ── Variantes de la barra ─────────────────────────────────────────────────── */
/*? Barra pegada arriba. */
.upaep__panorama.__sticky {
    position: sticky;
    top: 0;
    z-index: var(--upaep-zindex-sticky);
}

/*? Elevación mientras una superficie overlay está abierta.
   El nodo raíz casi siempre crea su propio contexto de apilamiento (basta
   `__sticky`, o un `transform`/`filter` en un ancestro): dentro de él, el
   z-index del panel fijo no compite con el backdrop, que vive en el <body>, y
   el panel quedaría DEBAJO del oscurecido. Elevando el raíz por encima del
   backdrop, el panel sube con él. El JS pone la clase al abrir y la retira al
   terminar el cierre, para que la salida también se anime por encima.
   Efecto buscado: la barra queda visible sobre el oscurecido (patrón habitual
   de un drawer/sheet móvil) mientras el resto de la página se atenúa.
   Límite conocido: si el contexto de apilamiento lo crea un ANCESTRO del
   componente, hay que elevar ese ancestro — igual que le pasa al
   `.upaep__offcanvas` del kit, que también es `position: fixed` en su sitio. */
.upaep__panorama.is-elevated {
    z-index: var(--upaep-panorama-overlay-zindex);

    /*? Con el overlay abierto el fondo queda `inert`, así que la hamburguesa no
       responde: mostrarla invita a pulsar algo muerto. Se oculta con
       `visibility` (no `display`) para que la barra no cambie de tamaño, y con
       la temporización de `is-elevated`, que el JS retira al TERMINAR el cierre
       —así el botón no reaparece mientras el panel aún se está yendo.
       En `collapse` no aplica: ahí el botón es justamente el que cierra. */
    .upaep__panorama-toggle {
        visibility: hidden;
    }
}

/*? Superficie oscura: se re-apuntan los roles, no se pisan las propiedades. */
.upaep__panorama.__dark {
    --upaep-panorama-bar-bg: var(--ui-surface-inverse, #000000);
    --upaep-panorama-brand-color: var(--ui-on-surface-inverse, #ffffff);
    --upaep-panorama-item-color: var(--ui-on-surface-inverse, #ffffff);
    --upaep-panorama-item-hover-color: var(--ui-on-surface-inverse, #ffffff);
    --upaep-panorama-item-hover-bg: rgba(255, 255, 255, .12);
    --upaep-panorama-item-open-color: var(--ui-on-surface-inverse, #ffffff);
    --upaep-panorama-item-open-bg: rgba(255, 255, 255, .12);
    --upaep-panorama-item-active-color: var(--ui-on-surface-inverse, #ffffff);
    --upaep-panorama-toggle-color: var(--ui-on-surface-inverse, #ffffff);
    --upaep-panorama-toggle-hover-bg: rgba(255, 255, 255, .12);
}

/* ── Variante `__inline`: el panel en un hueco reservado ──────────────────────
   El panel sigue siendo absoluto (cuelga de la barra) pero el componente reserva
   su alto con un margen inferior, así que el hueco existe DESDE EL PRINCIPIO y
   al abrir no tapa nada del contenido de la página: el mega menú vive en un
   contenedor fijo. `margin-bottom` y no `padding-bottom` a propósito: el panel
   se ancla a `top: 100%` del nodo raíz, y el padding movería ese 100%.
   Combínalo con `closeOnOutside: false` y `defaultOpen` si quieres que arranque
   abierto y se quede. */
.upaep__panorama.__inline {
    margin-bottom: var(--upaep-panorama-inline-height);

    .upaep__panorama-panel {
        --upaep-panorama-panel-maxheight: var(--upaep-panorama-inline-height);
        height: var(--upaep-panorama-inline-height);
        border-radius: 0;
        border-inline: 0;
        box-shadow: none;
    }

    .upaep__panorama-panel-inner {
        height: 100%;
        max-height: 100%;
    }
}

/*? En móvil el hueco no tiene sentido: el menú pasa a su superficie. */
.upaep__panorama.__inline.is-mobile {
    margin-bottom: 0;

    .upaep__panorama-panel {
        height: auto;
    }

    .upaep__panorama-panel-inner {
        height: auto;
    }
}

/*? Barra centrada / alineada al final. */
.upaep__panorama.__center .upaep__panorama-items {
    justify-content: center;
}

.upaep__panorama.__end .upaep__panorama-items {
    justify-content: flex-end;
}

/*? Sin sombra ni relleno: para incrustar Panorama en un header propio. */
.upaep__panorama.__bare {
    --upaep-panorama-bar-bg: transparent;
    --upaep-panorama-bar-shadow: none;
    --upaep-panorama-bar-padding: 0;
}

/*? Barra compacta. */
.upaep__panorama.__sm {
    --upaep-panorama-bar-min-height: 2.75rem;
    --upaep-panorama-bar-padding: .25rem .75rem;
    --upaep-panorama-item-padding: .4rem .6rem;
    --upaep-panorama-item-fz: var(--upaep-fz14);
    --upaep-panorama-panel-padding: 1rem;
}

/* ── Estado deshabilitado del componente ──────────────────────────────────── */
.upaep__panorama.is-disabled {
    opacity: .5;
    pointer-events: none;
}

/* ── Arranque sin animación ───────────────────────────────────────────────────
   El JS pone `is-booting` durante la inicialización y la retira un frame
   después. Sin esto, aplicar la superficie inicial cuenta como cambio de
   estado y se anima: en móvil el collapse tardaba la duración de la transición
   en volverse `visibility: hidden`, quedando tabulable mientras tanto. */
.upaep__panorama.is-booting,
.upaep__panorama.is-booting *,
.upaep__panorama.is-booting *::before,
.upaep__panorama.is-booting *::after {
    transition: none !important;
    animation: none !important;
}

/* ── Reducción de movimiento ───────────────────────────────────────────────── */
.upaep__panorama.__reduced-motion,
.upaep__panorama.__reduced-motion * {
    animation-duration: .01ms !important;
    transition-duration: .01ms !important;
}

@media (prefers-reduced-motion: reduce) {

    .upaep__panorama-panel,
    .upaep__panorama-collapse,
    .upaep__panorama-caret,
    .upaep__panorama-entry,
    .upaep__panorama-entry-arrow,
    .upaep__panorama-trigger::after,
    .upaep__panorama-link::after {
        animation-duration: .01ms !important;
        transition-duration: .01ms !important;
    }
}

/* ── Variante estática (`__static`) y respaldo sin JS ─────────────────────────
   Despliega TODO el menú como una lista anidada navegable: sirve para un mapa
   de sitio / menú de pie de página y es el mismo aspecto que se usa como
   respaldo cuando no hay JavaScript.

   Nota deliberada: los paneles NO se dejan visibles con `:not(.is-initialized)`.
   Un script `defer` puede ejecutarse después del primer pintado, así que esa
   regla mostraría el menú entero desplegado durante un instante en cada carga.
   El contenido igual viaja en el HTML (indexable); para el caso real de "JS
   desactivado" se aplica esta misma variante desde un <noscript>:

     <noscript><style>
       .upaep__panorama-items { flex-direction: column; align-items: stretch; }
       .upaep__panorama-panel { position: static; inset: auto; width: auto;
         max-width: none; max-height: none; visibility: visible; opacity: 1;
         box-shadow: none; border: 0; background: transparent; }
       .upaep__panorama-toggle, .upaep__panorama-caret { display: none; }
     </style></noscript>
*/
.upaep__panorama.__static {
    /*? Como lista anidada el relleno de mega panel sobra: aquí se aprieta y se
       indenta, que es lo que hace legible un mapa de sitio. */
    --upaep-panorama-panel-padding: .25rem 0 .75rem 1.25rem;
    --upaep-panorama-panel-row-gap: .5rem;
    --upaep-panorama-panel-column-gap: 1.5rem;
    --upaep-panorama-items-gap: .25rem;
    --upaep-panorama-item-padding: .375rem .5rem;

    .upaep__panorama-items {
        flex-direction: column;
        align-items: stretch;
    }

    .upaep__panorama-item {
        flex-direction: column;
        align-items: stretch;
    }

    .upaep__panorama-toggle,
    .upaep__panorama-close,
    .upaep__panorama-handle,
    .upaep__panorama-caret {
        display: none;
    }

    .upaep__panorama-panel {
        position: static;
        inset: auto;
        width: auto;
        max-width: none;
        max-height: none;
        visibility: visible;
        opacity: 1;
        box-shadow: none;
        border: 0;
        border-radius: 0;
        background: transparent;
        animation: none;
        /*? Igual que en móvil: estático y sin margen automático (ver la nota del
           bloque `is-mobile`). */
        margin-inline: 0;
        /*? Igual que en móvil: un panel en flujo NO debe crear capa. Ver la nota
           del bloque `is-mobile` (flex item + z-index = contexto de apilamiento). */
        z-index: auto;
    }

    .upaep__panorama-panel-inner {
        max-height: none;
        overflow: visible;
    }

    /*? Un mapa de sitio se muestra entero: los submenús quedan desplegados y
       sin caret (no hay nada que alternar). */
    .upaep__panorama-sublist {
        max-height: none;
        visibility: visible;
        transition: none;
    }

    .upaep__panorama-entry-caret {
        display: none;
    }
}
