/* ── Trinity Shell ─────────────────────────────────────────────────────────────
   Cascarón del dashboard (header + sidebar + main + footer) reestilizado en clave
   MINIMALISTA, con las MISMAS CLASES/IDs que el markup legacy de trinity.html
   → es un reemplazo directo (drop-in) de los bloques de shell de trinity.css.

   Se apoya en el lenguaje visual del componente `uikit/sidebar/sidebar.css`:
   superficie clara, hairlines en vez de rellenos, items redondeados, item activo
   con barra de acento a la izquierda, tipografía tenue y scrollbar sutil.

   MODOS
   -----
   Dos ejes independientes en el elemento raíz (o en cualquier ancestro del shell):

     `data-theme`  → la PALETA (la resuelve el core, style.css)
       <html data-theme="light">    → claro fijo
       <html data-theme="dark">     → oscuro fijo
       <html data-theme="auto">     → sigue a prefers-color-scheme
       Sin atributo → claro (no se auto-oscurece nada del portal por sorpresa).

     `data-shell`  → la PIEL DEL CASCARÓN (la resuelve esta hoja, sección 2B)
       <html data-shell="trinity">  → identidad del portal original:
                                      header rojo + sidebar gris + pie negro

   Combinándolos salen los cuatro modos del botón `#themeToggle`:
       claro         →  data-theme="light"
       oscuro        →  data-theme="dark"
       trinity       →  data-theme="light" data-shell="trinity"
       trinity-dark  →  data-theme="dark"  data-shell="trinity"

   Son dos atributos y no un `data-theme="trinity-dark"` porque la paleta del
   CONTENIDO no la pone esta hoja: los roles `--ui-*` los remapea el core bajo
   `[data-theme="dark"]`, y un valor propio dejaría el contenido en claro con el
   cascarón en oscuro. Atajo retrocompatible: `data-theme="trinity"` equivale a
   claro + piel trinity (es el único valor de paleta que esta hoja atiende).

   TOKENS
   ------
   Todo pasa por `--upaep-trinity-*`, y cada token lee el rol semántico `--ui-*`
   del core con FALLBACK literal. Por eso la hoja funciona igual:
     · con style.css cargado  → hereda la marca/tema del design system
     · sola (autocontenida)   → usa sus fallbacks

   USO
   ---
   a) Sobre el monolito:  <link trinity.css> DESPUÉS <link trinity-shell.css>
      (se replica `!important` solo donde el monolito lo usa, p. ej. #elementosMenu)
   b) Sola: ver trinity-shell.html
--------------------------------------------------------------------------------- */

/*? ═══════════════════════════════════════════════════════════════════════════
   1. TOKENS — tema claro
════════════════════════════════════════════════════════════════════════════ */
:root {
    /*? ── Geometría del shell ─────────────────────────────────────────── */
    --upaep-trinity-header-height: 56px;
    --upaep-trinity-footer-height: 48px;
    --upaep-trinity-sidebar-width: 240px;
    --upaep-trinity-sidebar-width-collapsed: 80px;
    --upaep-trinity-sidebar-width-drawer: 268px;
    --upaep-trinity-gutter: 1.5rem;
    --upaep-trinity-gutter-sm: 1rem;
    --upaep-trinity-zindex-header: 1099;
    --upaep-trinity-zindex-sidebar: 1100;
    --upaep-trinity-transition: .28s cubic-bezier(.4, 0, .2, 1);

    /*? Objetivo accionable mínimo (WCAG 2.2 AA · 2.5.8 Target Size). No es un
       gusto de espaciado: es el suelo por debajo del cual un enlace o un botón
       del cascarón deja de cumplir. Vive como token para que las dos capas
       —cascarón y vistas— midan contra el MISMO número y no se separen. */
    --upaep-trinity-target-min: 1.5rem;


    /*? ══════════════════════════════════════════════════════════════════════
       ESCALAS VISUALES
       ─────────────────────────────────────────────────────────────────────
       Antes de esto Trinity no tenía escalas: tenía 26 valores de espaciado
       distintos —de 1.6 a 48 px, en pasos de menos de un píxel—, siete radios
       para tres intenciones y una tipografía en la que `stat-value` (25.6 px)
       pesaba más que el `<h1>` de la página (20 px) y en la que `h2`, `h3` y
       `h4` medían los tres 15 px. Un valor por caso no es un sistema: es una
       colección de decisiones que nadie puede repetir.

       El patrón es SIEMPRE el mismo, y en este orden:

           escala  →  token  →  rol  →  componente

       La escala publica los peldaños. El rol dice PARA QUÉ sirve cada peldaño.
       El componente consume el rol, nunca el peldaño ni —jamás— un literal. Por
       eso aquí no hay ni un token por componente: si una pieza necesita una
       medida nueva, lo que falta es un rol, no un valor.

       Las escalas se publican legibles por máquina en `ai/tokens.json → scales`,
       derivadas de `src/site/data/scales.decisions.json`; un consumidor descubre
       los peldaños, los roles y los valores admitidos sin abrir una sola hoja de
       estilos. `tools/verify-tokens.mjs` comprueba que lo publicado y lo
       declarado aquí sigan siendo lo mismo, y que no reaparezcan literales
       equivalentes.
    ══════════════════════════════════════════════════════════════════════ */

    /*? ── Espaciado ── Base 4. Siete peldaños, y ninguno entre medias: el
       espacio es una decisión de RITMO, y un ritmo con pasos de 0.8 px no se
       percibe, solo se acumula. Se nombran por peldaño y no por píxeles para
       poder reafinar la escala sin renombrar nada. */
    --upaep-trinity-sp-1: .25rem;   /*  4px */
    --upaep-trinity-sp-2: .5rem;    /*  8px */
    --upaep-trinity-sp-3: .75rem;   /* 12px */
    --upaep-trinity-sp-4: 1rem;     /* 16px */
    --upaep-trinity-sp-5: 1.5rem;   /* 24px */
    --upaep-trinity-sp-6: 2rem;     /* 32px */
    --upaep-trinity-sp-7: 3rem;     /* 48px */

    /*? Roles de espaciado. Es la capa que evita el token por componente: una
       pieza nueva pregunta «¿qué papel juega este hueco?», no «¿cuántos px?». */
    --upaep-trinity-gap-tight: var(--upaep-trinity-sp-1);    /* líneas de un mismo bloque */
    --upaep-trinity-gap-inline: var(--upaep-trinity-sp-2);   /* controles de una misma fila */
    --upaep-trinity-gap-stack: var(--upaep-trinity-sp-4);    /* piezas hermanas apiladas */
    --upaep-trinity-pad-bar: var(--upaep-trinity-sp-3);      /* relleno vertical de una barra */
    --upaep-trinity-pad-surface: var(--upaep-trinity-sp-4);  /* relleno de una superficie */
    --upaep-trinity-pad-cell: var(--upaep-trinity-sp-2) var(--upaep-trinity-sp-3);

    /*? ── Radios ── Cuatro intenciones, no siete valores. `pill` es UNA sola
       medida en todo Trinity: dos formas de escribir «cápsula» son dos cápsulas
       distintas en cuanto una de las dos se retoca. */
    --upaep-trinity-radius-sm: 6px;
    --upaep-trinity-radius-md: 8px;
    --upaep-trinity-radius-lg: 12px;
    --upaep-trinity-radius-pill: 999px;

    /*? ── Tipografía ── ROLES, no tamaños sueltos. Los peldaños salen del
       diccionario de primitivas del core (`--upaep-fzN`), que es par: por eso
       aquí no aparecen `--upaep-fz11` ni `--upaep-fz13`, que Trinity venía
       citando aunque el core NO los declara —cada uso caía en su literal de
       reserva, es decir, en un valor fuera de escala escrito trece veces—.

       Dos invariantes que la escala existe para sostener:
         · h1 > stat-value — la cifra de un indicador no puede pesar más que el
           título de la página en la que vive;
         · h1 > h2 > h3 > h4, y ningún par consecutivo visualmente equivalente.

       Las FAMILIAS no se tocan aquí: las declara el kit y Trinity las hereda por
       `--upaep-trinity-font`. Ninguna pieza fija una fuente por su cuenta. */
    --upaep-trinity-fz-h1: var(--upaep-fz24, 1.5rem);        /* 24 */
    --upaep-trinity-fz-stat: var(--upaep-fz22, 1.375rem);    /* 22 */
    --upaep-trinity-fz-h2: var(--upaep-fz20, 1.25rem);       /* 20 */
    --upaep-trinity-fz-h3: var(--upaep-fz16, 1rem);          /* 16 */
    --upaep-trinity-fz-h4: var(--upaep-fz14, .875rem);       /* 14 */
    --upaep-trinity-fz-body: var(--upaep-fz14, .875rem);     /* 14 */
    --upaep-trinity-fz-label: var(--upaep-fz12, .75rem);     /* 12 · nunca por debajo */
    --upaep-trinity-fz-meta: var(--upaep-fz12, .75rem);      /* 12 */

    /*? Pesos por rol. `label` y `meta` comparten tamaño y se separan por peso,
       que es la distinción convencional entre «esto rotula» y «esto acompaña».
       `dt` y `dd` también viven aquí: la etiqueta es secundaria y el valor es lo
       que se lee: al revés, la lista se lee como una lista de etiquetas. */
    --upaep-trinity-fw-heading: var(--upaep-fw-semibold, 600);
    --upaep-trinity-fw-stat: var(--upaep-fw-bold, 700);
    --upaep-trinity-fw-label: var(--upaep-fw-semibold, 600);
    --upaep-trinity-fw-body: var(--upaep-fw-regular, 400);
    --upaep-trinity-fw-value: var(--upaep-fw-semibold, 600);

    /*? Alturas de línea por familia de rol: apretada para titulares, cómoda para
       texto corrido, exacta para lo que vive dentro de una caja de control. */
    --upaep-trinity-lh-heading: 1.25;
    --upaep-trinity-lh-body: 1.45;
    --upaep-trinity-lh-control: 1.2;

    /*? ── Geometría de control ── El alto de un control NO es espaciado: es una
       medida de caja, y por eso tiene su propia familia. El relleno sí sale de
       la escala. */
    /*? Alturas de caja de control. NO son espaciado —de ahi que tengan familia
       propia— pero tampoco pueden ser literales sueltos: `2.375rem` y `1.95rem`
       vivian escritos dentro del bloque del boton, donde nadie mas podia leerlos
       ni reutilizarlos. El pequeno se redondea de 1.95rem a 2rem: 0.8 px que no
       se ven y un numero que si se puede repetir. */
    --upaep-trinity-control-h-md: 2.375rem;  /* 38px */
    --upaep-trinity-control-h-sm: 2rem;      /* 32px */
    --upaep-trinity-control-pad: var(--upaep-trinity-sp-2) var(--upaep-trinity-sp-4);
    --upaep-trinity-control-pad-sm: var(--upaep-trinity-sp-1) var(--upaep-trinity-sp-3);

    /*? Offset lateral del contenido — lo recalculan las clases de estado */
    --upaep-trinity-offset: calc(var(--upaep-trinity-sidebar-width) + var(--upaep-trinity-gutter));

    /*? ── Superficies y texto ─────────────────────────────────────────── */
    --upaep-trinity-surface: var(--ui-surface, #ffffff);
    --upaep-trinity-surface-2: var(--ui-surface-2, #f4f5f6);
    --upaep-trinity-surface-3: var(--ui-surface-3, #eeeeee);
    --upaep-trinity-page-bg: var(--ui-surface-2, #f7f8f9);
    /*? ── Los dos colores de texto de Trinity, y por qué NO son los del core ──
       El core pinta `--ui-text` con el azul institucional y `--ui-text-strong`
       con NEGRO puro. En una vista de dashboard eso sale al revés de lo que se
       espera: el cuerpo tirando a azul y los títulos en negro, que es el color
       que menos pertenece a la marca.

       Se invierte: los títulos y el texto enfático van en `blue-primary`
       (#1D335B, 11.4:1 sobre blanco) y el cuerpo en `dark-grey` (#52565A, 7:1).
       Cero negro, y la jerarquía se lee por color además de por peso. Es lo
       mismo que ya hacía `.upaep__modal.__trinity` en el core: título azul,
       texto gris.

       Solo aplica al tema claro: el bloque `[data-theme="dark"]` de abajo
       devuelve la cadena `--ui-*`, donde el fuerte es blanco. */
    --upaep-trinity-text: var(--upaep-color-dark-grey, #52565A);
    --upaep-trinity-text-strong: var(--upaep-color-blue-primary, #1D335B);
    --upaep-trinity-text-muted: var(--ui-text-muted, #909496);
    --upaep-trinity-divider: var(--ui-divider, #e4e6e8);
    --upaep-trinity-hover-bg: var(--ui-surface-2, #f4f5f6);
    --upaep-trinity-active-bg: var(--ui-surface-hover, #e6e9ee);
    --upaep-trinity-accent: var(--ui-primary, var(--upaep-color-primary, #e10023));
    --upaep-trinity-on-accent: var(--ui-on-primary, #ffffff);

    /*? Item activo del menú. A propósito SIN declarar aquí: se leen con
       `var(--token, var(--upaep-trinity-accent))`, así que sin valor caen al
       acento del ámbito donde se usan (comportamiento de siempre) y quien los
       declare puede separar el TEXTO del activo de su BARRA — lo que necesita
       la variante trinity, donde la barra sigue roja y el texto va en blanco:
         --upaep-trinity-item-active-text
         --upaep-trinity-item-active-bar
       Mismo caso para el grafismo del logo del sidebar (que no es texto y sí
       debe conservar el color de marca):
         --upaep-trinity-brandmark   */
    --upaep-trinity-focus-ring: var(--ui-focus-ring, rgba(225, 0, 35, .22));
    --upaep-trinity-shadow: 0 8px 24px rgba(16, 20, 28, .10);
    --upaep-trinity-font: var(--ui-font-base, var(--upaep-ff-fira-sans, 'Fira Sans', system-ui, sans-serif));

    /*? ── Iconos heredados en base64 ──────────────────────────────────────
       El markup legacy trae PNG/SVG con relleno BLANCO (pensados para barras
       rojas/grises). Sobre superficie clara se invierten por CSS; en oscuro se
       dejan tal cual. Escapes: `.__no-invert` (por icono) y `.__menu-icons-color`
       en `.sidebar` (si tus iconos de menú ya son de color y no deben invertirse). */
    --upaep-trinity-icon-invert: 1;
    --upaep-trinity-menu-icon-invert: 1;

    /*? ── Scrollbar ─────────────────────────────────────────────────────
       Solo propiedades ESTÁNDAR (`scrollbar-width` / `scrollbar-color`), sin
       pseudoelementos `::-webkit-scrollbar`. Soportado por Chrome/Edge 121+,
       Firefox y Safari 18.2+; por debajo se ve la barra nativa del sistema. */
    --upaep-trinity-scrollbar-width: thin;
    /* `auto` = barra nativa de ancho completo */
    --upaep-trinity-scrollbar-thumb: #98a0a8;
    --upaep-trinity-scrollbar-track: #e3e6e9;
}

/*? ═══════════════════════════════════════════════════════════════════════════
   2. TOKENS — tema oscuro
   Cada valor sigue leyendo el rol --ui-* (que style.css ya remapea bajo
   [data-theme="dark"]); el fallback cubre el uso autocontenido.
════════════════════════════════════════════════════════════════════════════ */
[data-theme="dark"] {
    --upaep-trinity-surface: var(--ui-surface, #1b1b1e);
    --upaep-trinity-surface-2: var(--ui-surface-2, #232327);
    --upaep-trinity-surface-3: var(--ui-surface-3, #2c2c31);
    --upaep-trinity-page-bg: #141417;
    --upaep-trinity-text: var(--ui-text, #ececf0);
    --upaep-trinity-text-strong: var(--ui-text-strong, #ffffff);
    --upaep-trinity-text-muted: var(--ui-text-muted, #9a9aa2);
    --upaep-trinity-divider: var(--ui-divider, #2f2f34);
    --upaep-trinity-hover-bg: var(--ui-surface-2, #232327);
    --upaep-trinity-active-bg: var(--ui-surface-hover, #2c2c31);
    --upaep-trinity-focus-ring: var(--ui-focus-ring, rgba(255, 255, 255, .22));
    --upaep-trinity-shadow: 0 8px 24px rgba(0, 0, 0, .55);
    --upaep-trinity-scrollbar-thumb: #63636f;
    --upaep-trinity-scrollbar-track: #2b2b31;

    --upaep-trinity-icon-invert: 0;
    --upaep-trinity-menu-icon-invert: 0;
}

@media (prefers-color-scheme: dark) {
    [data-theme="auto"] {
        --upaep-trinity-surface: var(--ui-surface, #1b1b1e);
        --upaep-trinity-surface-2: var(--ui-surface-2, #232327);
        --upaep-trinity-surface-3: var(--ui-surface-3, #2c2c31);
        --upaep-trinity-page-bg: #141417;
        --upaep-trinity-text: var(--ui-text, #ececf0);
        --upaep-trinity-text-strong: var(--ui-text-strong, #ffffff);
        --upaep-trinity-text-muted: var(--ui-text-muted, #9a9aa2);
        --upaep-trinity-divider: var(--ui-divider, #2f2f34);
        --upaep-trinity-hover-bg: var(--ui-surface-2, #232327);
        --upaep-trinity-active-bg: var(--ui-surface-hover, #2c2c31);
        --upaep-trinity-focus-ring: var(--ui-focus-ring, rgba(255, 255, 255, .22));
        --upaep-trinity-shadow: 0 8px 24px rgba(0, 0, 0, .55);
        --upaep-trinity-scrollbar-thumb: #52525c;
        --upaep-trinity-scrollbar-track: #232327;

        --upaep-trinity-icon-invert: 0;
        --upaep-trinity-menu-icon-invert: 0;
    }
}

/*? ═══════════════════════════════════════════════════════════════════════════
   2B. TOKENS — piel «trinity»
   Recupera la identidad del portal original: HEADER rojo, SIDEBAR gris,
   FOOTER casi negro.

       <html data-shell="trinity">                    → contenido en claro
       <html data-shell="trinity" data-theme="dark">  → contenido en oscuro
       <html data-theme="trinity">                    → atajo del primero

   Es una PIEL, no un tema: no toca la paleta de la raíz, así que sirve igual
   con el contenido claro (modo «trinity») que oscuro (modo «trinity-dark»). Los
   tres colores de marca son los mismos en ambos; lo que cambia es el `main` y
   la capa de vistas, que los resuelve el core con `data-theme`.

   Cada región del cascarón redefine los tokens en SU PROPIO CONTENEDOR
   (`.container__Header` / `.container__Sidebar` / `.container__Footer`), que es
   el mismo patrón de la variante `.container__Header.__brand`. Los tokens se
   declaran en el contenedor y no en el `<header>`/`.sidebar`/`<footer>` para
   que los hereden también los hijos (popovers, iconos, submenús) sin pelear
   especificidad con las reglas anidadas de las secciones 4–7; y por eso mismo
   las declaraciones ganan a las de `[data-theme="dark"]` de la sección 2, que
   viven en la raíz.

   En las tres regiones `--upaep-trinity-accent` pasa a BLANCO: sobre rojo o
   sobre gris el rojo de marca no da contraste ni para texto ni para anillos de
   foco. El rojo se conserva donde es un GRAFISMO y no texto — la barra del item
   activo (`--upaep-trinity-item-active-bar`) y el logo (`--upaep-trinity-brandmark`).
════════════════════════════════════════════════════════════════════════════ */
:root {
    /*? Colores de la variante. Se exponen en la raíz para poder repintarla
       desde una hoja de marca sin reescribir los bloques de abajo. */
    --upaep-trinity-brand-header-bg: #E30921;
    /*? #52565A y no #4D4D4D: es exactamente `--upaep-color-dark-grey`, el gris
       de texto del design system, así que el sidebar deja de ser un gris suelto
       y pasa a ser un color del sistema. Sube 1.2:1 sobre el negro del item
       activo (#242424), que es lo que hace visible el item seleccionado. */
    --upaep-trinity-brand-sidebar-bg: #52565A;
    /*? El legacy usaba #2E2E2E; se baja a #242424 para que la barra roja del
       item activo pase de 2.80:1 a 3.12:1 contra su propio fondo (el mínimo de
       3:1 que pide WCAG para un grafismo con significado). A ojo es el mismo
       gris; el escalón contra el sidebar también mejora (1.61 → 1.79). */
    --upaep-trinity-brand-sidebar-active-bg: #242424;
    --upaep-trinity-brand-footer-bg: #1b1f22;
    --upaep-trinity-brand-panel-bg: #595959;
    --upaep-trinity-brand-bar: #E30921;
}

/*? ── Las barras superiores (carga y scroll) en la piel ─────────────────────
   Las dos barras viven en `<body>` y se pintan con el acento, que en la raíz es
   el rojo de marca. Debajo de un header ROJO eso las hace desaparecer: un rojo
   sobre otro rojo se lee como el filo de la barra, no como progreso.

   No se arregla con otro rojo —el problema es rojo sobre rojo—, así que en esta
   piel la barra pasa a BLANCO y se sube 3px para apoyarse en el borde inferior
   del header: sobre `#E30921` el blanco da 4.85:1, que es el máximo posible ahí,
   y se lee como una línea DENTRO de la barra roja. En claro y en oscuro sin piel
   se queda como estaba: el acento sobre el fondo de página.
   */
:is([data-theme="trinity"], [data-shell="trinity"]) {
    --upaep-trinity-progress-color: #ffffff;
    --upaep-trinity-progress-track: rgb(0 0 0 / .22);
    --upaep-trinity-progress-top: calc(var(--upaep-trinity-header-height, 56px) - 3px);
}

/*? ── HEADER rojo ──────────────────────────────────────────────────────────
   OJO CON EL TEXTO: `#E30921` es un color de luminancia MEDIA (L = .166), así
   que el contraste MÁXIMO que se puede conseguir encima es 4.85:1 — con blanco
   puro. Cualquier blanco translúcido se cae del 4.5:1 de AA (medido: .96 → 4.45,
   .92 → 4.23, .82 → 3.53) y por abajo tampoco hay salida (el negro da 3.32:1).
   Por eso en esta barra TODO el texto va blanco opaco y la jerarquía la dan el
   tamaño y el peso, que ya están en la sección 4. El translúcido se reserva para
   lo que no es texto: divisores, hovers y rellenos. */
:is([data-theme="trinity"], [data-shell="trinity"]) .container__Header {
    --upaep-trinity-surface: var(--upaep-trinity-brand-header-bg);
    /*? Dentro del header solo lo usa el disco del avatar, y tiene que ir a
       OSCURO: aclarando el rojo (un blanco al .16) sus iniciales se quedan en
       4.26:1 aun siendo blancas; oscureciéndolo suben a 6.70:1. */
    --upaep-trinity-surface-2: rgb(0 0 0 / .18);
    --upaep-trinity-surface-3: rgb(0 0 0 / .26);
    --upaep-trinity-text: #ffffff;
    --upaep-trinity-text-strong: #ffffff;
    --upaep-trinity-text-muted: #ffffff;
    --upaep-trinity-divider: rgb(255 255 255 / .24);
    --upaep-trinity-hover-bg: rgb(255 255 255 / .16);
    --upaep-trinity-active-bg: rgb(255 255 255 / .24);
    --upaep-trinity-accent: #ffffff;
    --upaep-trinity-focus-ring: rgb(255 255 255 / .32);
    /*? grafismos de marca dentro de la barra (y el relleno de `.__brand`, que
       si leyera el acento pintaría el header en blanco) */
    --upaep-trinity-brandmark: var(--upaep-trinity-brand-header-bg);
    /*? los <img> base64 del markup legacy ya vienen con relleno blanco */
    --upaep-trinity-icon-invert: 0;
}

/*? El hairline inferior sobra: el salto de color ya separa la barra del contenido.
   Misma profundidad que la regla de la sección 4 + el atributo → gana. */
:is([data-theme="trinity"], [data-shell="trinity"]) .container__Header header,
:is([data-theme="trinity"], [data-shell="trinity"]) .container__Header header.upaep__container__comp_dft {
    border-bottom-color: transparent;
}

/*? ── Popovers del header ──────────────────────────────────────────────────
   Cuelgan DENTRO del <header> (markup legacy), así que heredarían el rojo.
   Se les devuelve el panel gris del portal original (#595959) con el círculo
   del icono en blanco, como en trinity-shell-legacy.css. */
:is([data-theme="trinity"], [data-shell="trinity"]) #modal-overlay-platforms,
:is([data-theme="trinity"], [data-shell="trinity"]) #modal-overlay-img_user {
    --upaep-trinity-surface: var(--upaep-trinity-brand-panel-bg);
    /*? surface-2 aquí solo lo usa `.icon-platform` (el círculo del logo) */
    --upaep-trinity-surface-2: #ffffff;
    --upaep-trinity-text: rgb(255 255 255 / .92);
    --upaep-trinity-text-strong: #ffffff;
    --upaep-trinity-text-muted: rgb(255 255 255 / .72);
    --upaep-trinity-divider: rgb(255 255 255 / .16);
    --upaep-trinity-hover-bg: rgb(255 255 255 / .14);
    --upaep-trinity-shadow: 0 12px 32px rgb(0 0 0 / .45);
    --upaep-trinity-scrollbar-thumb: rgb(255 255 255 / .28);
    --upaep-trinity-scrollbar-track: rgb(255 255 255 / .08);
}

/*? ── SIDEBAR gris ────────────────────────────────────────────────────────── */
:is([data-theme="trinity"], [data-shell="trinity"]) .container__Sidebar {
    --upaep-trinity-surface: var(--upaep-trinity-brand-sidebar-bg);
    --upaep-trinity-surface-2: rgb(255 255 255 / .10);
    --upaep-trinity-surface-3: rgb(255 255 255 / .16);
    --upaep-trinity-text: rgb(255 255 255 / .88);
    --upaep-trinity-text-strong: #ffffff;
    --upaep-trinity-text-muted: rgb(255 255 255 / .66);
    --upaep-trinity-divider: rgb(255 255 255 / .16);
    --upaep-trinity-hover-bg: rgb(255 255 255 / .10);
    --upaep-trinity-active-bg: var(--upaep-trinity-brand-sidebar-active-bg);
    --upaep-trinity-accent: #ffffff;
    --upaep-trinity-focus-ring: rgb(255 255 255 / .32);
    /*? el rojo se queda donde es grafismo y no texto: la barra del item activo
       y el logo de marca */
    --upaep-trinity-item-active-bar: var(--upaep-trinity-brand-bar);
    --upaep-trinity-item-active-text: #ffffff;
    --upaep-trinity-brandmark: var(--upaep-trinity-brand-bar);
    --upaep-trinity-shadow: 0 12px 32px rgb(0 0 0 / .45);
    --upaep-trinity-scrollbar-thumb: rgb(255 255 255 / .24);
    --upaep-trinity-scrollbar-track: rgb(255 255 255 / .07);
    --upaep-trinity-icon-invert: 0;
    --upaep-trinity-menu-icon-invert: 0;
}

/*? Canto derecho: con el contenido en claro, una línea blanca traslúcida se lee
   como un brillo; el borde va en oscuro para dar profundidad (y en oscuro queda
   como costura entre el gris del sidebar y el fondo, más oscuro que él). */
:is([data-theme="trinity"], [data-shell="trinity"]) .container__Sidebar .sidebar {
    border-right-color: rgb(0 0 0 / .28);
}

/*? ── FOOTER casi negro ───────────────────────────────────────────────────── */
:is([data-theme="trinity"], [data-shell="trinity"]) .container__Footer {
    --upaep-trinity-surface: var(--upaep-trinity-brand-footer-bg);
    --upaep-trinity-surface-2: rgb(255 255 255 / .10);
    --upaep-trinity-text: rgb(255 255 255 / .82);
    --upaep-trinity-text-strong: #ffffff;
    --upaep-trinity-text-muted: rgb(255 255 255 / .72);
    --upaep-trinity-divider: rgb(255 255 255 / .12);
    --upaep-trinity-hover-bg: rgb(255 255 255 / .10);
    --upaep-trinity-accent: #ffffff;
    --upaep-trinity-focus-ring: rgb(255 255 255 / .32);
    --upaep-trinity-icon-invert: 0;
}

/*? ── Ajustes de «trinity-dark» ────────────────────────────────────────────
   Con el contenido en CLARO las tres bandas se separan solas (medido contra la
   página: sidebar 7.75:1, pie 15.22:1). Con el contenido en OSCURO eso se cae
   —el pie casi negro contra una página casi negra da 1.11:1— y la identidad se
   disuelve en una sola masa oscura.

   Y NO se arregla subiendo el color: para llegar al 3:1 contra `#141417` el pie
   tendría que ser ~`#6B6B6B`, que ya no es «pie casi negro». En la zona de los
   negros el ratio de luminancia deja de ser un buen modelo (dos casi-negros
   distintos siempre dan ~1.1) y el límite se ve por el FILO. Así que aquí:
     · el hairline del pie sube a 3:1 contra sus dos lados → el borde se lee;
     · el pie sube un escalón perceptible aunque su ratio siga siendo bajo;
     · el canto del sidebar pasa de oscuro a claro: en oscuro el sidebar es más
       CLARO que la página, y un filo negro difuminaba justo el límite que
       quedaba (2.18:1) en vez de marcarlo.
   Solo aplica a trinity + oscuro, así que se enchufa por los dos atributos. */
[data-theme="dark"][data-shell="trinity"] .container__Footer {
    --upaep-trinity-brand-footer-bg: #23272b;
    --upaep-trinity-divider: rgb(255 255 255 / .36);
}

[data-theme="dark"][data-shell="trinity"] .container__Sidebar .sidebar {
    border-right-color: rgb(255 255 255 / .18);
}

/*? ── Flyout del riel ──────────────────────────────────────────────────────
   Vive en el <body>, fuera del sidebar (lo recortaría el overflow de
   #elementosMenu, ver sección 5) → no hereda nada: se le repite la paleta gris
   para que se lea como parte del menú. El tooltip no hace falta: ya usa los
   roles `--ui-*-inverse`, oscuros en todos los modos. */
:is([data-theme="trinity"], [data-shell="trinity"]) .upaep__trinity-flyout {
    --upaep-trinity-surface: var(--upaep-trinity-brand-sidebar-bg);
    --upaep-trinity-text: rgb(255 255 255 / .88);
    --upaep-trinity-text-strong: #ffffff;
    --upaep-trinity-text-muted: rgb(255 255 255 / .66);
    --upaep-trinity-divider: rgb(255 255 255 / .16);
    --upaep-trinity-hover-bg: rgb(255 255 255 / .10);
    --upaep-trinity-accent: #ffffff;
    --upaep-trinity-shadow: 0 12px 32px rgb(0 0 0 / .45);
    --upaep-trinity-scrollbar-thumb: rgb(255 255 255 / .24);
    --upaep-trinity-scrollbar-track: rgb(255 255 255 / .07);
}

/*? ═══════════════════════════════════════════════════════════════════════════
   3. BASE DEL DOCUMENTO
════════════════════════════════════════════════════════════════════════════ */
body {
    margin: 0;
    padding: 0;
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
    font-family: var(--upaep-trinity-font);
    color: var(--upaep-trinity-text);
    background: var(--upaep-trinity-page-bg);
    -webkit-font-smoothing: antialiased;
}

/*? utilidad compartida del shell (el monolito la define igual) */
.none {
    display: none !important;
}

/*? Anillo de foco homogéneo en todo el cascarón */
.container__Header :focus-visible,
.container__Sidebar :focus-visible,
.container__Footer :focus-visible {
    outline: var(--ui-focus-ring-width, 2px) solid var(--ui-focus-ring-color, currentColor);
    outline-offset: var(--ui-focus-ring-offset, 2px);
    border-radius: var(--upaep-trinity-radius-sm, 6px);
}

/*? Botón/icono fantasma — base de todos los controles del shell.
   Sirve igual con <img> (markup legacy) o con <button> + SVG inline (markup limpio). */
.container__Header .icons-contenedores,
.container__Header #icon-logout,
.container__Header .upaep__logo_comp_dft #hamb-menu,
.container__Header .upaep__logo_comp_dft .hamb-menu,
.container__Sidebar .cont-logo #hamb-menu,
.container__Sidebar .cont-logo .hamb-menu {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 2.125rem;
    height: 2.125rem;
    padding: 0;
    border: 0;
    border-radius: var(--upaep-trinity-radius-md, 8px);
    background: transparent;
    color: var(--upaep-trinity-text-muted);
    cursor: pointer;
    transition: background .15s ease, color .15s ease;
    /*? contenedor de posicionamiento: de él cuelgan los badges flotantes
       (`.upaep__badge.__floating`) anclados con --upaep-badge-inset */
    position: relative;

    &:hover {
        background: var(--upaep-trinity-hover-bg);
        color: var(--upaep-trinity-text-strong);
    }
}

/*? Mismo tamaño para un glifo de Sigil dentro de los controles del shell.
   El header canónico usa `.upaep__sigil.__ico-logout` en #icon-logout, y un sigil
   no se dimensiona con width/height sino con su propia variable. El color no se
   toca: el sigil pinta con `currentColor`, así que hereda el `color` del botón
   —muted, y strong en :hover— y sigue al tema claro/oscuro sin regla aparte. */
.container__Header .icons-contenedores>.upaep__sigil,
.container__Header #icon-logout>.upaep__sigil,
.container__Header .upaep__logo_comp_dft>*>.upaep__sigil,
.container__Sidebar .cont-logo .hamb-menu>.upaep__sigil {
    --upaep-sigil-size: 1.2rem;
}

/*? Tamaño uniforme de los SVG inline dentro de los controles del shell */
.container__Header .icons-contenedores>svg,
.container__Header #icon-logout>svg,
.container__Header .upaep__logo_comp_dft>*>svg,
.container__Header .upaep__logo_comp_dft .hamb-menu>svg,
.container__Sidebar .cont-logo .hamb-menu>svg,
.container__Sidebar .cont-logo #hamb-menu>svg {
    width: 1.2rem;
    height: 1.2rem;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/*? ═══════════════════════════════════════════════════════════════════════════
   4. HEADER — barra limpia: superficie + hairline inferior, sin relleno de marca
════════════════════════════════════════════════════════════════════════════ */
.container__Header {
    position: fixed;
    inset: 0 0 auto 0;
    width: 100%;
    z-index: var(--upaep-trinity-zindex-header);
}

/*? El monolito estiliza los hijos del header ANIDADOS dentro de
   `header.upaep__container__comp_dft` → (0,3,x). Aquí se replica esa misma
   profundidad con una lista de selectores + nesting: `&` compila a
   `:is(.container__Header header, .container__Header header.upaep__container__comp_dft)`,
   que empata en especificidad y gana por orden de carga. */
.container__Header header,
.container__Header header.upaep__container__comp_dft {
    box-sizing: border-box;
    display: flex;
    align-items: center;
    gap: var(--upaep-trinity-sp-3, .75rem);
    float: none;
    width: auto;
    height: var(--upaep-trinity-header-height);
    /*? el contenido nunca queda debajo del sidebar fijo */
    padding: 0 var(--upaep-trinity-gutter) 0 calc(var(--upaep-trinity-offset) + .25rem);
    text-align: left;
    color: var(--upaep-trinity-text);
    background: var(--upaep-trinity-surface);
    border-bottom: 1px solid var(--upaep-trinity-divider);
    box-shadow: none;
    transition: padding-left var(--upaep-trinity-transition), background .2s ease, border-color .2s ease;

    /*? ── Hamburguesa del header ──────────────────────────────────────────
       Con sidebar visible es redundante (el sidebar trae la suya) → oculta.
       Se muestra sin sidebar, en modo `notSidebar`, o en móvil (ver responsive). */
    & .upaep__logo_comp_dft {
        display: none;
        justify-content: center;
        align-items: center;
        width: fit-content;

        body:not(:has(.container__Sidebar .sidebar)) &,
        body:has(.sidebar.notSidebar) & {
            display: flex;
        }

        /*? el filtro solo aplica al <img> base64 legacy (relleno blanco); un SVG
           inline ya se pinta con currentColor y no debe invertirse */
        & img#hamb-menu,
        & img.hamb-menu {
            width: 2.125rem;
            height: 2.125rem;
            padding: var(--upaep-trinity-sp-1, .25rem);
            filter: invert(var(--upaep-trinity-icon-invert));
        }
    }

    /*? ── Zona central: slot para breadcrumb o título de vista ── */
    /*? Rótulo de la sección. Va en NEGRITA (700, no más) y con el color de
       texto fuerte: en tenue y con peso normal, sobre una barra de color, no se
       leía — y es el único sitio que dice dónde estás. */
    & .upaep__center__comp_dft {
        display: flex;
        flex: 1;
        align-items: center;
        gap: var(--upaep-trinity-sp-3, .75rem);
        min-width: 0;
        width: auto;
        padding: 0;
        justify-content: flex-start;
        font-size: var(--upaep-fz14, .875rem);
        font-weight: var(--upaep-fw-bold, 700);
        color: var(--upaep-trinity-text-strong);

        /*? U-27 · LA BARRA MIDE 56px Y NO NEGOCIA.
           El rotulo no tenia nada que le impidiera envolverse: con un titulo real
           —"Gestion de solicitudes de vale de caja del periodo academico 2026-2"—
           a 320px se partia en TRES lineas de 17px dentro de una barra de 56, y la
           tercera quedaba cortada por la mitad. No desbordaba el documento, asi
           que ninguna prueba de overflow lo veia: simplemente se leia mal.

           Un rotulo de barra se recorta, no se envuelve. Con `title` en el marcado
           el texto completo sigue disponible al apuntar, y el contenido no se
           pierde: la vista siempre lleva su propio <h1>. */
        & > * {
            min-width: 0;
            overflow: hidden;
            white-space: nowrap;
            text-overflow: ellipsis;
        }
    }

    /*? ── Identidad del usuario ── */
    & .upaep__name__comp_dft {
        display: flex;
        align-items: center;
        gap: var(--upaep-trinity-sp-2, .5rem);
        width: fit-content;
        height: auto;
        margin: 0;
        justify-content: flex-end;

        & #upaep__name__comp__user {
            width: max-content;
            max-width: 22ch;
            margin: 0;
            padding: 0;
            overflow: hidden;
            font-size: var(--upaep-trinity-fz-meta, .75rem);
            font-weight: var(--upaep-fw-semibold, 600);
            line-height: 1.2;
            color: var(--upaep-trinity-text-strong);
            text-overflow: ellipsis;
            text-transform: capitalize;
            white-space: nowrap;
        }
    }

    /*? ── Fecha / hora ── */
    & .upaep__date__comp_dft {
        display: flex;
        align-items: center;
        padding-left: var(--upaep-trinity-sp-1, .25rem);
        border-left: 1px solid var(--upaep-trinity-divider);
    }

    /*? ── Acciones (plataformas, tema, logout) ── */
    & .upaep__icons__comp_dft {
        display: flex;
        align-items: center;
        justify-content: flex-end;
        gap: var(--upaep-trinity-sp-1, .25rem);
        width: fit-content;
        padding: 0;

        /*? El monolito lo pinta como cuadro blanco desde una regla anidada (1,4,0),
           así que el hover fantasma tiene que declararse a esta misma profundidad
           o el `background` del monolito le gana. */
        & #icon-logout {
            width: 2.125rem;
            height: 2.125rem;
            background: transparent;
            border-radius: var(--upaep-trinity-radius-md, 8px);
            color: var(--upaep-trinity-text-muted);
            transition: background .15s ease, color .15s ease;

            &:hover {
                background: var(--upaep-trinity-hover-bg);
                color: var(--upaep-trinity-text-strong);
            }

            & #icon_log {
                width: 1.15rem;
                height: 1.15rem;
            }
        }
    }
}

#upaep__icon_name__comp__user {
    display: grid;
    place-items: center;
    width: 2rem;
    height: 2rem;
    background: var(--upaep-trinity-surface-2);
    border-radius: 50%;
    box-shadow: inset 0 0 0 1px var(--upaep-trinity-divider);
    cursor: pointer;
    overflow: hidden;

    & #user_image_upaep {
        width: 100%;
        height: 100%;
        border-radius: 50%;
        object-fit: cover;
        object-position: center;
    }
}

/*? ── Fecha / hora: dato secundario, tenue y compacto ── */
#upaep__date__comp__user {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    justify-content: center;
    max-width: 105px;
    font-size: var(--upaep-trinity-fz-label, .75rem);
    font-weight: var(--upaep-fw-regular, 400);
    line-height: 1.35;
    color: var(--upaep-trinity-text-muted);
    white-space: nowrap;

    & .__hour {
        font-weight: var(--upaep-fw-semibold, 600);
        color: var(--upaep-trinity-text);
    }
}

#cont-icons {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--upaep-trinity-sp-1, .25rem);
}

.container__Header .icons-url {
    width: 1.125rem;
    height: 1.125rem;
    filter: invert(var(--upaep-trinity-icon-invert));
}

.container__Header #icon-logout #icon_log {
    width: 1.15rem;
    height: 1.15rem;
    /*? el SVG legacy usa fill:currentColor → dentro de <img> resuelve a negro */
    filter: invert(calc(1 - var(--upaep-trinity-icon-invert)));
}

/*? escape puntual: iconos que ya vienen en color y no deben invertirse */
.container__Header .__no-invert,
.container__Header .__no-invert img {
    filter: none;
}

/*? ── Variante opcional: header con relleno de marca (look clásico) ── */
.container__Header.__brand header,
.container__Header.__brand header.upaep__container__comp_dft {
    /*? El relleno sale del grafismo de marca, no del acento: en la variante
       trinity el acento del header ya es BLANCO (sección 2B) y esta clase
       pintaría la barra en blanco. Sin brandmark declarado → acento, como antes. */
    --upaep-trinity-surface: var(--upaep-trinity-brandmark, var(--upaep-trinity-accent));
    --upaep-trinity-text: var(--upaep-trinity-on-accent);
    --upaep-trinity-text-strong: var(--upaep-trinity-on-accent);
    --upaep-trinity-text-muted: rgb(255 255 255 / .78);
    --upaep-trinity-divider: rgb(255 255 255 / .18);
    --upaep-trinity-hover-bg: rgb(255 255 255 / .14);
    --upaep-trinity-icon-invert: 0;
}

/*? ═══════════════════════════════════════════════════════════════════════════
   5. SIDEBAR — superficie clara, hairline derecho, items redondeados
════════════════════════════════════════════════════════════════════════════ */
.container__Sidebar .sidebar {
    box-sizing: border-box;
    position: fixed;
    inset: 0 auto 0 0;
    z-index: var(--upaep-trinity-zindex-sidebar);
    display: flex;
    flex-direction: column;
    width: var(--upaep-trinity-sidebar-width);
    height: 100dvh;
    min-height: 0;
    padding: 0;
    text-align: left;
    color: var(--upaep-trinity-text);
    background: var(--upaep-trinity-surface);
    border-right: 1px solid var(--upaep-trinity-divider);
    box-shadow: none;
    overflow: hidden;
    transition: width var(--upaep-trinity-transition), transform var(--upaep-trinity-transition), background .2s ease;
}

/*? ── Cabecera de marca ── */
.container__Sidebar .sidebar .cont-logo {
    display: flex;
    flex-shrink: 0;
    align-items: center;
    justify-content: space-between;
    gap: var(--upaep-trinity-sp-2, .5rem);
    height: var(--upaep-trinity-header-height);
    padding: 0 var(--upaep-trinity-sp-3, .75rem);
    border-bottom: 1px solid var(--upaep-trinity-divider);

    & .cont-logo-upaep {
        display: flex;
        align-items: center;
        justify-content: flex-start;
        min-width: 0;
        height: 100%;
        overflow: hidden;

        /*? si el logo va envuelto en un enlace, no debe tomar el color de link */
        &>a {
            display: inline-flex;
            align-items: center;
            min-width: 0;
            color: inherit;
            text-decoration: none;
        }

        & #logo-sidebar {
            height: 1.75rem;
            max-width: 100%;
            padding: 0;
            border-radius: 0;
            object-fit: contain;
            cursor: pointer;
        }
    }

    & img#hamb-menu,
    & img.hamb-menu {
        width: 2.125rem;
        height: 2.125rem;
        padding: var(--upaep-trinity-sp-1, .25rem);
        transition: none;
        filter: invert(var(--upaep-trinity-icon-invert));
    }
}

/*? ── Selector de institución ─────────────────────────────────────────────
   La sección se separa del menú con un hairline. Si el selector está oculto
   (una sola institución sin lista, o ninguna) la sección entera desaparece
   junto con su separador. */
.container__Sidebar .upaep__section__up__menu__sidebar {
    flex-shrink: 0;
    padding: var(--upaep-trinity-sp-3, .75rem);
    border-bottom: 1px solid var(--upaep-trinity-divider);

    &:empty,
    &:not(:has(.choose-company:not([hidden]))) {
        display: none;
    }
}

.choose-company {
    margin-top: 0;
    padding: 0;
    border: 0;
    border-radius: var(--upaep-trinity-radius-md, 8px);
    background: var(--upaep-trinity-surface-2);
    cursor: pointer;
    overflow: hidden;

    &[hidden] {
        display: none;
    }

    /*? Una sola institución: es un rótulo, no un desplegable */
    &.__single {
        cursor: default;

        & .arrow,
        & .company-list {
            display: none;
        }
    }

    & .current-company {
        display: flex;
        align-items: center;
        gap: var(--upaep-trinity-sp-2, .5rem);
        padding: var(--upaep-trinity-sp-2, .5rem) var(--upaep-trinity-sp-3, .75rem);

        & img {
            flex-shrink: 0;
            width: 1.4rem;
            height: 1.4rem;
            object-fit: contain;
        }
    }

    & h2 {
        display: flex;
        flex: 1;
        align-items: center;
        gap: var(--upaep-trinity-sp-1, .25rem);
        min-width: 0;
        margin: 0;
        font-size: var(--upaep-fz12, .75rem);
        font-weight: var(--upaep-fw-semibold, 600);
        line-height: 1.3;
        letter-spacing: .02em;
        color: var(--upaep-trinity-text-strong);
        text-transform: uppercase;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        user-select: none;
    }

    & .current-company .arrow {
        display: inline-block;
        flex-shrink: 0;
        margin-left: auto;
        font-size: .7rem;
        color: var(--upaep-trinity-text-muted);
        transition: transform var(--upaep-trinity-transition);
    }

    & .arrow.rotated {
        transform: rotate(180deg);
    }

    /*? acordeón por max-height (lo alterna el JS legacy con `.expanded`) */
    & .company-list {
        max-height: 0;
        overflow: hidden;
        transition: max-height var(--upaep-trinity-transition);

        & ul {
            display: flex;
            flex-direction: column;
            gap: 0;
            margin: 0;
            padding: var(--upaep-trinity-sp-1, .25rem);
            list-style: none;
            text-align: left;
            border-top: 1px solid var(--upaep-trinity-divider);

            & li {
                display: flex;
                align-items: center;
                gap: var(--upaep-trinity-sp-2, .5rem);
                width: 100%;
                padding: var(--upaep-trinity-sp-2, .5rem) var(--upaep-trinity-sp-2, .5rem);
                border-radius: var(--upaep-trinity-radius-sm, 6px);
                opacity: 1;
                color: var(--upaep-trinity-text-muted);
                transition: background .15s ease, color .15s ease;

                & img {
                    flex-shrink: 0;
                    width: 1.25rem;
                    height: 1.25rem;
                    object-fit: contain;
                }

                & h2 {
                    font-size: var(--upaep-trinity-fz-label, .75rem);
                    font-weight: var(--upaep-fw-regular, 400);
                    color: inherit;
                }

                &:hover {
                    background: var(--upaep-trinity-surface);
                    color: var(--upaep-trinity-text-strong);
                }

                /*? institución activa dentro de la lista */
                &.is-current,
                &[aria-selected="true"] {
                    /*? U-08 · mismo criterio que el item activo del menu. */
                    color: var(--upaep-trinity-item-active-text, var(--ui-text-strong, var(--upaep-trinity-text-strong)));
                    font-weight: var(--upaep-fw-semibold, 600);
                }
            }
        }
    }

    /*? El máximo se calcula en JS (--upaep-trinity-company-list-h) según cuántas
       instituciones haya; el valor de aquí es solo el respaldo. */
    & .company-list.expanded {
        max-height: var(--upaep-trinity-company-list-h, 220px);
        overflow-y: auto;
        scrollbar-width: var(--upaep-trinity-scrollbar-width, thin);
        scrollbar-color: var(--upaep-trinity-scrollbar-thumb) var(--upaep-trinity-scrollbar-track);
    }
}

/*? Envoltorio semántico opcional: <nav> alrededor de #elementosMenu (markup limpio).
   Se hace transparente al layout para que la lista siga siendo el área que scrollea. */
.container__Sidebar .sidebar>nav {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    min-height: 0;
}

/*? ── Lista de navegación ────────────────────────────────────────────────
   Se replica `!important` porque el monolito lo usa en este mismo selector. */
.container__Sidebar .sidebar ul#elementosMenu,
#elementosMenu {
    flex: 1 1 auto !important;
    min-height: 0 !important;
    width: 100% !important;
    margin: var(--upaep-trinity-sp-2, .5rem) 0 !important;
    padding: 0 var(--upaep-trinity-sp-2, .5rem) !important;
    display: flex !important;
    flex-direction: column !important;
    justify-content: flex-start !important;
    gap: 1px !important;
    list-style: none !important;
    font-size: var(--upaep-fz14, .875rem) !important;
    font-weight: var(--upaep-fw-regular, 400) !important;
    color: var(--upaep-trinity-text) !important;
    text-decoration: none !important;
    overflow-x: hidden !important;
    overflow-y: auto !important;
    /*? barra visible y con carril: se reserva el hueco para que el menú no salte
       al aparecer/desaparecer el scroll */
    scrollbar-width: var(--upaep-trinity-scrollbar-width, thin);
    scrollbar-color: var(--upaep-trinity-scrollbar-thumb) var(--upaep-trinity-scrollbar-track);
    scrollbar-gutter: stable;
}

/*? ── Item de primer nivel ── */
#elementosMenu .item {
    position: relative;
    display: flex;
    /*? CRÍTICO: la lista es un contenedor flex en columna; sin esto los items se
       ENCOGEN cuando el contenido supera el alto disponible (con los submenús
       abiertos) y su `overflow: hidden` recorta el submenú a media línea.
       El scroll lo hace #elementosMenu, no el aplastado de los items. */
    flex: 0 0 auto;
    align-items: center;
    gap: var(--upaep-trinity-sp-3, .75rem);
    min-height: 2.4rem;
    /*? Más aire dentro del item: con `.3rem .55rem` el rótulo y el icono
       quedaban pegados al filo y la lista se leía apretada. */
    padding: var(--upaep-trinity-sp-2, .5rem) var(--upaep-trinity-sp-3, .75rem);
    border: 0;
    /*? Radio MENOR que el de las piezas de contenido (8→6): un item de menú es
       una fila de una lista, no una tarjeta; con 8px competía con los paneles. */
    border-radius: var(--upaep-trinity-radius-sm, 6px);
    background: transparent;
    color: var(--upaep-trinity-text);
    text-align: start;
    cursor: pointer;
    overflow: hidden;
    transition: background .15s ease, color .15s ease;

    &:hover {
        background: var(--upaep-trinity-hover-bg);
        color: var(--upaep-trinity-text-strong);
    }
}

/*? Activo: fondo neutro tenue + barra de acento a la izquierda + texto de marca.
   Texto y barra pasan por tokens propios (sin valor por defecto: caen al acento)
   para poder separarlos donde el acento no contrasta — ver sección 2B. */
/*? U-08 · EL ESTADO ACTIVO NO SE COMUNICA SOLO CON COLOR DE MARCA. El texto del
   item activo caia en `--upaep-trinity-accent` —el rojo institucional— sobre el
   fondo activo: 3.73:1 en UPAEP claro, por debajo del minimo para texto. Y era la
   UNICA senal cromatica: si no se distingue el rojo, no se distingue el estado.

   El reparto correcto separa las dos cosas que estaban en el mismo token:
     · la BARRA de acento sigue siendo de marca — es un grafismo, no texto, y su
       trabajo es decir "aqui estas" de un vistazo;
     · el TEXTO pasa a un rol legible (`--ui-text-strong`, 9.62:1 sobre el fondo
       activo), reforzado por el peso, que ya estaba.
   La arquitectura de navegacion no se toca: el owner activo, `aria-current` y la
   persistencia del estado siguen exactamente igual. */
#elementosMenu .item.active,
.item.active {
    border-left: 0;
    background: var(--upaep-trinity-active-bg);
    color: var(--upaep-trinity-item-active-text, var(--ui-text-strong, var(--upaep-trinity-text-strong)));
    font-weight: var(--upaep-fw-semibold, 600);
    box-shadow: inset 3px 0 0 var(--upaep-trinity-item-active-bar, var(--upaep-trinity-accent));
}

/*? El ENLACE es el objetivo accionable, no la fila que lo contiene: la fila
   aporta el relleno visual, pero quien recibe el clic y el foco es el `<a>`. Con
   `line-height: 1.35` su caja medía 19 px de alto y no llegaba al mínimo de
   2.5.8 Target Size (24x24) — GAP-A11Y-001. La caja se sube con la ALTURA DE
   LÍNEA, no con relleno ni con flex:
     · `line-height` centra el texto dentro de su propia línea sin desplazar
       nada; con relleno habría que compensarlo arriba y abajo.
     · `display: block` se conserva porque `text-overflow: ellipsis` no recorta
       el texto de un contenedor flex —el texto pasa a ser un item anónimo—, y
       el truncado del rótulo largo es contrato de esta pieza.
   La fila ya reserva 2.25rem menos .3rem de relleno = 26.4 px de caja interior,
   así que el item NO crece: solo deja de haber un objetivo de 19 px dentro. */
#elementosMenu li a,
#elementosMenu .itemMenu {
    flex: 1;
    min-width: 0;
    min-height: var(--upaep-trinity-target-min, 1.5rem);
    font-size: var(--upaep-fz14, .875rem);
    font-weight: inherit;
    line-height: var(--upaep-trinity-target-min, 1.5rem);
    color: inherit;
    text-decoration: none;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/*? ── EL RÓTULO SE ENVUELVE CUANDO HAY SITIO PARA ENVOLVERLO ───────────────
   La regla de arriba recorta con puntos suspensivos SIEMPRE, y eso solo tiene
   sentido en el riel, donde el rótulo ni siquiera se ve. Con la barra abierta
   producía «Visualización en Progra…» sobre 240px de ancho que estaban ahí sin
   usar: el nombre completo se perdía en una barra que tenía espacio de sobra.
   Un rótulo de navegación no es un dato de una celda; es lo que dice a dónde
   lleva ese item, y cortarlo por la mitad es cortar la información.

   Con la barra ABIERTA se envuelve, con un tope de dos líneas: la fila crece
   —`min-height` y `align-items: center`, así que el icono sigue centrado y el
   área de clic sigue siendo la fila entera— y un rótulo desmedido acaba
   truncando, pero en la línea 2, no a mitad de la primera.

   La ALTURA DE LÍNEA no se toca: sigue siendo el suelo de objetivo táctil de
   GAP-A11Y-001 (2.5.8), que una línea sola tiene que seguir cumpliendo. Y la
   especificidad sube por encima de la base sin `!important`: la regla base
   lleva el id `#elementosMenu`, así que aquí hace falta ese mismo id más las
   clases del estado.

   En el RIEL no cambia nada: `.sidebarClosed` saca el rótulo de la caja y el
   tooltip y el flyout siguen siendo quienes lo hacen legible. */
.sidebar.sidebarOpened #elementosMenu li a,
.sidebar.sidebarOpened #elementosMenu .itemMenu,
.sidebar.sidebarOpened .item-parent-container .itemMenu {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: var(--upaep-trinity-menu-lines, 2);
    line-clamp: var(--upaep-trinity-menu-lines, 2);
    /*? `anywhere` y no `break-word`: un rótulo puede traer una palabra técnica
       más ancha que la barra, y ahí lo correcto es partirla, no desbordar. */
    overflow-wrap: anywhere;
    white-space: normal;
}

/*? El rótulo de un GRUPO dispone de menos ancho que el de una hoja, y no por
   gusto: su fila reserva sitio para la flecha de despliegue. Medido con la
   barra a 240px — hoja 159px, grupo 140px —, así que el mismo rótulo que cabe
   en dos líneas como hoja necesita tres como grupo. El tope sube una línea por
   esa razón mecánica, no por preferencia: es la consecuencia de esos 19px. */
.sidebar.sidebarOpened .item-parent-container .itemMenu {
    --upaep-trinity-menu-lines: 3;
}

/*? RANURA DE ICONO del item, no «la imagen del item». La ranura mide siempre lo
   mismo y no le importa CÓMO esté dibujado el icono: `<img>` legacy, `<svg>` en
   línea o una pieza del inventario que se pinte con máscara o con fuente. Antes
   la geometría vivía en un selector `img`, así que meter la iconografía del kit
   en la navegación dejaba el icono a 1em (el tamaño del texto) y desalineado
   respecto a los `<img>` de al lado — el hueco quedaba a un tamaño y el icono a
   otro. `--upaep-trinity-icon-size` es la medida de la ranura, y con ella se
   remapean los tokens que publica la iconografía del kit: es la misma forma con
   la que `.upaep__trinity-btn` remapea `--upaep-btn-*`, composición y no parche. */
#elementosMenu li .imgMenu,
#elementosMenu li img,
.item-parent-container .imgMenu {
    --upaep-trinity-icon-size: 1.15rem;
    --upaep-sigil-size: var(--upaep-trinity-icon-size);
    flex-shrink: 0;
    width: var(--upaep-trinity-icon-size);
    height: var(--upaep-trinity-icon-size);
    padding: 0;
    object-fit: contain;
    opacity: .78;
    /*? Alineado con la línea del rótulo, no con la base del texto: sin esto un
       icono de máscara (`vertical-align: -.125em`) baja 2 px respecto a los
       `<img>` hermanos dentro de la misma fila. */
    vertical-align: middle;
}

/*? La inversión es SOLO para el mapa de bits legacy, que viene en blanco y sobre
   superficie clara desaparecería. Un icono que ya se pinta con `currentColor`
   —la iconografía vigente del kit— invertido saldría del color de la marca. */
#elementosMenu li img.imgMenu,
#elementosMenu li img {
    filter: invert(var(--upaep-trinity-menu-icon-invert));
}

/*? El icono del menú es decorativo (`aria-hidden`) y NO tiene interacción propia:
   dejarlo fuera del hit-testing hace que el pointer llegue siempre al item, en vez
   de quedarse en el `<svg>` o en su `<path>`. La navegación no depende de esto —la
   resuelve `closest()` en el runtime—, pero evita que el icono sea un objetivo
   distinto del item que lo contiene. */
#elementosMenu li>.imgMenu,
.item-parent-container>.imgMenu {
    pointer-events: none;
}

#elementosMenu .item.active .imgMenu {
    opacity: 1;
}

/*? escape: iconos de menú ya en color (no invertir) */
.sidebar.__menu-icons-color #elementosMenu img.imgMenu,
.sidebar.__menu-icons-color .item-parent-container img.imgMenu {
    filter: none;
}

/*? ── Item padre (grupo colapsable) ── */
.item-parent {
    position: relative;
    flex-direction: column;
    align-items: stretch !important;
    padding: 0 !important;
    gap: 0 !important;
    background: transparent;

    &:hover {
        background: transparent !important;
    }
}

.item-parent-container {
    display: flex;
    align-items: center;
    gap: var(--upaep-trinity-sp-3, .75rem) !important;
    min-height: 2.25rem;
    padding: var(--upaep-trinity-sp-1, .25rem) var(--upaep-trinity-sp-2, .5rem);
    border-radius: var(--upaep-trinity-radius-md, 8px);
    color: var(--upaep-trinity-text);
    text-align: start;
    cursor: pointer;
    overflow: hidden;
    transition: background .15s ease, color .15s ease;

    &:hover {
        background: var(--upaep-trinity-hover-bg);
        color: var(--upaep-trinity-text-strong);
    }
}

/*? Mismo criterio que en el item simple: la geometría de la ranura se declara
   arriba para cualquier icono y aquí sólo queda la inversión del mapa de bits. */
.item-parent-container img.imgMenu {
    filter: invert(var(--upaep-trinity-menu-icon-invert));
}


.item-parent-container .itemMenu {
    flex: 1;
    min-width: 0;
    /*? Mismo suelo de objetivo que el enlace de item simple (GAP-A11Y-001): el
       grupo también se abre desde su rótulo cuando el consumidor lo enlaza. */
    min-height: var(--upaep-trinity-target-min, 1.5rem);
    font-size: var(--upaep-fz14, .875rem);
    font-weight: var(--upaep-fw-regular, 400);
    line-height: var(--upaep-trinity-target-min, 1.5rem);
    color: inherit;
    text-decoration: none;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/*? ── CONTEXTO DEL GRUPO vs SELECCIÓN CONCRETA ─────────────────────────────
   Dos cosas distintas, y el sistema ya tenía lenguaje para las dos:

     · la BARRA de acento dice «esta es la página». La lleva la hoja: el item
       raíz (`inset 3px`) o el subitem (su guía de 2px). Es exclusiva.
     · la SUPERFICIE dice «la ruta activa está aquí dentro». Es del grupo.

   Antes el padre no se pintaba en absoluto con el sidebar abierto, con el
   argumento de que ya lo marca el hijo. Es cierto MIENTRAS el hijo se vea, y
   solo entonces: medido, el grupo que contiene la ruta activa salía idéntico
   a los otros cinco (peso 400, sin fondo, sin acento) porque además nadie
   escribía nunca la clase. Con el submenú PLEGADO no queda ningún portador.

   El reparto de abajo mantiene la intención original —padre e hijo no pueden
   parecer dos páginas activas— y a la vez hace perceptible el contexto:
   superficie suave (`hover-bg`, el escalón por debajo del activo) más el peso
   que ya estaba. Sin colores nuevos: los dos tokens ya se remapean por tema y
   por marca. */
.sidebar.sidebarOpened .item-parent-container.active {
    border-left: none;
    background: var(--upaep-trinity-hover-bg);
    box-shadow: none;
    color: var(--upaep-trinity-text-strong);
    font-weight: var(--upaep-fw-semibold, 600);
}

/*? Grupo PLEGADO que contiene la ruta activa: aquí el hijo no puede marcarla
   —está fuera de la vista— así que el acento vuelve al padre, exactamente
   como hace el riel en la sección 2B. No hay ambigüedad posible: no hay dos
   portadores a la vez, porque la condición es que el submenú esté cerrado. */
.sidebar.sidebarOpened .item-parent:not(:has(> .submenu.submenu-open)) > .item-parent-container.active {
    background: var(--upaep-trinity-active-bg);
    box-shadow: inset 3px 0 0 var(--upaep-trinity-item-active-bar, var(--upaep-trinity-accent));
}

.submenu-arrow {
    flex-shrink: 0;
    width: .65rem !important;
    height: .65rem !important;
    margin: 0 var(--upaep-trinity-sp-1, .25rem) 0 auto;
    opacity: .7;
    transition: transform var(--upaep-trinity-transition);
    filter: invert(var(--upaep-trinity-icon-invert));
}

.arrow-rotated {
    transform: rotate(180deg);
}

/*? ── Submenú — se conserva el mecanismo max-height que alterna el JS legacy ── */
.container__Sidebar .sidebar ul.submenu,
.submenu {
    position: relative;
    display: flex !important;
    flex-direction: column !important;
    gap: 1px !important;
    max-height: 0 !important;
    margin: 0 !important;
    padding: 0 !important;
    list-style: none !important;
    font-weight: var(--upaep-fw-regular, 400) !important;
    background: transparent !important;
    opacity: 0 !important;
    overflow: hidden !important;
    transition: max-height var(--upaep-trinity-transition), opacity .18s ease !important;
}

.container__Sidebar .sidebar ul.submenu-open,
.submenu-open {
    max-height: 600px !important;
    padding: var(--upaep-trinity-sp-1, .25rem) 0 var(--upaep-trinity-sp-1, .25rem) !important;
    opacity: 1 !important;
}

/*? Subitem: sangrado + guía vertical tenue */
#elementosMenu .submenu-item,
.submenu-item {
    min-height: 2rem !important;
    padding: var(--upaep-trinity-sp-1, .25rem) var(--upaep-trinity-sp-2, .5rem) var(--upaep-trinity-sp-1, .25rem) var(--upaep-trinity-sp-6, 2rem) !important;
    color: var(--upaep-trinity-text-muted);

    &::before {
        content: "";
        position: absolute;
        top: 0;
        bottom: 0;
        left: 1.3rem;
        width: 1px;
        background: var(--upaep-trinity-divider);
    }

    &:hover {
        color: var(--upaep-trinity-text-strong);
    }
}

.submenu-item .itemMenu {
    font-size: var(--upaep-trinity-fz-meta, .75rem);
    font-weight: inherit;
}

.submenu-item .imgMenu {
    display: none !important;
}

/*? Subitem activo: sin píldora — texto de marca + la propia guía como acento */
#elementosMenu .submenu-item.active,
.submenu-item.active {
    background: transparent;
    box-shadow: none;
    color: var(--upaep-trinity-item-active-text, var(--upaep-trinity-accent));
    font-weight: var(--upaep-fw-semibold, 600);

    &::before {
        width: 2px;
        background: var(--upaep-trinity-item-active-bar, var(--upaep-trinity-accent));
    }
}

/*? ── Pie del sidebar ── */
.container__Sidebar .sidebar .contenedorInferior,
.contenedorInferior {
    position: static;
    flex-shrink: 0;
    width: auto;
    bottom: auto;
    margin-top: auto;
    padding: var(--upaep-trinity-sp-2, .5rem);
    border-top: 1px solid var(--upaep-trinity-divider);

    &:empty {
        display: none;
    }

    & .container {
        display: flex;
        flex-direction: column;
        gap: var(--upaep-trinity-sp-1, .25rem);
        width: 100%;

        & .inner-container {
            display: flex;
            align-items: center;
            gap: var(--upaep-trinity-sp-2, .5rem);
            width: 100%;
            padding: var(--upaep-trinity-sp-1, .25rem) var(--upaep-trinity-sp-2, .5rem);
            border-radius: var(--upaep-trinity-radius-md, 8px);
            cursor: pointer;
            transition: background .15s ease;

            & .play-icon {
                width: 1.15rem;
                height: 1.15rem;
                opacity: .78;
                filter: invert(var(--upaep-trinity-menu-icon-invert));
            }

            & .title {
                font-size: var(--upaep-trinity-fz-meta, .75rem);
                font-weight: var(--upaep-fw-regular, 400);
                color: var(--upaep-trinity-text);
            }

            &:hover {
                background: var(--upaep-trinity-hover-bg);
                border-left: 0;

                & .title {
                    color: var(--upaep-trinity-text-strong);
                }
            }
        }
    }
}

/*? ── Identidad institucional de la marca ──────────────────────────────────
   `.upaep__trinity-brand` se pone SOBRE el slot de marca que el cascarón ya
   tenía (`.cont-logo-upaep`), así que el markup legacy que mete cualquier otra
   cosa ahí sigue comportándose igual, y el colapso a riel lo sigue gobernando
   la regla de siempre —«Estados de ancho»—: en el riel la zona de marca **no se
   ve** y la hamburguesa queda centrada. Aquí no se inventa ningún estado nuevo.

   Trinity resuelve UNA identidad: el **logotipo** de la marca activa.

     marca + tema → logo

   Ni isotipo, ni nombre, ni una segunda identidad al lado. `TrinityIdentity`
   decide QUÉ hay (escribe el `src` desde el contrato de marca y marca
   `__con-logo`) y este bloque lo presenta.

   Los huecos de **isotipo** y de **nombre** existen sólo si el consumidor los
   escribe en su markup, o si pide explícitamente `data-identity-context`. Son
   compatibilidad y opt-in, nunca comportamiento automático: un bloque vacío
   produce un logotipo y nada más. */
.upaep__trinity-brand {
    min-width: 0;
    max-width: 100%;

    & .upaep__trinity-brand-link {
        display: inline-flex;
        align-items: center;
        min-width: 0;
        max-width: 100%;
        height: 100%;
        color: inherit;
        text-decoration: none;
    }

    /*? Los candidatos parten apagados: sin contrato cargado no puede verse un
       `<img>` sin `src` ni un hueco de nombre vacío. */
    & .upaep__trinity-brand-logo,
    & .upaep__trinity-brand-icon,
    & .upaep__trinity-brand-name {
        display: none;
    }

    /*? El logotipo conserva su proporción: se le fija SOLO el alto —el que deja
       `.cont-logo` (56px) con aire— y el ancho lo pone su propio viewBox. Así un
       bloque apilado (UIC) y uno horizontal (UPAEP) caben sin deformarse. Por eso
       el `<img>` del logotipo no lleva width/height en el markup: su proporción
       la decide cada marca, no Trinity. */
    & .upaep__trinity-brand-logo {
        width: auto;
        height: 2.25rem;
        max-width: 100%;
        object-fit: contain;
    }

    /*? Opt-in. Sólo se estila; que aparezca depende de que el consumidor lo haya
       escrito o lo haya pedido con `data-identity-context="compact"`. */
    & .upaep__trinity-brand-icon {
        width: 1.75rem;
        height: 1.75rem;
        flex: 0 0 auto;
        object-fit: contain;
    }

    /*? Trinity NO pinta la identidad. Ni `color`, ni `filter`, ni una placa de
       fondo: cada asset trae sus colores escritos y, si necesita otro sobre
       fondo oscuro, la marca declara `logoDark` / `iconDark` y lo que cambia es
       el ARCHIVO, no el estilo. Un SVG cargado como <img> es ademas un documento
       aislado: el `color` del host no le llega, asi que recolorear desde aqui no
       funcionaria aunque se quisiera. */

    & .upaep__trinity-brand-name {
        overflow: hidden;
        font-size: .95rem;
        font-weight: 650;
        color: var(--ui-text-strong, currentColor);
        white-space: nowrap;
        text-overflow: ellipsis;
    }
}

/*? El comportamiento automático, y el único: el logotipo de la marca activa. */
.upaep__trinity-brand.__con-logo .upaep__trinity-brand-logo {
    display: block;
}

/*? COMPATIBILIDAD — no genera nada. Estas dos reglas sólo pueden encenderse si
   el consumidor escribió esos huecos en su markup (o pidió el contexto
   compacto). Un bloque hidratado por Trinity no tiene ninguno de los dos, así
   que para él no existen.

   · nombre: respaldo textual del markup histórico cuando la marca no tiene logo;
   · isotipo: sólo con `data-identity-context="compact"`, que es una petición
     explícita del consumidor, nunca una decisión de Trinity. */
.upaep__trinity-brand:not(.__con-logo) .upaep__trinity-brand-name {
    display: block;
}

.upaep__trinity-brand[data-identity-context="compact"] .upaep__trinity-brand-logo {
    display: none;
}

.upaep__trinity-brand[data-identity-context="compact"].__con-icono .upaep__trinity-brand-icon {
    display: block;
}

/*? ── Estados de ancho ─────────────────────────────────────────────────── */
.sidebar.sidebarOpened {
    width: var(--upaep-trinity-sidebar-width);

    & .cont-logo {
        justify-content: space-between;

        & .cont-logo-upaep {
            display: flex;
        }
    }
}

.sidebar.notSidebar {
    width: 0;
    padding: 0;
    border-right-width: 0;
    overflow: hidden;
}

/*? Riel: solo iconos, centrados */
.sidebar.sidebarClosed {
    width: var(--upaep-trinity-sidebar-width-collapsed);

    & .cont-logo {
        justify-content: center;

        /*? La zona de marca no se ve en el riel: es el contrato del cascarón desde
           siempre y la identidad no lo cambia. Se nombran los dos selectores
           porque `.upaep__trinity-brand` puede ir sobre `.cont-logo-upaep` o
           solo. NO se sustituye por un isotipo: que una marca tenga
           `app-icon.svg` publicado no significa que Trinity deba mostrarlo. */
        & .cont-logo-upaep,
        & .upaep__trinity-brand {
            display: none;
        }
    }

    & .upaep__section__up__menu__sidebar {
        padding: var(--upaep-trinity-sp-3, .75rem) var(--upaep-trinity-sp-2, .5rem);

        & .current-company {
            justify-content: center;
            padding: var(--upaep-trinity-sp-2, .5rem);
        }

        & h2 {
            display: none;
        }

        & .company-list {
            display: none;
        }
    }

    & #elementosMenu {
        padding: 0 var(--upaep-trinity-sp-2, .5rem) !important;
        /*? en el riel los 10px del carril descentrarían los iconos */
        scrollbar-gutter: auto;
    }

    & #elementosMenu .item,
    & .item-parent-container {
        justify-content: center;
        padding: var(--upaep-trinity-sp-2, .5rem) 0;
        /*? Objetivo táctil cómodo: en el riel el item se queda sin texto, pero el
           área de interacción no debe encogerse al tamaño del icono. */
        min-height: 2.75rem;
    }

    /*? El rótulo se oculta A LA VISTA, nunca del DOM: con `display: none` el item
       perdía su nombre accesible y —como los consumidores escuchan el clic en el
       `<a>`— el riel dejaba de navegar por completo. Aquí el ancla pasa a cubrir
       TODO el item, así que sigue siendo el disparador y el objetivo de foco;
       sólo su texto sale de la caja. */
    & #elementosMenu .item:not(.item-parent)>a.itemMenu {
        position: absolute;
        inset: 0;
        display: block;
        flex: none;
        width: auto;
        padding: 0;
        overflow: hidden;
        white-space: nowrap;
        text-indent: 101%;
    }

    /*? El item padre se dispara desde su contenedor, no desde el ancla: aquí basta
       con esconder el rótulo conservando el nombre accesible. */
    & .item-parent-container .itemMenu {
        position: absolute;
        width: 1px;
        height: 1px;
        /*? `min-height` se anula A PROPÓSITO: el suelo de objetivo táctil que la
           caja del enlace lleva por defecto (--upaep-trinity-target-min) es el
           mínimo de un objetivo REAL, y aquí el enlace no es el objetivo —lo es
           el contenedor del grupo, que en el riel mide 2.75rem—. Sin esto,
           `min-height` ganaría a `height: 1px` y el rótulo oculto pasaría a ser
           una caja de 24 px recortada, no un rótulo visualmente oculto. */
        min-height: 0;
        margin: -1px;
        padding: 0;
        overflow: hidden;
        white-space: nowrap;
        clip-path: inset(50%);
    }

    & .submenu-arrow {
        display: none !important;
    }

    /*? En el riel el icono es lo único que queda: crece la RANURA, y con ella
       cualquier icono que se dibuje dentro —mapa de bits, SVG o máscara—. */
    & #elementosMenu li .imgMenu,
    & .item-parent-container .imgMenu {
        --upaep-trinity-icon-size: 1.35rem;
    }

    /*? Con un hijo activo, el acento se muestra en el padre */
    & .item-parent-container.active {
        border-left: 0;
        background: var(--upaep-trinity-active-bg);
        color: var(--upaep-trinity-item-active-text, var(--upaep-trinity-accent));
        box-shadow: inset 3px 0 0 var(--upaep-trinity-item-active-bar, var(--upaep-trinity-accent));
    }

    & .submenu {
        display: none !important;
        max-height: 0 !important;
        opacity: 0 !important;
        transition: none !important;
    }

    & .submenu-item {
        display: none !important;
    }

    & .contenedorInferior .container .inner-container {
        justify-content: center;

        & .title {
            display: none;
        }
    }
}

.sidebar.sidebarOpened .item-parent .submenu.submenu-open {
    display: flex !important;
}

.sidebar.sidebarOpened .submenu-item {
    display: flex !important;
}

/*? El sidebar como bloque no lleva fondo de estado */
div.sidebar.active {
    background: var(--upaep-trinity-surface);
}

/*? ══════════════════════════════════════════════════════════════════════════
   TOOLTIP DEL RIEL
   Nodo único `position: fixed` en el <body>, creado y posicionado por
   trinity-shell.js. Va fuera del sidebar a propósito: `#elementosMenu` scrollea
   (overflow) y un tooltip absoluto dentro quedaría recortado.
   Mismo patrón que el componente uikit/sidebar.
════════════════════════════════════════════════════════════════════════════ */
.upaep__trinity-tooltip {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 2147483000;
    max-width: 220px;
    padding: var(--upaep-trinity-sp-1, .25rem) var(--upaep-trinity-sp-2, .5rem);
    font-family: var(--upaep-trinity-font);
    font-size: var(--upaep-trinity-fz-meta, .75rem);
    line-height: 1.3;
    color: var(--ui-on-surface-inverse, #fff);
    background: var(--ui-surface-inverse, #23262b);
    border-radius: var(--upaep-trinity-radius-sm, 6px);
    box-shadow: 0 6px 18px rgb(0 0 0 / .22);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    pointer-events: none;
    opacity: 0;
    transform: translateX(-4px);
    transition: opacity .12s ease, transform .12s ease;

    &.is-show {
        opacity: 1;
        transform: translateX(0);
    }
}

/*? ══════════════════════════════════════════════════════════════════════════
   FLYOUT DE SUBGRUPOS EN EL RIEL
   Con el sidebar colapsado los submenús están ocultos (no cabe el texto), así que
   al pulsar un grupo se despliega este panel al costado. Igual que el tooltip:
   nodo `position: fixed` en el <body> creado por trinity-shell.js, para que no
   lo recorte el `overflow` de #elementosMenu.
════════════════════════════════════════════════════════════════════════════ */
.upaep__trinity-flyout {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 2147482000;
    min-width: 210px;
    max-width: 300px;
    max-height: 70dvh;
    padding: var(--upaep-trinity-sp-1, .25rem);
    font-family: var(--upaep-trinity-font);
    color: var(--upaep-trinity-text);
    background: var(--upaep-trinity-surface);
    border: 1px solid var(--upaep-trinity-divider);
    border-radius: var(--upaep-trinity-radius-md, 8px);
    box-shadow: var(--upaep-trinity-shadow);
    overflow-y: auto;
    scrollbar-width: var(--upaep-trinity-scrollbar-width, thin);
    scrollbar-color: var(--upaep-trinity-scrollbar-thumb) var(--upaep-trinity-scrollbar-track);
    opacity: 0;
    visibility: hidden;
    transform: translateX(-4px);
    transition: opacity .12s ease, transform .12s ease, visibility .12s;

    &.is-show {
        opacity: 1;
        visibility: visible;
        transform: translateX(0);
    }

    /*? Cabecera: el nombre del grupo, que en el riel no se ve */
    & .upaep__trinity-flyout-title {
        display: block;
        margin: 0 0 var(--upaep-trinity-sp-1, .25rem);
        padding: var(--upaep-trinity-sp-1, .25rem) var(--upaep-trinity-sp-2, .5rem);
        font-size: var(--upaep-trinity-fz-label, .75rem);
        font-weight: var(--upaep-fw-semibold, 600);
        letter-spacing: .07em;
        text-transform: uppercase;
        color: var(--upaep-trinity-text-muted);
        border-bottom: 1px solid var(--upaep-trinity-divider);
    }

    & .upaep__trinity-flyout-item {
        display: block;
        width: 100%;
        margin: 1px 0;
        padding: var(--upaep-trinity-sp-2, .5rem) var(--upaep-trinity-sp-2, .5rem);
        border: 0;
        border-radius: var(--upaep-trinity-radius-sm, 6px);
        background: transparent;
        color: var(--upaep-trinity-text);
        font: inherit;
        font-size: var(--upaep-trinity-fz-meta, .75rem);
        line-height: 1.35;
        text-align: left;
        text-decoration: none;
        cursor: pointer;
        transition: background .15s ease, color .15s ease;

        &:hover {
            background: var(--upaep-trinity-hover-bg);
            color: var(--upaep-trinity-text-strong);
        }

        &:focus-visible {
            outline: var(--ui-focus-ring-width, 2px) solid var(--ui-focus-ring-color, currentColor);
            outline-offset: var(--ui-focus-ring-offset, 2px);
        }

        &.is-active {
            color: var(--upaep-trinity-item-active-text, var(--upaep-trinity-accent));
            font-weight: var(--upaep-fw-semibold, 600);
        }
    }
}

[data-theme="dark"] .upaep__trinity-tooltip {
    color: var(--ui-on-surface-inverse, #ececf0);
    background: var(--ui-surface-inverse, #3a3a40);
}

/*? ── Loader del sidebar ── */
.container__Sidebar .loader_sidebar,
.loader_sidebar {
    position: fixed;
    inset: 50% auto auto calc(var(--upaep-trinity-sidebar-width) / 2);
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;
    width: auto;
    visibility: hidden;
    opacity: 0;
    transition: opacity .35s ease, visibility .35s ease;

    &.is-visible {
        visibility: visible;
        opacity: 1;
    }
}

/*? ═══════════════════════════════════════════════════════════════════════════
   6. MAIN — offset sincronizado con el estado del sidebar
════════════════════════════════════════════════════════════════════════════ */
.container__Main {
    box-sizing: border-box;
    flex: 1;
    min-width: 0;
    margin: var(--upaep-trinity-header-height) 0 0 0;
    padding: var(--upaep-trinity-gutter) var(--upaep-trinity-gutter) var(--upaep-trinity-gutter) var(--upaep-trinity-offset);
    min-height: calc(100dvh - var(--upaep-trinity-header-height) - var(--upaep-trinity-footer-height));
    transition: padding-left var(--upaep-trinity-transition);

    &:has(.accesosGeneral) {
        display: flex;
        align-items: center;
        justify-content: center;
    }
}

.container__Main .upaep__views,
.container__Main .upaep__view {
    min-width: 0;
}

/*? ── Clases de estado (las alterna el JS legacy en main y footer) ─────────
   Redefinen el offset compartido en vez de repetir paddings. */
.opened-sidebar {
    --upaep-trinity-offset: calc(var(--upaep-trinity-sidebar-width) + var(--upaep-trinity-gutter));
}

.closed-sidebar {
    --upaep-trinity-offset: calc(var(--upaep-trinity-sidebar-width-collapsed) + var(--upaep-trinity-gutter));
}

.notSidebar,
.no-sidebar {
    --upaep-trinity-offset: var(--upaep-trinity-gutter);
}

/*? Reflejo en el header: el offset vive en `body`, así que se deriva con :has */
body:has(.sidebar.sidebarClosed) {
    --upaep-trinity-offset: calc(var(--upaep-trinity-sidebar-width-collapsed) + var(--upaep-trinity-gutter));
}

body:has(.sidebar.notSidebar),
body:not(:has(.container__Sidebar .sidebar)) {
    --upaep-trinity-offset: var(--upaep-trinity-gutter);
}

/*? Los paddings legacy se anulan: quien manda es --upaep-trinity-offset */
.container__Main.opened-sidebar,
.container__Main.closed-sidebar,
.container__Main.no-sidebar {
    padding: var(--upaep-trinity-gutter) var(--upaep-trinity-gutter) var(--upaep-trinity-gutter) var(--upaep-trinity-offset) !important;
}

/*? ═══════════════════════════════════════════════════════════════════════════
   7. FOOTER — hairline superior, texto tenue, sin barra negra
════════════════════════════════════════════════════════════════════════════ */
.container__Footer footer {
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--upaep-trinity-sp-4, 1rem);
    float: none;
    width: auto;
    min-height: var(--upaep-trinity-footer-height);
    padding: var(--upaep-trinity-sp-3, .75rem) var(--upaep-trinity-gutter) var(--upaep-trinity-sp-3, .75rem) var(--upaep-trinity-offset);
    text-align: center;
    font-size: var(--upaep-fz12, .75rem);
    color: var(--upaep-trinity-text-muted);
    background: var(--upaep-trinity-surface);
    border-top: 1px solid var(--upaep-trinity-divider);
    transition: padding-left var(--upaep-trinity-transition), background .2s ease;
}

.container__Footer footer p,
footer p {
    margin: 0;
    color: var(--upaep-trinity-text-muted) !important;
}

.container__Footer footer a {
    margin: 0;
    color: var(--upaep-trinity-text-muted);
    text-decoration: none;
    transition: color .15s ease;

    &:hover {
        color: var(--upaep-trinity-accent);
        text-decoration: underline;
    }
}

/*? Estados: el offset ya viene de las clases compartidas de la sección 6 */
.container__Footer footer.opened-sidebar,
.container__Footer footer.closed-sidebar,
.container__Footer footer.notSidebar {
    padding-left: var(--upaep-trinity-offset);
}

/*? ═══════════════════════════════════════════════════════════════════════════
   8. POPOVERS DEL HEADER (plataformas / imagen de usuario)
════════════════════════════════════════════════════════════════════════════ */
#modal-overlay-platforms {
    position: fixed;
    inset: calc(var(--upaep-trinity-header-height) + .5rem) 1rem auto auto;
    z-index: 5000;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    width: min(360px, calc(100vw - 2rem));
    height: auto;
    max-height: min(520px, calc(100dvh - var(--upaep-trinity-header-height) - 2rem));
    padding: 0;
    background: var(--upaep-trinity-surface);
    border: 1px solid var(--upaep-trinity-divider);
    border-radius: var(--upaep-trinity-radius-lg, 12px);
    box-shadow: var(--upaep-trinity-shadow);
    overflow: hidden;

    & #modal-border {
        width: 100%;
        height: 100%;
        background: transparent;
        border-radius: 0;
        overflow-y: auto;
        scrollbar-width: var(--upaep-trinity-scrollbar-width, thin);
        scrollbar-color: var(--upaep-trinity-scrollbar-thumb) var(--upaep-trinity-scrollbar-track);

        & #cont-platforms {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(92px, 1fr));
            gap: var(--upaep-trinity-sp-1, .25rem);
            width: 100%;
            padding: var(--upaep-trinity-sp-3, .75rem);

            & .platform-content {
                display: flex;
                flex-direction: column;
                align-items: center;
                justify-content: flex-start;
                gap: var(--upaep-trinity-sp-2, .5rem);
                width: auto;
                min-height: auto;
                padding: var(--upaep-trinity-sp-3, .75rem) var(--upaep-trinity-sp-1, .25rem);
                border-radius: var(--upaep-trinity-radius-md, 8px);
                cursor: pointer;
                transition: background .15s ease;

                & .icon-platform {
                    display: grid;
                    place-items: center;
                    width: 2.5rem;
                    min-height: 2.5rem;
                    height: 2.5rem;
                    background: var(--upaep-trinity-surface-2);
                    border-radius: 50%;
                    overflow: hidden;

                    & img {
                        width: 62%;
                        height: 62%;
                        border-radius: 0;
                        object-fit: contain;
                    }
                }

                & .title-platform {
                    font-size: var(--upaep-fz12, .75rem);
                    line-height: 1.3;
                    color: var(--upaep-trinity-text);
                    text-align: center;
                    text-wrap: balance;
                }

                &:hover {
                    height: auto;
                    background: var(--upaep-trinity-hover-bg);
                    outline: none;

                    & .title-platform {
                        color: var(--upaep-trinity-text-strong);
                    }
                }
            }
        }
    }
}

#modal-overlay-img_user {
    position: fixed;
    inset: calc(var(--upaep-trinity-header-height) + .5rem) 1rem auto auto;
    z-index: 5000;
    display: flex;
    align-items: center;
    justify-content: center;
    width: min(330px, calc(100vw - 2rem));
    height: auto;
    padding: var(--upaep-trinity-sp-4, 1rem);
    background: var(--upaep-trinity-surface);
    border: 1px solid var(--upaep-trinity-divider);
    border-radius: var(--upaep-trinity-radius-lg, 12px);
    box-shadow: var(--upaep-trinity-shadow);
}

/*? ═══════════════════════════════════════════════════════════════════════════
   9. SOPORTE PARA SVG INLINE (markup limpio)
   El markup legacy usa <img> con base64 de relleno blanco (se invierten por CSS).
   El markup limpio de trinity-shell.html usa SVG inline: se pintan con
   currentColor, así que aquí se les quita el filtro y se les da caja.
════════════════════════════════════════════════════════════════════════════ */
#elementosMenu svg.imgMenu {
    flex-shrink: 0;
    /*? La caja la fija la ranura (`--upaep-trinity-icon-size`), no un literal
       repetido: si el riel cambia la medida, el SVG y un icono de máscara tienen
       que crecer LOS DOS. Antes el literal vivía aquí y sólo crecía el SVG. */
    width: var(--upaep-trinity-icon-size, 1.15rem);
    height: var(--upaep-trinity-icon-size, 1.15rem);
    fill: none;
    stroke: currentColor;
    stroke-width: 1.7;
    stroke-linecap: round;
    stroke-linejoin: round;
    opacity: .78;
    filter: none;
}

#elementosMenu .item-parent-container.active>.imgMenu {
    opacity: 1;
}

/*? En el riel el icono es lo ÚNICO que queda del item: crece la ranura, y con
   ella todo lo que se dibuje dentro, venga como SVG o como máscara. */
.sidebar.sidebarClosed #elementosMenu .imgMenu,
.sidebar.sidebarClosed .item-parent-container .imgMenu {
    --upaep-trinity-icon-size: 1.35rem;
}

svg.submenu-arrow {
    fill: none;
    stroke: currentColor;
    stroke-width: 2.2;
    stroke-linecap: round;
    stroke-linejoin: round;
    filter: none;
}

.choose-company .current-company>svg,
.choose-company .company-list li>svg {
    flex-shrink: 0;
    width: 1.35rem;
    height: 1.35rem;
    border-radius: var(--upaep-trinity-radius-sm, 6px);
}

.contenedorInferior .inner-container>svg.play-icon {
    flex-shrink: 0;
    width: 1.15rem;
    height: 1.15rem;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.7;
    stroke-linecap: round;
    stroke-linejoin: round;
    opacity: .78;
    filter: none;
}

/*? Avatar por iniciales (cuando no hay imagen de usuario) */
#upaep__icon_name__comp__user {
    font-size: var(--upaep-trinity-fz-label, .75rem);
    font-weight: var(--upaep-fw-semibold, 600);
    letter-spacing: .02em;
    color: var(--upaep-trinity-text-muted);
    user-select: none;
}

/*? ═══════════════════════════════════════════════════════════════════════════
   10. RESPONSIVE
════════════════════════════════════════════════════════════════════════════ */
/*? Tablet: el sidebar abierto ya no roba tanto ancho */
@media (width <=1200px) {
    :root {
        --upaep-trinity-gutter: 1rem;
    }
}

/*? Móvil: el sidebar pasa a cajón off-canvas */
@media (width <=780px) {

    :root,
    body,
    body:has(.sidebar.sidebarClosed),
    .opened-sidebar,
    .closed-sidebar {
        --upaep-trinity-offset: var(--upaep-trinity-gutter-sm);
    }

    .container__Header header,
    .container__Header header.upaep__container__comp_dft {
        gap: var(--upaep-trinity-sp-1, .25rem);
        padding: 0 var(--upaep-trinity-gutter-sm);
    }

    /*? la hamburguesa del header es el disparador del cajón */
    .container__Header header .upaep__logo_comp_dft,
    body:has(.sidebar.sidebarOpened) .container__Header header .upaep__logo_comp_dft,
    body:has(.sidebar.sidebarClosed) .container__Header header .upaep__logo_comp_dft {
        display: flex;
    }

    .container__Sidebar .sidebar {
        width: var(--upaep-trinity-sidebar-width-drawer);
        max-width: 86vw;
        box-shadow: var(--upaep-trinity-shadow);
        transform: translateX(-100%);
    }

    .container__Sidebar .sidebar.sidebarClosed,
    .container__Sidebar .sidebar.notSidebar {
        width: var(--upaep-trinity-sidebar-width-drawer);
        max-width: 86vw;
        padding: 0;
        border-right-width: 1px;
        transform: translateX(-100%);
    }

    .container__Sidebar .sidebar.sidebarOpened {
        transform: translateX(0);
    }

    /*? En el cajón el riel no aplica: textos y submenús vuelven a verse. Hay que
       deshacer TODO el tratamiento de rótulo del riel (posición y recorte), no
       sólo el `display`, o el cajón mostraría los items sin texto. */
    .container__Sidebar .sidebar.sidebarClosed .cont-logo-upaep,
    .container__Sidebar .sidebar.sidebarClosed .upaep__trinity-brand,
    .container__Sidebar .sidebar.sidebarClosed .submenu-arrow {
        display: initial !important;
    }

    .container__Sidebar .sidebar.sidebarClosed #elementosMenu .item:not(.item-parent)>a.itemMenu,
    .container__Sidebar .sidebar.sidebarClosed .item-parent-container .itemMenu {
        position: static;
        inset: auto;
        display: block;
        flex: 1;
        width: auto;
        height: auto;
        margin: 0;
        overflow: hidden;
        clip-path: none;
        text-indent: 0;
        text-overflow: ellipsis;
    }

    /*? El cajón es navegación táctil: el item mantiene el objetivo cómodo. */
    .container__Sidebar .sidebar #elementosMenu .item,
    .container__Sidebar .sidebar .item-parent-container {
        min-height: 2.75rem;
    }

    /*? Muescas y barra de gestos: el cajón llega a los bordes reales del panel. */
    .container__Sidebar .sidebar {
        padding-bottom: env(safe-area-inset-bottom, 0);
        padding-left: env(safe-area-inset-left, 0);
    }

    /*? Abrir y cerrar la navegación son los dos controles más pulsados del móvil:
       el icono sigue midiendo lo mismo, sólo crece su caja de interacción. Con el
       cajón abierto, el disparador del header queda DEBAJO del propio cajón —que
       ocupa el borde izquierdo a toda altura—, así que el cierre es este. */
    .container__Sidebar .cont-logo .hamb-menu,
    .container__Header header .hamb-menu {
        min-width: 2.75rem;
        min-height: 2.75rem;
    }

    /*? Scrim: se pinta desde el contenedor del sidebar (clic → lo cierra el JS) */
    body:has(.sidebar.sidebarOpened) .container__Sidebar::after {
        content: "";
        position: fixed;
        inset: 0;
        z-index: calc(var(--upaep-trinity-zindex-sidebar) - 1);
        background: rgb(15 18 25 / .45);
    }

    body:has(.sidebar.sidebarOpened) {
        overflow: hidden;
    }

    .container__Main,
    .container__Main.opened-sidebar,
    .container__Main.closed-sidebar,
    .container__Main.no-sidebar {
        padding: var(--upaep-trinity-gutter-sm) !important;
        padding-top: var(--upaep-trinity-gutter-sm) !important;
    }

    .container__Footer footer,
    .container__Footer footer.opened-sidebar,
    .container__Footer footer.closed-sidebar,
    .container__Footer footer.notSidebar {
        padding-left: var(--upaep-trinity-gutter-sm) !important;
    }
}

/*? Compacto: se sacrifica primero el nombre, luego la fecha */
@media (width <=640px) {
    .container__Header header .upaep__name__comp_dft #upaep__name__comp__user {
        display: none !important;
    }
}

@media (width <=480px) {
    .container__Header header .upaep__date__comp_dft {
        display: none;
    }
}

/*? ═══════════════════════════════════════════════════════════════════════════
   11. MOVIMIENTO REDUCIDO
════════════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {

    .container__Header header,
    .container__Sidebar .sidebar,
    .container__Sidebar .sidebar *,
    .container__Main,
    .container__Footer footer {
        transition-duration: .01ms !important;
    }
}