/* ============================================================================
   Sistema Académico · Unidad Educativa Montessori
   Sistema de diseño v5 — «institucional denso».
   Reemplaza a dashboards.css (shell de tarjetas flotantes).

   Qué manda aquí, en orden:
     1. DESIGN.md del repo (paleta y contraste MEDIDOS de los activos reales).
     2. Registro «producto» del stack de diseño: el software sirve a la tarea.
        Una sola familia tipográfica, escala fija, color contenido, densidad alta.

   Decisiones que NO son de gusto y por eso están escritas:
     · Sin serif en datos. El serif de títulos que había antes ponía Palatino en
       las CIFRAS: una fuente de texto corrido en una columna numérica hace bailar
       los dígitos. Una familia, pesos distintos.
     · Sin sombras ni elevación en el contenido. La sombra solo existe donde algo
       flota DE VERDAD (menú, paleta de comandos, diálogo).
     · Sin animación de entrada por tarjeta. El directivo entra a leer un número,
       no a mirar cómo aparece.
     · El dorado NO se usa para texto sobre claro (2,45:1, DESIGN.md §3). Vive en
       la selección actual, el foco y el riel navy.
     · Tipografía del sistema. DESIGN.md §5 pedía Inter autoalojada; no hay ningún
       .woff2 en el repo, así que hoy es la pila del sistema (Segoe UI en los
       equipos del colegio). Es honesto y no rompe la CSP.
   ========================================================================== */

/* ============================================================================
   0. FUENTE — Inter Variable AUTOALOJADA (V7 §4.3). DESIGN.md §5 la pedía y no
   había ningún .woff2 en el repo; ahora vive en /web/fuentes/. La CSP es
   default-src 'self': nada se pide a un host externo. `font-display:swap`: el
   texto sale al toque con la pila del sistema y cambia cuando la fuente llega.
   ========================================================================== */
/* DISPLAY · Bricolage Grotesque (SIL OFL 1.1, autoalojada). Inter queda como
   tipografía de TEXTO y de cifras; usarla también de display es el tell número
   uno de interfaz generada. Su eje de ANCHURA (75-100%) es la firma tipográfica
   de la casa: los títulos de pantalla se condensan, y así la jerarquía la lleva
   la forma de la letra y no un peso de 800. */
@font-face{
  font-family:"Bricolage";
  src:url("/web/fuentes/BricolageGrotesque-latin.woff2") format("woff2");
  font-weight:400 800;
  font-stretch:75% 100%;
  font-style:normal;
  font-display:swap;
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,
    U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:"Bricolage";
  src:url("/web/fuentes/BricolageGrotesque-latin-ext.woff2") format("woff2");
  font-weight:400 800;
  font-stretch:75% 100%;
  font-style:normal;
  font-display:swap;
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,
    U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+2C60-2C7F,U+A720-A7FF;
}
/* V11 · Hanken Grotesk: la cara del texto y de los títulos.
   Por qué se cambia. Hidalgo, tres veces: «bonito pero anticuado», «no tan
   institucional», «necesito algo más moderno». Inter como cara de interfaz es —
   medido por los propios auditores de diseño— la cara con la que converge cada
   oleada de interfaces generadas; leerla es leer «esto lo hizo un molde». Hanken
   Grotesk es una «bouba grotesk»: terminales apenas redondeados, ojos generosos,
   trazos rectos levísimamente curvados. Se lee cálida sin ser blanda, y no es
   institucional porque no viene de la señalética.
   Inter NO se retira: se queda donde es imbatible, en las CIFRAS con
   `tabular-nums` (DESIGN.md §5 — sin eso los números bailan al actualizarse).
   Licencia OFL-1.1, autoalojada: la CSP prohíbe hosts externos. */
@font-face{
  font-family:"Hanken";
  src:url("/web/fuentes/HankenGrotesk-latin.woff2") format("woff2");
  font-weight:100 900;
  font-style:normal;
  font-display:swap;
}

/* V12 · Fraunces (SIL OFL 1.1, autoalojada, subset latin). El SERIF editorial para
   TÍTULOS de pantalla y de panel: es lo que sube la percepción de «caro» sin caer en
   el look de plantilla. Óptico variable (opsz), pensado para display. No toca CIFRAS
   (siguen en Inter con tabular-nums) ni el cuerpo (Hanken). Degrada a Georgia si no
   carga. */
@font-face{
  font-family:"Fraunces";
  src:url("/web/fuentes/Fraunces-latin.woff2") format("woff2");
  font-weight:400 600;
  font-style:normal;
  font-display:swap;
}

@font-face{
  font-family:"Inter";
  src:url("/web/fuentes/InterVariable.woff2") format("woff2");
  font-weight:100 900;
  font-style:normal;
  font-display:swap;
}

:root{
  /* --- Marca: valores literales de sus activos (DESIGN.md §1-2) --- */
  --navy:#03045e;
  --navy-logo:#314a97;
  --oro:#dd9817;
  --oro-txt:#8a5f0e;      /* el dorado legible SOBRE PAPEL CÁLIDO: 4,96:1 lienzo · 4,78 panel-2 */
  --verde-txt:#157a4c;    /* el verde legible sobre papel: 5,13:1 */
  --verde-marca:#22a66c;

  /* --- V9-b · EL ARCOÍRIS ORDENA LA SUITE (DESIGN.md §10) -------------------
     Cada aplicación toma un color del logo. No es decoración: responde «¿en qué
     repisa estoy?» sin leer una palabra, y es lo único de la marca que se ve
     mientras se trabaja — el papel de la v2 dejó al colegio en 0,05 % de la
     pantalla de entrada (medido sobre la captura, 2026-08-17).

     🔴 Dos tokens por aplicación, y no es redundancia: el dorado del logo da
     2,35:1 sobre el papel y el verde 2,99:1. Sirven como RELLENO (banda, punto,
     gráfico) y NO como texto — la regla que el DESIGN.md §3 conserva de la v1.
     El `-txt` es la variante oscurecida del mismo matiz, la única que puede
     escribir. Un solo token por app pintaría la miga de GABI en dorado
     ilegible, que es justo el error que esta tabla existe para evitar. --- */
  --app-aula:#314a97;          --app-aula-txt:#314a97;        /* 7,87:1 */
  --app-secretaria:#22a66c;    --app-secretaria-txt:#157a4c;  /* 5,13:1 */
  --app-gabi:#dd9817;          --app-gabi-txt:#8a5f0e;        /* 4,96:1 lienzo (papel cálido) */
  --app-mirador:#03045e;       --app-mirador-txt:#03045e;     /* 17,01:1 */
  /* Sin aplicación («Hoy», la entrada) no manda un color: manda el arcoíris. */
  --app:var(--navy-logo);      --app-txt:var(--navy-logo);

  /* --- Semáforo: color + ícono + texto, siempre (DESIGN.md §4) --- */
  --ok:#1b7f4f;
  --alerta:#8a5a00;
  --riesgo:#b3261e;
  --nada:#5b6478;
  --navy-f:#efe7d6;       /* tinte cálido: fila activa, selección (arena) */
  /* Filas intercaladas y cabecera de tabla — pedido de Hidalgo con la captura del
     Rector al lado: «todo está en blanco, quisiera que tuviera los colores de la
     institución y que vayan intercalados, y así en todo el sistema».
     Medido sobre papel #fff: la banda se separa 19,6 % en luminancia (se VE, que era
     la queja) y la tinta queda a 14,39:1 sobre ella. La cabecera navy con texto blanco,
     17,75:1. El hover es MÁS fuerte que la banda: si fuera más claro, pasar el ratón por
     una fila par la aclararía y se leería como si se hubiera apagado. */
  --fila-par:#ece3d0;
  --fila-hover:#ddd0b4;
  --ok-f:#e6f0e8; --alerta-f:#f6ecd6; --riesgo-f:#f6e4df; --nada-f:#efece4;
  /* V11 · los rellenos de los BLOQUES de cifra (§5). Son los del semáforo
     oscurecidos hasta que el BLANCO encima llegue a 4,5:1, no los del semáforo
     tal cual: `--ok` #1b7f4f con blanco da 4,3 y se queda corto. */
  --navy-bloque:#03045e; --azul-bloque:#314a97;
  --ok-bloque:#157a4c; --alerta-bloque:#946100; --riesgo-bloque:#b3261e;

  /* --- V9 · PAPEL. El fondo es papel y la tarjeta es la hoja encima: al revés
     del patrón habitual (lienzo gris-azul + tarjeta blanca), que es de donde
     salía la sensación pesada. Neutros CÁLIDOS pero desaturados (croma ≤ 0,013
     en oklch): se leen como papel, no como el crema de plantilla que hoy es el
     tell de «IA con gusto». Cada valor sale de un oklch calculado y su contraste
     está MEDIDO, no estimado (DESIGN.md §3). --- */
  /* ─── V11 (2026-08-18) · LA SUPERFICIE DEJA DE SER CREMA ────────────────
     Encargo de Hidalgo, dicho tres veces: «bonito pero anticuado», «no tan
     institucional», «necesito algo más moderno». Y una medición propia que le da
     la razón: sobre las capturas de la V9, el 96,23 % de la pantalla de entrada
     era neutro. El diagnóstico de 2026 para eso tiene nombre — «cementerios en
     escala de grises con tipografía cortés» — y el crema, que la V9 eligió
     precisamente para huir del beige de plantilla, terminó leyéndose como lo que
     el colegio ya tenía: papelería institucional.
     Lo que cambia y por qué cada cosa:
     · el lienzo pasa a un blanco FRÍO (#f5f6fa). No es gris azulado de plantilla:
       es la temperatura de una pantalla, no de un papel. El producto es una
       herramienta, no un impreso.
     · la hoja sigue blanca pura, pero ahora FLOTA (`--lift`, tres capas) en vez
       de llevar un borde. Un borde de 1 px es la firma del panel de 2010; una
       elevación mínima es la de 2026, y además hace la jerarquía sin dibujar.
     · las tintas se enfrían al mismo matiz del lienzo (265°) y se re-miden.
     · la banda de cifras deja de ser una tabla de números y pasa a BLOQUES DE
       COLOR (§5). Es el cambio que se ve de lejos, que es el que se pidió.
     Lo que NO cambia: el semáforo, los mínimos de contraste medidos, la
     prohibición del dorado como texto, y que la marca no pinta ningún dato. */
  --tinta:#241f18;        /* espresso cálido · 14,38:1 lienzo · 16,09 hoja · 13,87 panel-2 — medido */
  --tinta-2:#5a5245;      /*  6,77:1 lienzo · 7,57 hoja — secundario, cálido */
  /* 🔴 Era `#70747b`, declarado «4,50:1 — el mínimo AA», y lo era exactamente:
     contra el PAPEL. Pero la tinta terciaria vive sobre todo en cabeceras de
     columna, filas alternas y celdas inertes, que son `--panel-2` #f7f6f2 — ahí
     daba **4,34:1** y no llegaba a AA. Es el mismo fallo del dorado `#9c6c10` de
     la v2, otra vez: medir contra una superficie y usar sobre otra. Estar
     clavado en el mínimo no deja margen para nada, así que el token nuevo se
     calibra contra la superficie PEOR y con holgura. Mismo matiz y croma
     (oklch .013 265): solo baja la luminosidad. */
  /* 🔴 La superficie PEOR ya no es `--panel-2`: con el lienzo frío, la peor es el
     LIENZO (4,80 contra 4,89). Da igual cuántas veces se haya medido antes: al
     cambiar las superficies hay que volver a medir cuál es la que aprieta. */
  --tinta-3:#6f6455;      /*  5,09:1 lienzo (la peor) · 4,91 panel-2 · 5,69 hoja — cálido */
  /* Se calibró a 3,00 exactos contra el lienzo: #9096ac daba 2,72 y DESIGN.md
     pide 3:1 para el deshabilitado. Mismo matiz, solo menos luz. */
  --tinta-4:#8b8270;      /*  3,34:1 lienzo · 3,22 panel-2 · 3,74 hoja — deshabilitado, cálido */
  /* Las líneas pasan a rgba: una línea sólida sobre un fondo que cambia de tono
     entre superficies se ve dura en una y sucia en otra. En rgba se compone. */
  --linea:rgba(43,34,22,.09);
  --linea-fuerte:rgba(43,34,22,.17);
  --lienzo:#f9f5ec;       /* la MESA: marfil cálido Montessori (materiales naturales) */
  --panel:#fffdf8;        /* la HOJA: blanco cálido, lo que recibe contenido, y FLOTA */
  --panel-2:#f7f3e7;      /* arena: cabeceras de tabla, filas alternas, celdas inertes */
  --realce:#efe9db;       /* hover de fila */
  --seleccion:#e7ddc8;
  /* La elevación de la hoja: anillo de 1 px + dos profundidades suaves. En claro
     una sombra en capas hace lo que un borde hacía, sin dibujar una caja. */
  --lift:0 0 0 1px rgba(43,34,22,.05), 0 1px 2px -1px rgba(43,34,22,.10),
         0 8px 20px -12px rgba(43,34,22,.16);
  --lift-2:0 0 0 1px rgba(43,34,22,.06), 0 2px 4px -2px rgba(43,34,22,.11),
           0 14px 32px -16px rgba(43,34,22,.20);

  /* --- Riel: MISMO papel que el lienzo, separado por una línea de pelo. Antes
     era navy sólido: dos mundos («el del riel» y «el del contenido») que
     partían la pantalla en dos y pesaban. La jerarquía la hace el espacio. --- */
  /* V11 · el riel es la HOJA (blanca) y el contenido descansa en la MESA. Al
     revés de la V9, donde riel y lienzo eran el mismo papel: cuando todo es la
     misma superficie, nada tiene borde y la pantalla se lee como un documento.
     Con el riel en blanco sobre mesa fría hay una jerarquía de dos planos sin
     pintar ninguna franja de color (§8 sigue en pie). */
  --riel:#fffdf8;
  --riel-2:#f4f0e7;
  --riel-txt:#5a5245;
  --riel-txt-2:#6f6455;   /* la misma tinta terciaria, re-medida (ver --tinta-3) */
  --riel-linea:rgba(43,34,22,.09);
  --riel-activo:#eee7d8;
  /* 🔴 La tinta y el fondo del estado ACTIVO sobre el riel. Existen porque el riel
     dejó de ser navy en la V9 y ocho reglas seguían escribiendo `#fff` y fondos
     `rgba(255,255,255,.12)` sobre él: en claro eso es blanco sobre crema. Medido en
     producción: «Inicio del entorno» a 1,07:1, el chip «Ctrl K» a 1,06:1 y el módulo
     de GABI que se acaba de abrir, invisible —que es literalmente lo que se
     reportó: «no puedo ni siquiera ver a qué le di click»—.
     La lección, la misma de las 51 reglas de la capa de mando: cuando una SUPERFICIE
     cambia de oscura a clara, lo que se rompe no es una regla, es toda la familia que
     daba por hecho el fondo viejo. Se arregla con un token por tema, no color a color. */
  --riel-tinta-act:#241f18;
  --riel-fondo-act:#e7ddc8;

  /* --- Escala tipográfica FIJA (razón ~1,15). Producto, no marca --- */
  /* V11 · la escala sube un paso y los saltos de arriba se AGRANDAN. La razón
     ~1,15 de la V9 daba 16 · 18 · 26: un título de pantalla a 18 px con texto a
     14 no manda nada, y era la causa de que las pantallas se leyeran planas («no
     se entiende de un vistazo qué hacer»). Abajo la razón se mantiene apretada
     —los metadatos tienen que caber—, arriba se abre a ~1,4: 17 → 22 → 34 → 52.
     La jerarquía la hacen tamaño + peso + color a la vez, nunca el tamaño solo. */
  --t-2xs:.6875rem;  /* 11 — cabeceras de columna, micro-etiquetas */
  --t-xs:.75rem;     /* 12 — tablas densas, metadatos */
  --t-sm:.8125rem;   /* 13 — tablas, controles densos */
  --t-md:.90625rem;  /* 14.5 — TEXTO DE INTERFAZ por defecto */
  --t-lg:1.0625rem;  /* 17 — título de panel */
  --t-xl:1.375rem;   /* 22 — subtítulo / cifra secundaria */
  --t-2xl:2.125rem;  /* 34 — título de PANTALLA */
  --t-cifra:1.625rem;/* 26 — KPI en tabla */
  --t-cifra-g:3.25rem;  /* 52 — la cifra que manda, dentro de su bloque */

  /* --- Espacio: escala de 4 --- */
  --e1:4px; --e2:8px; --e3:12px; --e4:16px; --e5:20px; --e6:24px; --e8:32px;
  --gabi-inset:var(--e5);   /* margen lateral de GABI: cabecera y cuerpo, el MISMO */

  /* --- Forma: radios contenidos. Nada de almohadones de 16px --- */
  /* V10 · la escala de radios sale de la CURVATURA DEL LOGO, medida, no elegida.
     Se muestrearon los tres contornos de `marca-puente.svg` y se ajustó un
     círculo cada 6 puntos: el radio característico del doblez de cada cinta
     (decil bajo, que descarta el ruido del vectorizado) es **17,0 · 10,8 ·
     6,9** en las unidades del logo. Las tres están en razón **1,57** una de
     otra, y esa razón es la escala.
     Lo bueno del hallazgo: `--r:4px` y `--r-lg:6px` ya estaban en esa razón por
     puro ojo, así que la escala no rompe nada — solo la extiende hacia arriba
     con los dos pasos que la V10 necesita para las formas de cinta (portada de
     aplicación, tarjeta de hueco). 4 · 6 · 10 · 15. */
  /* V11 · la misma razón medida del logo (1,57), un paso arriba: 6 · 9 · 14 · 22.
     No es capricho de forma — con radio 4 px una hoja de 300 px de alto tiene
     esquinas prácticamente rectas, y una esquina recta sobre fondo claro es la
     silueta del panel de administración de hace quince años. */
  --r:6px; --r-lg:9px; --r-xl:14px; --r-2xl:22px; --r-full:999px;
  /* Alto de la cinta de marca en la cabecera (ver §20). Ficha y no valor suelto
     porque la portada de aplicación la dibuja al doble. */
  --cinta-alto:12px;

  /* --- La sombra existe solo donde algo flota de verdad --- */
  --flota:0 8px 28px rgba(6,10,50,.16), 0 1px 3px rgba(6,10,50,.10);
  --flota-alta:0 24px 64px rgba(6,10,50,.26), 0 2px 8px rgba(6,10,50,.12);

  /* --- Movimiento (V7 §5.1). Solo transform/opacity/clip-path/filter; ease-in
     prohibido; lo que entra sale MÁS RÁPIDO. `--curva` queda como alias viejo. */
  --rapido:120ms;  --normal:180ms;  --medio:240ms;  --lento:320ms;
  --sale:cubic-bezier(0.23,1,0.32,1);        /* ease-out fuerte: entradas */
  --mueve:cubic-bezier(0.77,0,0.175,1);      /* ease-in-out: movimiento en pantalla */
  --cajon:cubic-bezier(0.32,0.72,0,1);       /* drawer, tipo iOS */
  --curva:cubic-bezier(.22,1,.36,1);

  /* --- V10 · RESORTES. «Futurista» en este producto no es cromo: es cómo se
     COMPORTA (docs/DIRECCION-V10-PUENTE.md §3). Un `ease-out` llega y se para;
     un resorte llega, se pasa un poco y se acomoda, que es lo que hace una cosa
     con masa. La diferencia se nota sin poder nombrarla, y es lo que separa una
     interfaz que «se siente hecha» de una plantilla.

     No son curvas inventadas: salen de integrar un oscilador armónico
     amortiguado (x(t)=1−e^{−ζω₀t}[cos(ω_d t)+(ζω₀/ω_d)sin(ω_d t)]) y muestrearlo
     en 24 puntos. `--resorte` es ζ=0,72 ω₀=28 → asienta en 257 ms con **3,8 %**
     de sobrepaso; `--resorte-vivo` es ζ=0,62 ω₀=32 → 288 ms y **8,4 %**, para lo
     que celebra algo. La duración acompaña a la curva: usar `--resorte` con
     otra duración deja el rebote a medio camino, así que van en pareja.

     🔴 `linear()` no existe antes de Chrome 113 / Firefox 112 / Safari 17.2, y el
     truco de declarar el respaldo antes NO sirve a través de una ficha: para el
     motor una custom property siempre es «válida», así que un navegador viejo se
     quedaría con un timing-function que no entiende y la transición saldría
     instantánea. Por eso el valor base es una bézier real y `@supports` lo
     asciende al resorte solo donde de verdad se puede. --- */
  --resorte-t:260ms;      --resorte:cubic-bezier(.22,1.2,.36,1);
  --resorte-vivo-t:290ms; --resorte-vivo:cubic-bezier(.18,1.4,.4,1);

  /* --- z-index semántico, sin 9999 --- */
  /* z-index semántico: cinta < popover del menú < velo < cajón < diálogo < aviso.
     Nada de 9999. El aviso (toast) va ARRIBA del cajón: un error dentro del
     panel tiene que verse aunque el panel siga abierto. */
  --z-velo:25; --z-barra:30; --z-pop:40; --z-cajon:50; --z-dialogo:60; --z-aviso:70;

  --riel-ancho:244px;
  --riel-ancho-min:60px;

  --sans:"Hanken","Inter",system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
  /* La cara de las CIFRAS, separada de la del texto a propósito: Hanken no
     garantiza el mismo avance por dígito y una tabla de notas que baila al
     refrescarse es peor que una tabla fea. */
  --cifras:"Inter","Hanken",system-ui,sans-serif;
  /* Display: la misma pila, con Bricolage delante. Si no cargara, degrada a Inter
     y la pantalla sigue siendo legible — nunca a una serif de sistema. */
  /* Los títulos van en la MISMA cara que el texto, un peso arriba y con tracking
     negativo. Bricolage se queda solo para la puerta (login) y el membrete: una
     display condensada en cada título de pantalla es lo que hacía que el producto
     se leyera como un folleto institucional en vez de como una herramienta. */
  --display:"Hanken","Inter",system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
  --display-marca:"Bricolage","Hanken","Inter",system-ui,sans-serif;
  /* V12 · el serif editorial para TÍTULOS (pantalla y panel). El toque «caro». */
  --serif:"Fraunces",Georgia,"Times New Roman",serif;
  --mono:ui-monospace,"SF Mono","Cascadia Mono","Segoe UI Mono","Roboto Mono",monospace;

  /* --- Densidad (V7 §4.4): afecta el AIRE GENERAL, no solo las tablas — canal
     de la rejilla, cuerpo de panel, banda de cifras y alto de fila. En
     cualquier pantalla se nota el cambio (un conmutador que no produce cambio
     visible donde estás parado está roto en la práctica). El atributo lo fija
     tema.js en <html> y persiste por persona. --- */
  /* V11 · 9px → 14px: la fila cómoda pasa de ~36 px a ~46 px. La densidad es una
     DECISIÓN y esta pantalla la abre un directivo a las 7 de la mañana para
     leerla, no un operador para teclear 300 filas; el modo compacto sigue ahí
     para quien captura. */
  --fila-pad:14px;
  --aire-rejilla:14px;   /* canal entre paneles (las hojas ya flotan: menos canal) */
  --aire-panel:20px;     /* padding del cuerpo de panel */
  --aire-cifra:18px;     /* padding vertical de los bloques de cifra */

  /* --- Color de DATO por token: los JS pintan con var(--…) por CSSOM, así el
     tema oscuro y la capa de mando los vuelven luminosos sin tocar el JS. --- */
  --graf-trazo:#2e5e4e;          /* línea, área y puntos de la serie (verde bosque) */
  --graf-col:#3c7a63;            /* columnas del histograma (verde vivo) */
  --oro-osc:#9c6c10;             /* la mínima institucional dibujada */
  --dato-a:#2e5e4e; --dato-b:#b0532b; --dato-c:#9a6f18;
  --verde-suave:#57b588; --alerta-2:#b06a00;
  /* --- Rampa secuencial de 5 pasos (reparto, apiladas y sus muestras de
     leyenda). 🔴 Re-derivada el 2026-08-17 y cada paso MEDIDO contra la
     superficie MÁS OSCURA del tema claro (`--panel-2` #f7f6f2), que es la peor
     para un azul: antes era `#03045e #2b3488 #5563ad #8791cf #b9c0e8` y los dos
     últimos pasos daban **2,78:1 y 1,65:1** — por debajo del 3:1 que pide un
     gráfico. En una barra apilada el segmento aguanta porque tiene vecinos y
     borde; lo que se perdía era la MUESTRA de la leyenda, un cuadrito de 10 px
     flotando sobre la hoja: la 5.ª categoría se quedaba sin referencia de color
     y la leyenda pasaba a ser una lista. Ya había pasado por otra causa (la CSP
     descartaba el `style=`), y se arregló ahí sin volver a medir los valores.

     Ahora los cinco pasos van de 16,42:1 a 3,06:1 con **1,52:1 exacto entre
     consecutivos** — el reparto más ancho posible sin bajar del 3:1, así que
     ningún paso es invisible y ninguno se confunde con su vecino. El primero
     sigue siendo el navy institucional literal. --- */
  --rampa-1:#143a29; --rampa-2:#1d5c3d; --rampa-3:#276f4a; --rampa-4:#327f57; --rampa-5:#3f8f64;
  --esq-brillo:#edeff5;          /* la veta clara del esqueleto de carga */
}

/* ============================================================================
   0-bis. LAS DOS CAPAS Y EL TEMA OSCURO (V7 §4.2)
   · Capa de trabajo (módulos operativos): los tokens de :root.
   · Capa de mando (las 9 pantallas del Dashboard): navy profundo SIEMPRE.
   · Tema oscuro: aplica a las DOS capas; en la de trabajo re-tinta los neutros,
     la de mando ya es oscura de fábrica.
   El bloque compartido define los neutros/semáforo sobre oscuro (contraste
   MEDIDO: texto 14,7:1 · secundario 7,7:1 · semáforo ≥7:1 · dorado 7,2:1);
   los dos bloques de abajo solo eligen el fondo. `[data-capa]` va DESPUÉS de
   `[data-tema]` a la misma especificidad: en tema oscuro, el mando gana.
   ========================================================================== */
:root[data-tema="oscuro"]{
  --tinta:#e8eaf6;               /* 14,66:1 sobre #111342 */
  --tinta-2:#c0c5e2;
  --tinta-3:#a5aacb;             /* 7,70:1 sobre #111342 */
  --linea:rgba(255,255,255,.10);
  --linea-fuerte:rgba(255,255,255,.20);
  --fila-par:rgba(151,165,238,.07);
  --fila-hover:rgba(151,165,238,.20);
  --realce:rgba(255,255,255,.05);
  --seleccion:rgba(151,165,238,.16);
  --navy-f:rgba(151,165,238,.14);
  --navy-logo:#9db2f5;           /* 8,46:1 — enlaces, barras y acentos, luminosos */
  --oro-txt:#e8b04b;             /* el dorado legible sobre oscuro (8,98:1) */
  --verde-txt:#7fd6ac;           /* 10,13:1 */
  /* El arcoíris en oscuro: los mismos matices, subidos de luminosidad hasta que
     cada uno pasa 7:1 sobre #111342 (medido). Aquí el color SÍ puede escribir,
     así que relleno y texto coinciden — al revés que sobre papel. */
  --app-aula:#9db2f5;          --app-aula-txt:#9db2f5;        /* 8,46:1 */
  --app-secretaria:#7fd6ac;    --app-secretaria-txt:#7fd6ac;  /* 10,13:1 */
  --app-gabi:#e8b04b;          --app-gabi-txt:#e8b04b;        /* 8,98:1 */
  --app-mirador:#b9c0e8;       --app-mirador-txt:#b9c0e8;     /* 9,84:1 */
  --ok:#4cc38a; --alerta:#e8b04b; --riesgo:#ef8b84; --nada:#9aa3c4;   /* ≥7:1 */
  --ok-f:rgba(76,195,138,.14); --alerta-f:rgba(232,176,75,.14);
  --riesgo-f:rgba(239,139,132,.14); --nada-f:rgba(154,163,196,.12);
  --graf-trazo:#7fe3b4;          /* 8,27:1: la línea verde se vuelve luminosa */
  --graf-col:#58c894;            /* 6,17:1 (gráfico: pide 3:1) */
  --oro-osc:#e8b04b;
  --dato-a:#7fe3b4; --dato-b:#e8b04b; --dato-c:#58c894;
  --verde-suave:#7fd6ac; --alerta-2:#d99a2b;
  /* 🔴 La rampa oscura, re-derivada el 2026-08-17 contra la superficie NUEVA.
     Era `#b9c0e8 #97a5ee #7583d6 #5563ad #39447f`, calibrada para el near-black
     de antes; con el lienzo 2,5× más luminoso sus dos últimos pasos cayeron a
     **2,62:1 y 1,60:1** sobre `--panel-2`. Es exactamente la lección que este
     mismo commit escribió sobre el dorado —al cambiar la superficie hay que
     re-medir lo que se apoya en ella— repitiéndose en la misma pasada, y solo
     salió al medir la rampa entera en vez de los tokens que se tocaron.
     De 10,99:1 a 3,05:1, 1,38:1 exacto entre pasos consecutivos. */
  --rampa-1:#cdf3e0; --rampa-2:#9fe4c2; --rampa-3:#74d3a4; --rampa-4:#53bd8a; --rampa-5:#3f9f70;
  --esq-brillo:rgba(255,255,255,.10);
  --flota:0 8px 28px rgba(0,0,0,.55), 0 1px 3px rgba(0,0,0,.4);
  --flota-alta:0 24px 64px rgba(0,0,0,.6), 0 2px 8px rgba(0,0,0,.45);
}
/* Capa de trabajo en tema oscuro: neutros hondos, tintados al navy. */
:root[data-tema="oscuro"]{
  /* V10 · 2026-08-17. Antes: #0d0f1e / #14172b / #191d33 — un near-black casi sin
     matiz (R13 G15 B30) con luminancia 0,0052. Hidalgo: «es muy dark y depresivo».
     Lo era, y medido: el lienzo tenía la mitad de luz que el papel de un tema
     oscuro normal, y de azul institucional no le quedaba nada.

     🔴 El primer intento fue adoptar #0c0c35, que es el azul profundo que el
     colegio usa en las secciones de su propio sitio. Medirlo lo descartó: su
     luminancia es 0,0060, MÁS OSCURO que el que ya había. Copiar un color de la
     marca sin medirlo habría empeorado exactamente la queja. El azul de su web es
     un acento de sección sobre base clara, no un ambiente para trabajar ocho
     horas dentro.

     Estas superficies tienen 2,5× la luz de las anteriores, matiz azul
     inequívoco, y conservan el escalón lienzo→hoja (1,67× contra 1,81×). Todo el
     texto y el semáforo re-medidos sobre el panel nuevo: el peor par queda en
     6,04:1. Lo custodia test_marca_por_aplicacion.py. */
  --lienzo:#171b3e; --panel:#1f2550; --panel-2:#272e5e;
  /* V9 · el riel también necesita sus tokens en oscuro. Antes era navy sólido en
     LOS DOS temas, así que el bloque oscuro no los declaraba; al volverse papel en
     claro, en oscuro se quedaba con texto oscuro sobre fondo oscuro y la marca y
     el pie del riel desaparecían. Misma regla que en claro: el riel es la MISMA
     superficie que el lienzo, separada por una línea de pelo. */
  --riel:#171b3e; --riel-2:#1f2550;
  --riel-txt:#c0c5e2; --riel-txt-2:#a5aacb;
  --riel-linea:rgba(255,255,255,.10);
  --riel-activo:rgba(151,165,238,.14);
  --riel-tinta-act:#fff;
  --riel-fondo-act:rgba(255,255,255,.12);
  --tinta-4:#7a7d83;
}
/* Capa de mando: navy profundo SIEMPRE (en claro y en oscuro). */
/* 🔴 V9 · LA CAPA DE MANDO YA NO ES OSCURA. Antes, entrar al Dashboard cambiaba
   el mundo entero a navy #0b0d34 con degradado: era el patrón «sala de mando»
   —dos temperaturas dentro de una misma aplicación— y es de donde venía la
   sensación de peso que el cliente pidió quitar. Una sola temperatura de
   superficie; la jerarquía la hace el espacio, no el contraste de fondos.
   El atributo `data-capa` se CONSERVA porque el JS lo escribe y lo lee: ahora
   solo gobierna la escala de las cifras, que sí tiene razón de ser (una pantalla
   de dirección se lee de más lejos que un formulario). */
/* Cifras de mando: grandes de verdad (escala V7: 34/48) y con ss01. */
[data-capa="mando"] .cifra .kpi-v{font-size:var(--t-cifra-g)}
[data-capa="mando"] .cifra.principal .kpi-v{font-size:3rem}

/* Los resortes de verdad, solo donde el navegador sabe interpolarlos. La bézier
   de arriba es el respaldo y ya se parece; esto es la física exacta (24 muestras
   del oscilador). Se pregunta por `linear()` con la sintaxis mínima: si el motor
   la parsea, la parsea completa. */
@supports (transition-timing-function:linear(0,1)){
  :root{
    --resorte:linear(0, 0.042, 0.1437, 0.2754, 0.416, 0.5512, 0.6725, 0.7754,
      0.8585, 0.9225, 0.9694, 1.0017, 1.0221, 1.0334, 1.038, 1.0379, 1.0349,
      1.0303, 1.025, 1.0196, 1.0147, 1.0104, 1.0068, 1);
    --resorte-vivo:linear(0, 0.0676, 0.2251, 0.4177, 0.6076, 0.7721, 0.9007,
      0.9913, 1.0475, 1.0758, 1.0835, 1.0778, 1.0646, 1.0485, 1.0327, 1.0189,
      1.0082, 1.0007, 0.996, 0.9937, 0.993, 0.9935, 0.9946, 1);
  }
}

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

/* 🔴 `[hidden]` gana SIEMPRE. Regla general, no parche por elemento: una regla de
   autor con `display` (`.frescura{display:inline-flex}`) le gana al `[hidden]` del
   navegador, así que un `el.hidden = true` desde JS no oculta nada. En este
   proyecto ya pasó TRES veces: la cinta «DATOS DE DEMOSTRACIÓN» apareció sobre
   datos reales (el peor error posible), la portada se mostraba en cada deep-link, y
   el sello «Dato leído —» quedaba colgado en pantallas sin frescura. Con esto, el
   atributo vuelve a significar lo que dice en todo el sistema. */
[hidden]{display:none!important}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--lienzo);color:var(--tinta);
  font:var(--t-sm)/1.5 var(--sans);
  -webkit-font-smoothing:antialiased;
}
h1,h2,h3,h4{margin:0;font-weight:600;letter-spacing:-.011em;line-height:1.25}
p{margin:0}
a{color:inherit;text-decoration:none}
button,input,select,textarea{font:inherit;color:inherit}
svg{display:block}

/* Toda cifra es tabular. Sin esto la columna tiembla al refrescar. */
.n,td.n,th.n,.cifra,.kpi-v,table td,table th{font-variant-numeric:tabular-nums}

:focus-visible{outline:2px solid var(--oro);outline-offset:1px;border-radius:3px}
.cinta :focus-visible{outline-color:var(--oro)}

/* Solo para lectores de pantalla */
.sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* Salto de teclado: primer tabulador de la página */
.saltar{position:absolute;left:-9999px;top:0;z-index:var(--z-dialogo);
  background:var(--navy);color:#fff;padding:10px 16px;border-radius:0 0 var(--r) 0;font-weight:600}
.saltar:focus{left:0}

/* ============================================================================
   1. ARMAZÓN — cinta superior con menús desplegables + contenido
   Decisión del cliente (2026-08-10, DESIGN.md §10): la navegación es una cinta
   superior con un menú por dominio, no el riel lateral de §9. El costo del
   segundo clic se compensa con Ctrl/⌘K y con el módulo activo marcado dentro
   de su menú.
   ========================================================================== */
#app{display:flex;flex-direction:column;min-height:100vh}

.cinta{
  /* z-index por encima de la fila de contexto (--z-barra): el panel del menú es
     HIJO de la cinta, así que su propio z-index no lo saca de este contexto de
     apilamiento. Con ambos en 30, `.contexto` —posterior en el DOM y flex item con
     z-index, que SÍ cuenta aunque sea static— pintaba encima y se comía los dos
     primeros ítems del menú abierto. Se veía en pantalla y no en la geometría:
     medir rectángulos no detecta quién pinta encima. */
  position:sticky;top:0;z-index:var(--z-pop);
  display:flex;align-items:center;gap:var(--e4);
  background:var(--riel);color:var(--riel-txt);
  border-bottom:1px solid rgba(0,0,0,.25);
  padding:0 var(--e5);min-height:52px;
}
.cinta-marca{display:flex;align-items:center;gap:9px;flex:none;text-decoration:none}
.cinta-marca .escudo{background:var(--panel);border:1px solid var(--linea);border-radius:var(--r);padding:3px 5px;display:flex;flex:none}
.cinta-marca .escudo img{height:20px;width:auto;display:block}
.cinta-marca .txt{line-height:1.15;min-width:0}
.cinta-marca .txt b{display:block;font-size:var(--t-sm);font-weight:650;color:var(--tinta);
  font-family:var(--display);font-stretch:88%;letter-spacing:-.015em}
.cinta-marca .txt span{display:block;font-size:var(--t-2xs);color:var(--riel-txt-2);letter-spacing:.02em}

/* Los menús ------------------------------------------------------------------ */
.menus{
  display:flex;align-items:stretch;gap:2px;flex:1;min-width:0;
  /* Sin esto los botones (white-space:nowrap) desbordan y se pintan ENCIMA del
     buscador. Recortar con overflow:hidden escondería un dominio entero, así que
     la cinta se desplaza: no se pierde ningún destino. */
  overflow-x:auto;overflow-y:hidden;scrollbar-width:none;
}
.menus::-webkit-scrollbar{display:none}
.menu-b{
  display:flex;align-items:center;gap:6px;background:none;border:0;cursor:pointer;
  color:var(--riel-txt);font:inherit;font-size:var(--t-sm);padding:0 11px;height:52px;
  border-bottom:2px solid transparent;white-space:nowrap;
  transition:color var(--rapido) linear,background var(--rapido) linear,transform var(--rapido) var(--sale);
}
.menu-b:hover{color:var(--tinta);background:var(--realce)}
.menu-b .fl{width:9px;height:9px;opacity:.55;transition:transform var(--rapido) ease-out}
.menu-b[aria-expanded="true"]{color:var(--tinta);background:var(--realce)}
.menu-b[aria-expanded="true"] .fl{transform:rotate(180deg)}
.menu-b.activo{color:#fff;font-weight:600;border-bottom-color:var(--oro)}
.menu-b .cnt{
  font-size:var(--t-2xs);font-weight:700;padding:1px 6px;border-radius:var(--r-full);
  background:var(--riesgo);color:#fff;font-variant-numeric:tabular-nums;
}

/* Panel del menú: `fixed` a propósito — en el flujo, un `absolute` lo recorta el
   primer ancestro con overflow (§11 tiene varios). */
.menu-pop{
  position:fixed;z-index:var(--z-pop);min-width:264px;max-width:340px;
  background:var(--panel);border:1px solid var(--linea-fuerte);border-radius:var(--r);
  box-shadow:0 12px 32px -8px rgba(3,4,94,.28),0 2px 6px rgba(3,4,94,.10);
  padding:5px;max-height:min(74vh,560px);overflow-y:auto;overscroll-behavior:contain;
}
.menu-pop[hidden]{display:none}
.menu-pop .gt{
  padding:7px 9px 4px;font-size:var(--t-2xs);font-weight:700;letter-spacing:.09em;
  text-transform:uppercase;color:var(--tinta-3);
}
.menu-it{
  display:flex;align-items:flex-start;gap:9px;width:100%;text-align:left;
  padding:7px 9px;border-radius:var(--r);color:var(--tinta);font-size:var(--t-sm);
  text-decoration:none;cursor:pointer;border:0;background:none;
}
.menu-it:hover,.menu-it:focus-visible{background:var(--panel-2)}
.menu-it .ic{width:15px;height:15px;flex:none;margin-top:2px;color:var(--navy-logo);opacity:.8}
.menu-it .tx{min-width:0;flex:1}
.menu-it .tx b{display:block;font-weight:550;line-height:1.3}
.menu-it .tx i{display:block;font-style:normal;font-size:var(--t-2xs);color:var(--tinta-3);
  line-height:1.35;margin-top:1px}
.menu-it[aria-current="page"]{background:var(--navy-f)}
.menu-it[aria-current="page"] .tx b{font-weight:700;color:var(--navy-logo)}
.menu-it[aria-current="page"]::after{content:"●";margin-left:auto;color:var(--oro);font-size:9px;line-height:1.6}
.menu-it .cnt{
  flex:none;font-size:var(--t-2xs);font-weight:700;padding:1px 6px;border-radius:var(--r-full);
  background:var(--riesgo-f);color:var(--riesgo);font-variant-numeric:tabular-nums;
}

.cinta-der{display:flex;align-items:center;gap:var(--e3);flex:none}
.sesion{display:flex;align-items:center;gap:8px;font-size:var(--t-xs);
  padding-left:var(--e3);border-left:1px solid var(--riel-linea)}
.sesion .avatar{
  width:25px;height:25px;border-radius:var(--r-full);flex:none;display:grid;place-items:center;
  background:var(--seleccion);color:var(--navy-logo);font-weight:700;font-size:var(--t-2xs);
}
.sesion .quien{min-width:0;line-height:1.25}
.sesion .quien b{display:block;color:var(--tinta);font-weight:600;font-size:var(--t-2xs);
  max-width:130px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sesion .quien span{display:block;color:var(--riel-txt-2);font-size:var(--t-2xs);text-transform:capitalize}
.sesion .salir{background:none;border:0;color:var(--riel-txt-2);cursor:pointer;padding:4px;border-radius:var(--r)}
.sesion .salir:hover{color:var(--tinta);background:var(--realce)}

.hamb{display:none;background:none;border:1px solid var(--linea-fuerte);border-radius:var(--r);
  color:var(--tinta-2);cursor:pointer;padding:6px;flex:none}

/* ============================================================================
   2. FILA DE CONTEXTO — qué pantalla es, qué pregunta responde y sus filtros.
   Los filtros son de verdad: viajan al servidor y él los honra.
   ========================================================================== */
.contexto{
  position:sticky;top:52px;z-index:var(--z-barra);
  display:flex;align-items:flex-end;gap:var(--e4);flex-wrap:wrap;
  background:var(--panel);border-bottom:1px solid var(--linea);
  padding:var(--e3) var(--e5);
}
.ctx-izq{min-width:0;flex:1}
.miga{display:flex;align-items:center;gap:6px;font-size:var(--t-2xs);color:var(--tinta-3);margin-bottom:2px}
.miga b{color:var(--navy-logo);font-weight:600}
.miga .sep{opacity:.45}
.miga button{background:none;border:0;padding:0;color:inherit;font:inherit;cursor:pointer}
.miga button:hover{color:var(--navy-logo);text-decoration:underline}
/* V11 · 22 px → 34 px. Un título de pantalla a 18-22 px con el texto a 14 no
   encabeza nada: la pantalla se leía como una lista de cosas del mismo peso. */
.contexto h1{font-family:var(--serif);font-size:var(--t-2xl);color:var(--tinta);font-weight:600;line-height:1.04}
.pregunta{font-size:var(--t-xs);color:var(--tinta-2);margin-top:2px;max-width:74ch}

.filtros{display:flex;align-items:flex-end;gap:var(--e3);flex-wrap:wrap;flex:none}
.filtro{display:flex;flex-direction:column;gap:3px}
.filtro > span{font-size:var(--t-2xs);font-weight:700;letter-spacing:.06em;
  text-transform:uppercase;color:var(--tinta-3)}
.filtro select{min-width:132px}
.filtro.limpiar{align-self:flex-end}

/* Buscador / paleta de comandos: disparador */
.buscar-abrir{
  display:flex;align-items:center;gap:8px;min-width:196px;
  background:rgba(255,255,255,.10);border:1px solid rgba(255,255,255,.18);border-radius:var(--r);
  padding:6px 9px;font-size:var(--t-xs);color:var(--riel-txt);cursor:pointer;text-align:left;
  transition:border-color var(--rapido) linear,background var(--rapido) linear,transform var(--rapido) var(--sale);
}
.buscar-abrir:hover{border-color:rgba(255,255,255,.34);background:rgba(255,255,255,.15)}
.buscar-abrir .ic{width:14px;height:14px;flex:none;opacity:.75}
.buscar-abrir .kbd{margin-left:auto}
.kbd{
  font:600 var(--t-2xs)/1 var(--sans);color:var(--tinta-3);
  background:var(--panel);border:1px solid var(--linea-fuerte);border-bottom-width:2px;
  border-radius:3px;padding:3px 5px;white-space:nowrap;
}
.cinta .kbd{background:var(--riel-fondo-act);border-color:var(--riel-linea);
  color:var(--riel-tinta-act)}

/* Frescura del dato: elemento de primera clase (DESIGN.md §6) */
.frescura{
  display:inline-flex;align-items:center;gap:6px;font-size:var(--t-2xs);
  color:var(--tinta-3);white-space:nowrap;
}
.frescura .pt{width:7px;height:7px;border-radius:50%;background:var(--ok);flex:none;
  box-shadow:0 0 0 3px var(--ok-f)}
.frescura.rancio .pt{background:var(--alerta);box-shadow:0 0 0 3px var(--alerta-f)}

/* ============================================================================
   3. LIENZO Y REJILLA
   ========================================================================== */
main{padding:var(--e5);flex:1;min-width:0}
/* 🔴 `align-items:stretch`, no `start`. Con `start` los paneles de una fila NO se
   estiran: en Histórico uno medía 286 px y su vecino 344, así que su línea de cierre
   quedaba a 41 px de diferencia y la fila se leía como dos piezas sueltas en vez de
   una fila. Medido con `tools/detector_descuadre.py`.
   Estirar solo alinea si además el pie del panel está anclado al fondo — las dos
   reglas van juntas o ninguna sirve. */
.rejilla{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:var(--aire-rejilla);align-items:stretch}
.col-3{grid-column:span 3}.col-4{grid-column:span 4}.col-5{grid-column:span 5}
.col-6{grid-column:span 6}.col-7{grid-column:span 7}.col-8{grid-column:span 8}
.col-9{grid-column:span 9}.col-12{grid-column:1/-1}

/* ============================================================================
   4. PANEL — el contenedor. Borde de 1px, sin sombra, sin levitación.
   ========================================================================== */
/* V11 · la hoja flota. El borde de 1 px pasa al anillo interior de `--lift`, así
   que la caja sigue teniendo un límite nítido pero deja de estar DIBUJADA: la
   jerarquía la hace la elevación. Es la diferencia entre un panel de
   administración y una aplicación, y no cuesta ni un píxel de contenido. */
/* 🔴 COLUMNA con el pie anclado al fondo. La rejilla ya estira los paneles de una
   fila al mismo alto, pero el contenido caía de arriba abajo y el pie quedaba justo
   debajo del cuerpo: dos paneles vecinos con cuerpos de distinto alto tenían su pie a
   41 px de diferencia (medido en Histórico con `tools/detector_descuadre.py`). Con el
   pie al fondo, la línea de cierre de los paneles de una fila coincide — que es lo que
   hace que una rejilla se lea como una rejilla y no como piezas sueltas. */
.panel{
  background:var(--panel);border:0;border-radius:var(--r-xl);box-shadow:var(--lift);
  min-width:0;overflow:hidden;
  display:flex;flex-direction:column;
}
.panel > .panel-pie{margin-top:auto}
.panel-cab{
  display:flex;align-items:center;gap:var(--e3);
  padding:15px var(--e5) 12px;border-bottom:1px solid var(--linea);background:var(--panel);
}
.panel-cab .tt{min-width:0;flex:1}
.panel-cab h2{font-size:var(--t-lg);font-weight:600;color:var(--tinta);letter-spacing:-.015em}
.panel-cab .sub{font-size:var(--t-xs);color:var(--tinta-3);margin-top:2px}
.panel-cab .acc{display:flex;align-items:center;gap:var(--e2);flex:none}
/* 🔴 El cuerpo ABSORBE el alto sobrante del panel.
   Sin esto, en una fila de dos paneles el más corto deja su hueco JUSTO ENCIMA del pie,
   porque `.panel-pie` lleva `margin-top:auto` y se va al fondo. Medido en Histórico:
   «Tamaño medio de paralelo» dejaba 58 px de blanco entre su cuerpo y su pie mientras
   «Retención año a año», del mismo alto, dejaba 0. Con `flex:1` el sobrante se lo queda
   el cuerpo —donde vive el gráfico— en vez de convertirse en una franja vacía sobre el
   pie. `min-height:0` va con él: sin eso un hijo que desborda ignora el reparto. */
.panel-cuerpo{padding:var(--aire-panel); flex:1 1 auto; min-height:0}
/* Y lo mismo para la tabla que cuelga DIRECTA del panel, sin `.panel-cuerpo` de por
   medio: en Convivencia el panel «Conducta» mide 639 px para igualar a su vecino y su
   tabla dejaba 129 px de blanco antes del pie. La regla de arriba no la alcanzaba
   porque no es un `.panel-cuerpo`. */
.panel > .tabla-marco{flex:1 1 auto; min-height:0}
.panel-cuerpo.sin-aire{padding:0}
/* 🔴 BLOQUE, no flex. Era `display:flex` con `gap`, y un flex convierte en ítem
   propio cada trozo de texto y cada etiqueta en línea: la frase «Solo entre años
   lectivos <b>consecutivos</b>: los años que faltan…» se partía en tres cajas
   separadas por el hueco y salía impreso «consecutivos : los años», con el signo de
   puntuación despegado. Se veía en cinco pies por lo menos.
   Alguien lo había tapado envolviendo UN pie en un <span>; el arreglo es éste, porque
   ninguno de los catorce pies del sistema quiere una fila: todos son una frase. */
.panel-pie{
  padding:8px var(--e4);border-top:1px solid var(--linea);background:var(--panel-2);
  font-size:var(--t-2xs);color:var(--tinta-3);line-height:1.5;
}
.panel-pie b{color:var(--navy-logo);font-weight:650}

/* ============================================================================
   5. CIFRAS — banda de KPI. Sin tarjeta por número: una banda, celdas divididas.
   ========================================================================== */
/* ─── V11 · LOS BLOQUES ──────────────────────────────────────────────────────
   Antes: una banda blanca dividida en celdas por líneas de 1 px, con la cifra en
   tinta y un rótulo gris encima. Cuatro celdas del mismo peso = ningún foco, y
   96 % de la pantalla en neutro. Eso es lo que se reportó como «plano», «no es
   nada impactante» y «no se entiende de un vistazo qué hacer», tres veces.

   Ahora cada medida es un BLOQUE de color con la cifra en blanco a 52 px. Es el
   cambio que se ve de lejos, que era el requisito.

   🔴 De dónde sale el color de cada bloque, porque esto es lo que puede volverse
   un semáforo falso: el color NO es el de la aplicación (DESIGN.md §10 lo
   prohíbe para cualquier dato: el verde de Secretaría y el `--ok` se distinguen
   en ΔE 2,1, o sea no se distinguen, y una cifra verde anuncia «al día» sin que
   nadie lo haya declarado). El color es el ESTADO que el servidor ya declaró en
   `tono`: sin tono, azul estructural; con tono, el color del semáforo del §4. Y
   el bloque principal es SIEMPRE navy `#03045e` — el navy no es ningún estado del
   semáforo, así que no puede leerse como uno; es «esta es la cifra que manda».
   Blanco sobre cada relleno, medido: navy 17,75:1 · azul 8,21 · riesgo 6,54 ·
   oro 5,29 · verde 5,36. Todos por encima de 4,5.
   El rótulo y el pie viven DENTRO del bloque en blanco translúcido, no en gris:
   gris sobre color saturado es la forma más rápida de perder un rótulo. */
/* 🔴 `:not(.instrumento)` NO es un detalle de alcance: es la corrección de un
   error que ya se cometió y se vio en pantalla. Sin él, «Resumen de dirección»
   —que es UNA placa navy porque es la única superficie donde el dorado puede
   escribir (§17)— se partió en CINCO bloques saturados: promedio institucional en
   verde, retención en rojo, captura en verde… un arcoíris de semáforos donde
   cuatro de los cinco no eran estados que nadie declarara, y con los rótulos del
   instrumento en rojo oscuro sobre rojo. La placa se queda como estaba: un solo
   plano, un solo foco. Los bloques de color son para las bandas normales. */
.cifras:not(.instrumento){
  display:grid;grid-template-columns:repeat(auto-fit,minmax(178px,1fr));
  gap:var(--e3);background:none;border:0;border-radius:0;overflow:visible;
}
.cifras:not(.instrumento) > .cifra{
  padding:18px 20px 17px;min-width:0;border-left:0;border-radius:var(--r-2xl);
  background:var(--azul-bloque);color:#fff;box-shadow:var(--lift);position:relative;
}
.cifras:not(.instrumento) > .cifra:has(.kpi-v.riesgo){background:var(--riesgo-bloque)}
.cifras:not(.instrumento) > .cifra:has(.kpi-v.alerta){background:var(--alerta-bloque)}
.cifras:not(.instrumento) > .cifra:has(.kpi-v.ok){background:var(--ok-bloque)}
/* 🔴 El principal va DESPUÉS y con dos clases: si va antes, `:has(.kpi-v.ok)`
   —misma especificidad, más abajo en el archivo— le gana y el bloque de la cifra
   que manda sale VERDE. Se vio en la primera captura: «Captura del parcial 99,5 %»
   en verde, que es el semáforo diciendo «al día» sobre un dato que nadie declaró
   como estado. Justo el fallo que estos bloques tenían que evitar. */
/* 🔴 La clase va DOS VECES y no es un tic: es aritmética de especificidad. El
   selector del tono es `.cifras:not(.instrumento) > .cifra:has(.kpi-v.ok)`, y
   `:has()` pesa lo que su argumento más específico —`.kpi-v.ok`, dos clases—, así
   que suma (0,5,0) contra los (0,4,0) del principal: el tono ganaba aunque el
   principal fuera después, y el bloque de la cifra que manda salía VERDE.
   Repetir la clase lo lleva a (0,5,0) y, empatados, gana el que va después.
   `test_bloques_cifra.py` lo mide con el caso que falló: principal + tono `ok`. */
.cifras:not(.instrumento) > .cifra.principal.principal{
  background:var(--navy-bloque);grid-column:span 2}
/* Con CUATRO o más medidas el principal deja el doble ancho: a 1920 px eso le dejaba
   ~640 px de navy para una cifra de 52, y un bloque medio vacío se lee como un error
   de maquetación y no como énfasis. El énfasis con cuatro bloques ya lo dan el color
   y la posición. Se cuenta con `:has` sobre el cuarto hijo — el CSS sí sabe contar
   hermanos, y así no hace falta que el JS decida el ancho. */
.cifras:not(.instrumento):has(> .cifra:nth-child(4)) > .cifra.principal.principal{
  grid-column:auto}
.cifras:not(.instrumento) .kpi-k{
  font-size:var(--t-2xs);font-weight:650;letter-spacing:.09em;text-transform:uppercase;
  color:rgba(255,255,255,.80);display:block;margin-bottom:9px;
}
.cifra .kpi-v{
  font-family:var(--cifras);font-size:var(--t-cifra);font-weight:600;letter-spacing:-.03em;
  line-height:1;display:flex;align-items:baseline;gap:5px;flex-wrap:wrap;
}
.cifras:not(.instrumento) .kpi-v{color:#fff}
/* La cifra NUNCA se parte, aunque el bloque sea estrecho: antes se veía «$26 /
   400». Si no cabe, se encoge la unidad, no el número. */
.cifra .kpi-v > span:first-child{white-space:nowrap}
.cifra .kpi-v .u-larga{flex-basis:100%;margin-top:6px}
.cifras:not(.instrumento) .cifra.principal .kpi-v{font-size:var(--t-cifra-g)}
.cifras.instrumento .cifra.principal .kpi-v{font-size:var(--t-2xl)}
.cifras:not(.instrumento) .kpi-v.ok,.cifras:not(.instrumento) .kpi-v.riesgo,
.cifras:not(.instrumento) .kpi-v.alerta{color:#fff}
.cifra .kpi-v .u{font-family:var(--sans);font-size:var(--t-md);font-weight:500;letter-spacing:0}
.cifras:not(.instrumento) .kpi-v .u{color:rgba(255,255,255,.78)}
.cifra .kpi-pie{font-size:var(--t-xs);margin-top:9px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cifras:not(.instrumento) .kpi-pie{color:rgba(255,255,255,.82)}
.cifras:not(.instrumento) .delta,.cifras:not(.instrumento) .delta.sube,
.cifras:not(.instrumento) .delta.baja,.cifras:not(.instrumento) .delta.igual{color:rgba(255,255,255,.92)}
@media (max-width:700px){ .cifras:not(.instrumento) > .cifra.principal{grid-column:span 1} }

/* ─── PESTAÑAS DEL GRUPO (V11 fase 2) ────────────────────────────────────────
   14 entradas de menú pasaron a 5 grupos con pestañas dentro, al estilo del
   «Mensual / Acumulado» de Zeus. La fila va pegada al borde inferior de la banda de
   contexto —es parte del encabezado de la pantalla, no del contenido— y con el mismo
   inset lateral que los paneles, para no repetir el desfase de 20 px de la banda
   secundaria. */
#pestanas-grupo:empty{display:none}
.pestanas-grupo{
  display:flex;gap:2px;margin:0 var(--e6) calc(var(--aire-rejilla) * -0.5);
  padding:0;background:none;box-shadow:none;border-bottom:1px solid var(--linea);
}
.pestanas-grupo button{
  appearance:none;background:none;border:0;cursor:pointer;
  font:550 var(--t-md)/1 var(--sans);color:var(--tinta-3);
  padding:11px var(--e4) 12px;border-radius:0;border-bottom:2px solid transparent;
  margin-bottom:-1px;transition:color var(--rapido) linear;
}
.pestanas-grupo button:hover{color:var(--tinta)}
/* La pestaña activa se marca con la LÍNEA de abajo y el peso, no con un relleno: un
   relleno la convierte en un botón y deja de leerse como «estás aquí». */
.pestanas-grupo button[aria-pressed="true"]{
  color:var(--tinta);font-weight:650;border-bottom-color:var(--navy-logo);background:none;
}
.pestanas-grupo button:focus-visible{outline:2px solid var(--navy-logo);outline-offset:-2px}
@media (max-width:700px){
  /* 🔴 `overflow-x:auto` solo sirve si la fila tiene un ancho que no pueda pasar: con
     tres pestañas (Matrículas históricas, 2026-09-25) la fila medía 399 px en 390 y
     empujaba la PÁGINA. `max-width` la ata al ancho disponible y el desplazamiento
     queda dentro de la fila. */
  .pestanas-grupo{margin-inline:var(--e3);overflow-x:auto;scrollbar-width:none;
    max-width:calc(100% - 2 * var(--e3));min-width:0}
  .pestanas-grupo button{white-space:nowrap;padding-inline:var(--e3)}
}

/* ─── LA BANDA SECUNDARIA: dentro de un panel, encabezando SU tabla ──────────
   Reporte de Hidalgo sobre «Profesores»: «esos cuadros se ven muy grandes y
   desfasados». Las dos cosas, medidas a 1920 px:

     · alto del bloque 112,5 px con la cifra a 52 px — en un panel cuyo punto son
       70 filas de tabla, la banda pesaba más que el contenido;
     · el rótulo del bloque arrancaba en x=304 y la tabla en x=284: **20 px** de
       diferencia entre dos cosas apiladas en el mismo panel. Es el mismo defecto
       que los 4 px de la cabecera de GABI: casi coincide, y por eso se lee torcido.

   La causa es que la V11 dio a TODA banda el tratamiento de la banda HÉROE —la que
   responde la pregunta de la pantalla, con su cifra de 52 px y su bloque a doble
   ancho—. Una banda dentro de un panel no es esa: encabeza una tabla, así que va
   compacta y **a ras** del contenido que encabeza (mismo inset lateral que las
   celdas, cero radio, sin gap), y la cifra baja a 26. El color se queda: sigue
   diciendo el estado que el servidor declaró. */
.panel-cuerpo .cifras:not(.instrumento){gap:0;border-radius:0;overflow:hidden}
.panel-cuerpo .cifras:not(.instrumento) > .cifra{
  padding:13px var(--e4);border-radius:0;box-shadow:none;
  border-left:1px solid rgba(255,255,255,.18);
}
.panel-cuerpo .cifras:not(.instrumento) > .cifra:first-child{border-left:0}
/* Sin doble ancho: en una banda de tres celdas, la primera al doble descuadra la
   rejilla contra las columnas de la tabla que va debajo. */
.panel-cuerpo .cifras:not(.instrumento) > .cifra.principal.principal{grid-column:auto}
.panel-cuerpo .cifras:not(.instrumento) .kpi-v{font-size:var(--t-cifra)}
.panel-cuerpo .cifras:not(.instrumento) .cifra.principal .kpi-v{font-size:var(--t-cifra)}
.panel-cuerpo .cifras:not(.instrumento) .kpi-k{margin-bottom:5px}
.panel-cuerpo .cifras:not(.instrumento) .kpi-pie{margin-top:5px}

/* Variación contra el período anterior: signo + flecha, nunca solo color */
.delta{display:inline-flex;align-items:center;gap:2px;font-size:var(--t-xs);font-weight:650;
  letter-spacing:0;font-variant-numeric:tabular-nums}
.delta.sube{color:var(--ok)} .delta.baja{color:var(--riesgo)} .delta.igual{color:var(--tinta-3)}
.delta::before{font-size:.85em}
/* La ESCALA del promedio, pegada al número. Tiene que verse subordinada —es el
   denominador, no otra cifra— pero legible: es lo que impide leer un 16,68 sobre 20
   como si fuera peor que un 9,02 sobre 10. */
.uni{font-size:.8em;font-weight:500;color:var(--tinta-3);letter-spacing:0}

/* ─── BANDA DE AMBIENTE DEL SRI ──────────────────────────────────────────────
   Persistente y a ancho completo, porque la pantalla de pruebas y la de producción
   son idénticas y confundirlas es emitir un documento fiscal real creyendo que se
   estaba ensayando. PRUEBAS en gris y PRODUCCIÓN en rojo: el color no decora, es la
   única diferencia visible entre las dos. */
.sri-ambiente{
  grid-column:1/-1;padding:10px var(--e4);border-radius:var(--r);
  font-size:var(--t-xs);letter-spacing:.02em;
  border:1px solid var(--linea);background:var(--panel-2);color:var(--tinta-2);
}
.sri-ambiente b{letter-spacing:.08em}
/* `--riesgo-f` en vez de un color-mix propio: ese token ya tiene su variante para el
   tema oscuro, y un fondo inventado aquí se vería mal en la mitad de las pantallas. */
.sri-ambiente.prod{
  border-color:var(--riesgo);background:var(--riesgo-f);
  color:var(--riesgo);font-weight:600;
}
.sri-zona{margin-top:var(--e3)}
/* Acciones por comprobante (RIDE · pedir OK · emitir): en fila, sin partir el botón. */
td.acciones{white-space:nowrap}
td.acciones .btn+.btn{margin-left:var(--e1,4px)}
.delta.sube::before{content:"▲"} .delta.baja::before{content:"▼"} .delta.igual::before{content:"—"}

/* ============================================================================
   6. TABLA — la pieza principal de este sistema
   ========================================================================== */
.tabla-marco{overflow:auto;max-height:none}
.tabla-marco.alto{max-height:520px}
table.tabla{border-collapse:separate;border-spacing:0;width:100%;font-size:var(--t-sm)}
.tabla thead th{
  position:sticky;top:0;z-index:1;
  /* Navy institucional con texto blanco, como la del Rector: la cabecera tiene que
     leerse como el borde superior de la tabla, no como otra fila más. */
  background:var(--navy);color:#fff;
  font-size:var(--t-2xs);font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  text-align:left;padding:10px var(--e4);white-space:nowrap;
  border-bottom:0;
}
.tabla thead th.n{text-align:right}
/* Cabecera de GRUPO (abarca dos columnas): centrada sobre las suyas, o parece
   el rótulo de la columna de la derecha. */
.tabla thead th.grupo{text-align:center}
.tabla thead th.orden{cursor:pointer;user-select:none}
.tabla thead th.orden:hover{color:#fff;text-decoration:underline}
.tabla thead th.orden::after{content:"";display:inline-block;width:0;height:0;margin-left:5px;
  border:3px solid transparent;vertical-align:middle;opacity:0}
.tabla thead th.orden[aria-sort="ascending"]::after{border-bottom-color:var(--navy-logo);opacity:1;margin-bottom:3px}
.tabla thead th.orden[aria-sort="descending"]::after{border-top-color:var(--navy-logo);opacity:1;margin-top:3px}
.tabla tbody td{
  /* --fila-pad = densidad conmutable (cómoda 9px→fila ~36px, compacta 5px→~28px) */
  padding:var(--fila-pad) var(--e4);border-bottom:1px solid var(--linea);
  color:var(--tinta);vertical-align:middle;
}
.tabla tbody td.n{text-align:right}
.tabla tbody td.suave{color:var(--tinta-3)}
/* Filas intercaladas. Van con `nth-child` sobre las filas VISIBLES de la tabla; la
   paginación reconstruye el tbody, así que no hace falta recalcular nada al pasar de
   página. */
.tabla tbody tr:nth-child(even) td{background:var(--fila-par)}
.tabla tbody tr:hover td{background:var(--fila-hover)}
.tabla tbody tr.sel td{background:var(--seleccion)}
.tabla tbody tr:last-child td{border-bottom:0}
/* El total se queda pegado al pie: en una tabla con scroll propio, un tfoot que
   se va con el desplazamiento es un total que nadie ve. */
.tabla tfoot td{
  position:sticky;bottom:0;z-index:2;
  padding:7px var(--e3);border-top:1px solid var(--linea-fuerte);background:var(--panel-2);
  font-weight:700;color:var(--tinta);
}
.tabla tfoot td.n{text-align:right}
.tabla .fijo{position:sticky;left:0;background:var(--panel);z-index:2}
.tabla thead .fijo{z-index:3;background:var(--panel-2)}
.tabla tbody tr:nth-child(even) .fijo{background:var(--fila-par)}
.tabla tbody tr:hover .fijo{background:var(--fila-hover)}
.tabla.compacta tbody td{padding:3px var(--e3)}
.tabla .idx{color:var(--tinta-3);font-variant-numeric:tabular-nums;text-align:right;width:1%;
  padding-right:var(--e2);font-size:var(--t-2xs)}
.tabla .nom{font-weight:550}
.tabla td.destaca{font-weight:700}
.tabla td.mal{color:var(--riesgo);font-weight:650}
.tabla td.bien{color:var(--ok);font-weight:650}

/* Barra dentro de la celda: compara sin gastar un gráfico entero */
.barra-celda{display:flex;align-items:center;gap:var(--e2);justify-content:flex-end}
.barra-celda .pista{width:64px;height:7px;background:var(--panel-2);border-radius:2px;overflow:hidden;flex:none;
  border:1px solid var(--linea)}
/* display:block obligatorio: es un <span> y su padre no es flex; en inline el
   ancho no se aplica y la barra no se dibuja nunca. */
.barra-celda .relleno{display:block;height:100%;background:var(--instr-val);border-radius:1px}
.barra-celda .relleno.mal{background:var(--riesgo)}
.barra-celda .relleno.bien{background:var(--ok)}
.barra-celda .relleno.alerta{background:var(--alerta)}

/* Dos filas de cabecera (grupo arriba, V/M debajo). Las dos son `sticky`, así
   que sin este `top` la segunda se pega al mismo borde que la primera y la de
   arriba la tapa al desplazar. 27px = 7+7 de relleno + la línea de texto. */
.tabla.dos-cabeceras thead tr:last-child th{top:27px}

/* Barra de herramientas de tabla: búsqueda, filtros, conteo */
.tabla-herr{
  display:flex;align-items:center;gap:var(--e2);flex-wrap:wrap;
  padding:var(--e2) var(--e4);border-bottom:1px solid var(--linea);background:var(--panel);
}
.tabla-herr .cuenta{margin-left:auto;font-size:var(--t-2xs);color:var(--tinta-3);white-space:nowrap;
  font-variant-numeric:tabular-nums}
.tabla-herr .cuenta b{color:var(--tinta);font-weight:650}

/* Paginación: 879 estudiantes NO se vuelcan de un tirón */
.pagina{
  display:flex;align-items:center;gap:var(--e2);padding:7px var(--e4);
  border-top:1px solid var(--linea);background:var(--panel-2);font-size:var(--t-2xs);color:var(--tinta-3);
}
.pagina .rango{font-variant-numeric:tabular-nums}
.pagina .rango b{color:var(--tinta);font-weight:700}
.pagina .ctrl{margin-left:auto;display:flex;gap:var(--e1)}

/* ============================================================================
   7. CONTROLES — vocabulario único. Nada de widgets nativos sin vestir.
      El <select> se ESTILA, no se reinventa: sigue siendo un select nativo y
      por eso funciona con teclado, con lector de pantalla y en el móvil.
   ========================================================================== */
.campo{display:inline-flex;flex-direction:column;gap:3px;min-width:0}
.campo > label,.campo > span,.et-campo{
  font-size:var(--t-2xs);font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--tinta-3);
}
.campo.fila{flex-direction:row;align-items:center;gap:var(--e2)}
.campo.fila > label,.campo.fila > span{text-transform:none;letter-spacing:0;font-size:var(--t-xs);color:var(--tinta-2)}

.select,.input,.filtro select{
  appearance:none;-webkit-appearance:none;
  background:var(--panel);border:1px solid var(--linea-fuerte);border-radius:var(--r);
  padding:5px 9px;font-size:var(--t-xs);color:var(--tinta);line-height:1.5;
  min-height:30px;max-width:100%;
  transition:border-color var(--rapido) linear,box-shadow var(--rapido) linear;
}
.select,.filtro select{
  padding-right:26px;cursor:pointer;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23646C85' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 9px center;
}
.select:hover,.input:hover,.filtro select:hover{border-color:#a9b1c4}
.select:focus,.input:focus,.filtro select:focus{outline:0;border-color:var(--navy-logo);box-shadow:0 0 0 3px rgba(49,74,151,.16)}
/* `background-color`, NO el atajo `background`: el atajo reseteaba
   `background-repeat` a `repeat`, y como el tema oscuro vuelve a poner la flecha
   del select, se repetía en mosaico (el zigzag). Con `background-color` se
   conserva el `no-repeat` de la regla base y la flecha sale UNA vez. */
.select:disabled,.input:disabled,.filtro select:disabled{background-color:var(--panel-2);color:var(--tinta-3);cursor:not-allowed;border-color:var(--linea)}
.input::placeholder{color:var(--tinta-3)}
.input.busca{
  padding-left:28px;min-width:190px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23646C85' stroke-width='2.2' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='M20 20l-3.6-3.6'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:9px center;
}
.input.nota{width:64px;text-align:right;font-variant-numeric:tabular-nums}

.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:6px;
  border:1px solid transparent;border-radius:var(--r);cursor:pointer;
  padding:5px 12px;min-height:30px;font-size:var(--t-xs);font-weight:600;white-space:nowrap;
  transition:background var(--rapido) linear,border-color var(--rapido) linear,color var(--rapido) linear,
    transform var(--rapido) var(--sale);
}
.btn .ic{width:14px;height:14px;flex:none}
.btn-1{background:var(--navy);color:#fff}
.btn-1:hover{background:#0a0c7a}
.btn-1:active{background:#020347}
.btn-2{background:var(--panel);color:var(--tinta);border-color:var(--linea-fuerte)}
.btn-2:hover{background:var(--panel-2);border-color:#a9b1c4}
.btn-3{background:transparent;color:var(--tinta-2)}
.btn-3:hover{background:var(--realce);color:var(--tinta)}
.btn-riesgo{background:var(--riesgo);color:#fff}
.btn-riesgo:hover{background:#96201a}
.btn:disabled{opacity:.5;cursor:not-allowed;pointer-events:none}
.btn.cargando{position:relative;color:transparent}
.btn.cargando::after{
  content:"";position:absolute;width:13px;height:13px;border-radius:50%;
  border:2px solid currentColor;border-top-color:transparent;
  animation:gira .7s linear infinite;color:#fff;
}
.btn-2.cargando::after,.btn-3.cargando::after{color:var(--tinta-2)}
@keyframes gira{to{transform:rotate(360deg)}}
.btn-ico{padding:5px;width:30px}

/* Control segmentado: 2-4 vistas de lo mismo */
.seg{display:inline-flex;background:var(--panel-2);border:1px solid var(--linea);
  border-radius:var(--r);padding:2px;gap:2px}
.seg button{
  background:none;border:0;border-radius:3px;cursor:pointer;padding:4px 11px;
  font-size:var(--t-xs);font-weight:600;color:var(--tinta-3);
  transition:background var(--rapido) linear,color var(--rapido) linear,transform var(--rapido) var(--sale);
}
.seg button:hover{color:var(--tinta)}
.seg button[aria-pressed="true"]{background:var(--panel);color:var(--navy);
  box-shadow:0 1px 2px rgba(6,10,50,.10)}


/* Formulario corto: campos en fila que se apilan solos. La acción va al final
   y NUNCA se estira: un botón de ancho completo parece una barra de progreso. */
.forma{display:flex;flex-wrap:wrap;gap:var(--e3);align-items:flex-end}
.forma .campo{min-width:190px;flex:1 1 190px}
/* 🔴 Las cabeceras de bloque de la ficha de admisión ocupan la FILA ENTERA. `.forma`
   es un flex que envuelve, así que una cabecera suelta se colocaba como un campo más
   —en medio de la fila, entre dos casillas— y la ficha se leía como si los rótulos
   fueran datos. `flex-basis:100%` la fuerza a su propio renglón. */
.forma > .et-campo.bloque{flex:0 0 100%;margin:14px 0 2px}
.forma > .et-campo.bloque:first-child{margin-top:0}
.forma-accion{flex:none;display:flex;gap:var(--e2)}

/* Recibo: lo que el sistema entrega UNA sola vez (clave temporal, hash). No va
   en un aviso flotante —el aviso se va solo y esto hay que copiarlo—; se queda
   en pantalla, seleccionable, hasta que se navegue a otra parte. */
.recibo{margin-top:var(--e4);padding:var(--e4);border:1px solid var(--linea-fuerte);
  border-radius:var(--r);background:var(--panel-2)}
.recibo[hidden]{display:none}
.recibo-t{font-size:var(--t-xs);color:var(--tinta);margin-bottom:var(--e3)}
.recibo-t b{color:var(--navy-logo);font-weight:700}
.recibo-clave{display:flex;align-items:baseline;gap:var(--e3);margin-bottom:var(--e2);flex-wrap:wrap}
.recibo-clave code{
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:var(--t-sm);color:var(--tinta);background:var(--panel);
  border:1px solid var(--linea);border-radius:var(--r);padding:3px 9px;
  user-select:all;word-break:break-all;
}
.recibo .nota-honesta{margin-top:var(--e3)}

/* ============================================================================
   8. MARCAS DE DATO — semáforo, etiquetas, señales
   ========================================================================== */
.sem{
  display:inline-flex;align-items:center;gap:5px;font-size:var(--t-2xs);font-weight:650;
  padding:2px 8px;border-radius:var(--r-full);white-space:nowrap;
}
.sem::before{font-size:.9em;line-height:1}
.sem.ok{background:var(--ok-f);color:var(--ok)} .sem.ok::before{content:"●"}
.sem.alerta{background:var(--alerta-f);color:var(--alerta)} .sem.alerta::before{content:"▲"}
.sem.riesgo{background:var(--riesgo-f);color:var(--riesgo)} .sem.riesgo::before{content:"■"}
.sem.nada{background:var(--nada-f);color:var(--nada)} .sem.nada::before{content:"○"}

.etiq{
  display:inline-flex;align-items:center;gap:5px;font-size:var(--t-2xs);font-weight:650;
  padding:2px 8px;border-radius:var(--r);background:var(--panel-2);color:var(--tinta-2);
  border:1px solid var(--linea);white-space:nowrap;
}
.etiq.navy{background:var(--seleccion);color:var(--navy);border-color:#cbd3ee}
.etiq.oro{background:#fbf3e2;color:var(--oro-txt);border-color:#eddcb6}

.pos{
  display:inline-grid;place-items:center;width:19px;height:19px;border-radius:3px;
  background:var(--panel-2);border:1px solid var(--linea);
  font-size:var(--t-2xs);font-weight:700;color:var(--tinta-3);font-variant-numeric:tabular-nums;
}
.pos.top{background:var(--navy);border-color:var(--navy);color:var(--oro)}

/* ============================================================================
   9. ESTADOS — vacío, carga, error. Los tres se diseñan o el sistema se ve roto.
   ========================================================================== */
.vacio{
  display:flex;flex-direction:column;align-items:center;gap:var(--e2);
  padding:var(--e8) var(--e5);text-align:center;color:var(--tinta-3);
}
.vacio .fig{width:34px;height:34px;color:var(--linea-fuerte)}
/* La figura del hueco es el PUENTE (V10 fase 2). Apaisado 361×168, así que no
   cabe en la caja cuadrada del ícono que había: forzarla lo achataría un 45 %.
   Va más ancho y en tinta terciaria — presente, sin competir con la frase, que es
   la que de verdad informa por qué el hueco está vacío. */
.vacio .fig-puente{width:76px;height:35px;color:var(--linea-fuerte)}
.vacio h3{font-size:var(--t-md);color:var(--tinta-2);font-weight:600}
.vacio p{font-size:var(--t-xs);max-width:46ch;line-height:1.55}
.vacio .btn{margin-top:var(--e2)}

.esqueleto{
  background:linear-gradient(90deg,var(--panel-2) 25%,var(--esq-brillo) 37%,var(--panel-2) 63%);
  background-size:400% 100%;animation:brilla 1.2s ease-in-out infinite;
  border-radius:3px;height:11px;
}
@keyframes brilla{0%{background-position:100% 0}100%{background-position:0 0}}
.esqueleto-fila{display:flex;gap:var(--e3);padding:8px var(--e3);align-items:center;
  border-bottom:1px solid var(--linea)}
.esqueleto-fila .esqueleto:nth-child(1){flex:2}
.esqueleto-fila .esqueleto:nth-child(2){flex:3}
.esqueleto-fila .esqueleto:nth-child(3){flex:1}
.esqueleto-fila .esqueleto:nth-child(4){flex:1}

.fallo{
  display:flex;gap:var(--e3);align-items:flex-start;
  padding:var(--e3) var(--e4);margin:var(--e4);
  background:var(--riesgo-f);border:1px solid #f0cfcd;border-radius:var(--r);
  font-size:var(--t-xs);color:#7a231d;line-height:1.55;
}
.fallo .ic{width:16px;height:16px;flex:none;color:var(--riesgo);margin-top:1px}
.fallo b{display:block;font-size:var(--t-sm);margin-bottom:2px;color:var(--riesgo)}
.fallo .btn{margin-top:var(--e2)}

.nota-honesta{
  font-size:var(--t-2xs);color:var(--tinta-3);line-height:1.6;
}
.nota-honesta b{color:var(--navy-logo);font-weight:650}

.aviso{
  display:flex;gap:var(--e2);align-items:flex-start;
  background:var(--alerta-f);border:1px solid #ebdcb6;border-radius:var(--r);
  padding:9px var(--e3);font-size:var(--t-xs);color:#63501c;line-height:1.5;
}
.aviso .ic{color:var(--oro-txt);flex:none;font-weight:800}
.aviso .cerrar{margin-left:auto;background:none;border:0;color:#8a7534;cursor:pointer;
  padding:2px 4px;border-radius:3px;flex:none;line-height:1}
.aviso .cerrar:hover{background:rgba(0,0,0,.07)}

/* ============================================================================
   10. EXCEPCIONES — reemplaza al bloque narrado «Lectura de dirección».
       No narra: lista lo que está fuera de norma y a dónde ir a arreglarlo.
   ========================================================================== */
.excep{display:grid;grid-template-columns:1fr;border-top:1px solid var(--linea)}
.excep-f{
  display:grid;grid-template-columns:88px 1fr auto;align-items:center;gap:var(--e3);
  padding:var(--fila-pad) var(--e4);border-bottom:1px solid var(--linea);
  font-size:var(--t-xs);color:var(--tinta);cursor:pointer;text-align:left;
  background:none;border-left:0;border-right:0;border-top:0;width:100%;
  transition:background var(--rapido) linear;
}
.excep-f:last-child{border-bottom:0}
.excep-f:hover{background:var(--realce)}
.excep-f .qué{min-width:0;line-height:1.45}
.excep-f .qué b{font-weight:650}
.excep-f .val{font-variant-numeric:tabular-nums;font-weight:700;font-size:var(--t-sm);
  white-space:nowrap;text-align:right}
.excep-f .val.riesgo{color:var(--riesgo)}
.excep-f .val.alerta{color:var(--alerta)}
.excep-f .val.ok{color:var(--ok)}

/* ============================================================================
   11. GRÁFICOS — SVG a mano, sin librería (CSP). Reglas:
       · Toda etiqueta cabe dentro del viewBox. Nada recortado.
       · Si el gráfico no responde una pregunta, va como tabla.
   ========================================================================== */
/* `--k` = escala real del viewBox (la fija `ajustarEscala` en ui.js). Los textos
   y los trazos se dividen por ella para conservar su tamaño en píxeles de
   pantalla: sin esto, en un monitor grande el gráfico se estira y la tipografía
   con él (etiquetas de 10 px vistas a 29 px). */
.grafico{width:100%;height:auto;display:block;overflow:visible;--k:1}
.grafico .eje{stroke:var(--linea);stroke-width:calc(1 / var(--k))}
.grafico .malla{stroke:var(--linea);stroke-width:calc(1 / var(--k));
  stroke-dasharray:calc(2 / var(--k)) calc(3 / var(--k))}
.grafico .rot{fill:var(--tinta-3);font-size:calc(10.5px / var(--k));font-family:var(--sans)}
.grafico .rot-v{fill:var(--tinta);font-size:calc(11.5px / var(--k));font-weight:650;font-family:var(--sans);
  font-variant-numeric:tabular-nums}
/* El trazo del dato va por token (--graf-trazo): navy sobre claro, periwinkle
   luminoso (7,47:1) sobre la capa de mando y el tema oscuro. */
.grafico .linea{fill:none;stroke:var(--graf-trazo);stroke-width:calc(2 / var(--k));stroke-linejoin:round;stroke-linecap:round}
.grafico .area{fill:var(--graf-trazo);opacity:.07}
/* `r` como propiedad CSS (SVG2): el punto también tiene que conservar su tamaño
   real, o en pantalla grande son pelotas. */
.grafico .punto{fill:var(--panel);stroke:var(--graf-trazo);stroke-width:calc(2 / var(--k));
  r:calc(3.5 / var(--k))}

/* Reparto: barra apilada + leyenda. Reemplaza al donut decorativo. */
.reparto{display:flex;height:22px;border-radius:3px;overflow:hidden;border:1px solid var(--linea)}
.reparto span{display:block;height:100%}
.leyenda{display:flex;gap:var(--e4);flex-wrap:wrap;margin-top:10px;font-size:var(--t-2xs);color:var(--tinta-2)}
.leyenda i{display:inline-flex;align-items:center;gap:6px;font-style:normal}
.leyenda .sw{width:9px;height:9px;border-radius:2px;flex:none}
.leyenda b{font-variant-numeric:tabular-nums;color:var(--tinta);font-weight:700}

/* Histograma: columnas navy; las que caen bajo la mínima, en el rojo del
   semáforo. La mínima institucional se dibuja donde está. */
.grafico .col{fill:var(--graf-col)}
.grafico .col.mal{fill:var(--riesgo)}
.grafico .corte{stroke:var(--oro-osc,#9c6c10);stroke-width:calc(1.5 / var(--k));
  stroke-dasharray:calc(4 / var(--k)) calc(3 / var(--k))}
/* 🔴 SIN `fill` aquí. Lo llevaba (`fill:url(#rayado)`) y una propiedad CSS gana sobre
   el atributo de presentación del SVG, así que pisaba el patrón que cada gráfico
   declara. Los patrones pasaron a tener id ALEATORIO —para que dos gráficos en la
   misma pantalla no colisionen— y `#rayado` dejó de existir: la referencia rota no
   da error, simplemente pinta el hueco de BLANCO. Los años sin archivo se veían como
   una columna vacía en vez de rayada, que es lo mismo que se ve cuando el valor es
   cero. Afectaba a los dos gráficos de Histórico. */
.grafico .hueco{stroke:var(--linea);stroke-width:1}
/* Barras de porcentaje con escala 0–100 fija (ui.barrasPorcentaje). El color dice el
   veredicto, igual que en las filas de transición que reemplaza: por debajo de 70 la
   retención es un problema, no un matiz. */
.grafico .barra-pc{fill:var(--navy-logo)}
.grafico .barra-pc.ok{fill:var(--ok)}
.grafico .barra-pc.alerta{fill:var(--alerta)}
.grafico .barra-pc.mal{fill:var(--riesgo)}
/* El detalle fila a fila, cerrado por defecto: está para quien busca un año concreto,
   no para ocupar pantalla en quien solo quiere ver la tendencia. */
.trans-detalle{margin-top:14px;border-top:1px solid var(--linea);padding-top:10px}
.trans-detalle > summary{cursor:pointer;font-size:13px;font-weight:600;color:var(--tinta-2);
  list-style:none;display:flex;align-items:center;gap:6px;padding:2px 0}
.trans-detalle > summary::-webkit-details-marker{display:none}
.trans-detalle > summary::before{content:"▸";font-size:11px;transition:transform .18s ease}
.trans-detalle[open] > summary::before{transform:rotate(90deg)}
.trans-detalle > summary:hover{color:var(--navy-logo)}
.trans-detalle > summary:focus-visible{outline:2px solid var(--navy-logo);outline-offset:3px;border-radius:4px}
.trans-detalle .trans{margin-top:10px}
/* Tres notas de la misma categoría, en fila. Se envuelven en pantallas estrechas;
   con `1fr` a secas la nota más larga estiraba a las tres. */
.notas-fila{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:18px}
.notas-fila .nota-celda{min-width:0}
.captura-exclusiones{display:flex;flex-direction:column}
.captura-exclusion{min-width:0}
.captura-exclusion + .captura-exclusion{border-top:1px solid var(--linea)}
.captura-exclusion summary{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,2fr) auto;
  align-items:center;gap:var(--e4);padding:var(--e4) 0;cursor:pointer;list-style:none}
.captura-exclusion summary::-webkit-details-marker{display:none}
.captura-exclusion summary:focus-visible{outline:2px solid var(--navy-logo);outline-offset:var(--e1)}
.captura-exclusion-titulo{font-size:var(--t-md);font-weight:600;color:var(--tinta)}
.captura-exclusion-resumen{font-size:var(--t-sm);color:var(--tinta-2);line-height:1.5}
.captura-exclusion-resumen b{font-weight:600;color:var(--tinta)}
.captura-exclusion-abrir{font-size:var(--t-xs);color:var(--navy-logo);white-space:nowrap}
.captura-exclusion-detalle{max-width:90ch;margin:0 0 var(--e4);font-size:var(--t-sm);
  color:var(--tinta-2);line-height:1.6;overflow-wrap:anywhere}
@media(max-width:768px){
  .captura-exclusion summary{grid-template-columns:minmax(0,1fr) auto;gap:var(--e2) var(--e3)}
  .captura-exclusion-resumen{grid-column:1/-1;grid-row:2}
  .captura-exclusion-abrir{grid-column:2;grid-row:1}
}
.notas-fila .nota-celda .et-campo{margin-top:0}
.notas-fila .nota-celda .parrafo{margin:6px 0 0}
/* «Ahora mismo»: qué período corre en este curso, en palabras. La fila resaltada ya
   lo señala, pero un color no se lee — y quien abre esta pantalla suele venir con la
   pregunta «¿en qué están ahora?», no a interpretar una barra. */
.franja-ahora-aviso{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px;
  margin:0 0 12px;padding:8px 12px;font-size:13px;
  background:var(--navy-logo);color:#fff;border-radius:6px}
.franja-ahora-aviso b{font-weight:700}
.franja-ahora-aviso .suave{color:#c9d6e8}
/* La repisa del final del riel: separada de los dominios por una línea, para que se
   lea como «otra cosa» y no como un módulo más de Operativo. */
.riel-apps-pie{margin-top:auto;padding-top:10px;border-top:1px solid var(--riel-linea,rgba(255,255,255,.12))}
.riel-apps-pie .riel-apps-rot{display:none}
@media (prefers-reduced-motion: reduce){.trans-detalle > summary::before{transition:none}}
.leyenda .sw.rayado{
  background:repeating-linear-gradient(45deg,var(--nada-f) 0 3px,var(--panel) 3px 6px);
  border:1px solid var(--linea);
}

/* Medidor de captura: el porcentaje nunca va solo; al lado, lo que falta. */
.medidor{display:block}
.med-riel{height:9px;border-radius:var(--r-full);background:var(--nada-f);overflow:hidden;
  border:1px solid var(--linea)}
/* Sin transición de `width` (regla dura V7 §5.1: solo transform/opacity). La
   entrada crece con scaleX en el catálogo de motion (§ MOVIMIENTO). */
.med-val{display:block;height:100%;border-radius:var(--r-full);background:var(--navy-logo)}
.med-val.ok{background:var(--ok)}.med-val.alerta{background:var(--alerta)}.med-val.mal{background:var(--riesgo)}
.med-pie{display:flex;align-items:baseline;gap:8px;margin-top:6px;font-size:var(--t-2xs);color:var(--tinta-3)}
.med-pie b{font-size:var(--t-md);color:var(--tinta);font-weight:700;font-variant-numeric:tabular-nums}

/* Transiciones año→año (retención). Una fila por salto, con su magnitud. */
.trans{display:flex;flex-direction:column}
.trans-f{display:grid;grid-template-columns:132px minmax(0,1fr) 56px;
  grid-template-areas:"et b v" ". d d";gap:2px 10px;align-items:center;
  padding:9px 0;border-bottom:1px solid var(--linea)}
.trans-f:last-child{border-bottom:0}
.trans-et{grid-area:et;font-size:var(--t-xs);font-weight:600;color:var(--tinta);
  font-variant-numeric:tabular-nums}
.trans-b{grid-area:b;display:block;height:9px;border-radius:var(--r-full);
  background:var(--nada-f);border:1px solid var(--linea);overflow:hidden}
.trans-b .sig{display:block;height:100%;background:var(--navy-logo)}
.trans-b .sig.ok{background:var(--ok)}.trans-b .sig.alerta{background:var(--alerta)}.trans-b .sig.mal{background:var(--riesgo)}
.trans-v{grid-area:v;text-align:right;font-size:var(--t-sm);font-weight:700;color:var(--tinta);
  font-variant-numeric:tabular-nums}
.trans-d{grid-area:d;font-size:var(--t-2xs);color:var(--tinta-3)}

/* Matriz materia × curso: el valor VA ESCRITO en la celda. Un mapa de calor sin
   número obliga a adivinar, y aquí el número es el punto. */
.matriz{border-collapse:separate;border-spacing:0;font-size:var(--t-2xs);width:100%}
.matriz th{
  font-size:var(--t-2xs);font-weight:700;color:var(--tinta-3);padding:4px 5px;
  text-align:center;white-space:nowrap;background:var(--panel-2);
  border-bottom:1px solid var(--linea-fuerte);position:sticky;top:0;z-index:1;
}
.matriz th.materia{
  text-align:left;position:sticky;left:0;z-index:3;background:var(--panel-2);
  min-width:150px;max-width:150px;overflow:hidden;text-overflow:ellipsis;
  color:var(--tinta);font-weight:600;text-transform:none;letter-spacing:0;font-size:var(--t-xs);
}
.matriz tbody th.materia{border-bottom:1px solid var(--linea);top:auto;z-index:2}
.matriz td{
  padding:0;border-bottom:1px solid var(--panel);border-right:1px solid var(--panel);
  text-align:center;font-variant-numeric:tabular-nums;font-weight:650;
  min-width:38px;height:26px;color:var(--tinta);
}
.matriz td .v{display:block;padding:5px 3px;border-radius:2px}
.matriz td.sin{color:var(--tinta-3);font-weight:400}

/* Piezas de texto y navegación dentro de un panel ---------------------------- */
.parrafo{font-size:var(--t-md);color:var(--tinta-2);line-height:1.6;max-width:74ch}
.et-campo.bloque{display:block;margin:18px 0 7px}
.enlace{background:none;border:0;padding:0;font:inherit;font-weight:600;color:var(--navy-logo);
  cursor:pointer;text-align:left}
.enlace:hover{text-decoration:underline;text-underline-offset:2px}
.nota-honesta ul{margin:6px 0 0;padding-left:18px}
.nota-honesta li{margin-top:3px}

/* Atajos del resumen a las otras ocho pantallas. No son tarjetas decorativas:
   cada una lleva la pregunta que responde, que es el criterio para entrar. */
.atajos{display:grid;grid-template-columns:repeat(auto-fit,minmax(232px,1fr));
  border-top:1px solid var(--linea)}
.atajo{
  display:block;padding:11px var(--e4);text-decoration:none;
  border-right:1px solid var(--linea);border-bottom:1px solid var(--linea);
  transition:background var(--rapido) linear;
}
.atajo:hover{background:var(--panel-2)}
.atajo b{display:block;font-size:var(--t-sm);font-weight:600;color:var(--navy-logo)}
.atajo i{display:block;font-style:normal;font-size:var(--t-2xs);color:var(--tinta-3);
  line-height:1.4;margin-top:2px}

/* ============================================================================
   12. PALETA DE COMANDOS (⌘K) — <dialog> nativo: sin trampas de foco propias
   ========================================================================== */
dialog.paleta{
  border:0;padding:0;border-radius:var(--r-lg);width:min(560px,calc(100vw - 32px));
  background:var(--panel);box-shadow:var(--flota-alta);color:var(--tinta);
  margin:12vh auto auto;max-height:70vh;overflow:hidden;
}
dialog.paleta::backdrop{background:rgba(8,12,40,.42)}
.paleta-cab{display:flex;align-items:center;gap:var(--e2);padding:var(--e3) var(--e4);
  border-bottom:1px solid var(--linea)}
.paleta-cab .ic{width:16px;height:16px;color:var(--tinta-3);flex:none}
.paleta-cab input{flex:1;border:0;outline:0;background:none;font-size:var(--t-md);padding:2px 0}
.paleta-cab input::placeholder{color:var(--tinta-3)}
.paleta-lista{overflow-y:auto;max-height:min(48vh,420px);padding:var(--e1) 0}
.paleta-grupo{font-size:var(--t-2xs);font-weight:700;letter-spacing:.07em;text-transform:uppercase;
  color:var(--tinta-3);padding:8px var(--e4) 3px}
.paleta-op{
  display:flex;align-items:center;gap:10px;width:100%;padding:7px var(--e4);
  background:none;border:0;cursor:pointer;text-align:left;font-size:var(--t-sm);color:var(--tinta);
}
.paleta-op .ic{width:15px;height:15px;color:var(--tinta-3);flex:none}
.paleta-op .et{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.paleta-op .don{font-size:var(--t-2xs);color:var(--tinta-3);flex:none}
.paleta-op[data-act="1"]{background:var(--seleccion)}
.paleta-op[data-act="1"] .ic{color:var(--navy)}
.paleta-pie{display:flex;align-items:center;gap:var(--e4);padding:7px var(--e4);
  border-top:1px solid var(--linea);background:var(--panel-2);font-size:var(--t-2xs);color:var(--tinta-3)}
.paleta-pie i{font-style:normal;display:inline-flex;align-items:center;gap:5px}

/* ============================================================================
   13. DOCUMENTO OFICIAL — boletín, sábana, acta.
       Aquí SÍ manda el papel: márgenes, sello y firmas como el Rector.
   ========================================================================== */
.doc{background:var(--panel);border:1px solid var(--linea);border-radius:var(--r-lg);padding:var(--e6)}
.doc-cab{display:flex;align-items:flex-start;gap:var(--e4);padding-bottom:var(--e3);
  border-bottom:2px solid var(--navy);margin-bottom:var(--e4)}
.doc-cab .escudo{width:56px;height:56px;flex:none;display:grid;place-items:center}
.doc-cab .escudo img{width:56px;height:56px;object-fit:contain;display:block}
.doc-cab .inst{flex:1;min-width:0}
.doc-cab .inst .pais{font-size:var(--t-2xs);font-weight:700;letter-spacing:.12em;
  color:var(--tinta-3);text-transform:uppercase}
.doc-cab .inst .col{font-size:var(--t-lg);font-weight:700;color:var(--navy);line-height:1.2;margin-top:1px}
.doc-cab .inst .codigos{font-size:var(--t-2xs);color:var(--tinta-3);letter-spacing:.03em;margin-top:2px}
.doc-cab .inst .tit{font-size:var(--t-xs);color:var(--oro-txt);font-weight:700;margin-top:5px;
  text-transform:uppercase;letter-spacing:.05em}
.doc-cab .meta-doc{text-align:right;font-size:var(--t-2xs);color:var(--tinta-3);flex:none}
.doc-escala{margin-top:var(--e5);padding:var(--e3);background:var(--panel-2);
  border:1px solid var(--linea);border-radius:var(--r)}
.doc-escala .et-campo{display:block;margin-bottom:5px}
.esc-item{display:inline-block;margin-right:var(--e5);font-size:var(--t-2xs);color:var(--tinta-2)}
.esc-item b{color:var(--navy);font-weight:700}
.doc-lugar{margin-top:var(--e6);text-align:right;font-size:var(--t-xs);color:var(--tinta-2)}
.doc-firmas{display:flex;gap:var(--e8);flex-wrap:wrap;justify-content:space-around;
  margin-top:var(--e8);padding-top:var(--e2)}
.doc-firmas .firma{text-align:center;min-width:180px}
.doc-firmas .firma .linea{border-top:1px solid var(--tinta);margin-bottom:5px;height:1px}
.doc-firmas .firma .firmante{font-size:var(--t-xs);font-weight:650;color:var(--tinta)}
.doc-firmas .firma .rol{font-size:var(--t-2xs);color:var(--tinta-3)}


/* Piezas nuevas del documento (V6). El boletín se lee en pantalla y se archiva en
   papel: manda el papel. */
.doc-acciones{display:flex;align-items:center;gap:var(--e3);flex-wrap:wrap;margin-bottom:var(--e4)}
.doc-pista{font-size:var(--t-2xs);color:var(--tinta-3);max-width:62ch;line-height:1.45}

/* Ficha del estudiante: etiqueta arriba, valor debajo, en fila. */
.doc-alumno{
  display:flex;gap:var(--e6);flex-wrap:wrap;
  padding:var(--e3) 0 var(--e4);border-bottom:1px solid var(--linea);margin-bottom:var(--e4);
}
.da-campo{display:flex;flex-direction:column;gap:2px;min-width:0}
.da-k{font-size:var(--t-2xs);font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--tinta-3)}
.da-v{font-size:var(--t-md);font-weight:650;color:var(--tinta)}
.da-campo:first-child .da-v{font-size:var(--t-lg)}

.doc-sub{font-size:var(--t-2xs);font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  color:var(--tinta-3);margin:var(--e5) 0 var(--e2)}
/* En pantalla, una sábana de 16 materias se desplaza dentro de su marco (el
   papel manda en §16, donde el overflow vuelve a visible). */
.doc-tabla{margin-top:var(--e2);overflow-x:auto}
.doc-tabla table{width:100%;border-collapse:collapse;font-size:var(--t-sm)}
.doc-tabla th{
  text-align:left;font-size:var(--t-2xs);font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  color:var(--tinta-3);padding:6px 8px;border-bottom:1px solid var(--tinta);
}
.doc-tabla th.num,.doc-tabla td.num{text-align:right;font-variant-numeric:tabular-nums}
.doc-tabla td{padding:5px 8px;border-bottom:1px solid var(--linea);color:var(--tinta)}
.doc-tabla td.fuerte{font-weight:700}
.doc-tabla tbody tr:nth-child(even) td{background:var(--panel-2)}

/* Franja de resumen: la cifra con su etiqueta AL LADO. Antes el valor iba arriba y
   la etiqueta debajo, que se cortaba en dos líneas («… 2 EN LETRAS, FUERA»). */
.doc-resumen{
  display:flex;align-items:flex-end;justify-content:space-between;gap:var(--e5);flex-wrap:wrap;
  margin-top:var(--e4);padding:var(--e3) var(--e4);
  background:var(--panel-2);border:1px solid var(--linea);border-radius:var(--r);
}
.dr-bloque{display:flex;flex-direction:column;gap:2px;min-width:0}
.dr-bloque.der{align-items:flex-end;text-align:right}
.dr-k{font-size:var(--t-2xs);font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--tinta-3)}
.dr-v{font-size:2rem;font-weight:700;line-height:1;color:var(--navy);font-variant-numeric:tabular-nums}
.dr-n{font-size:var(--t-2xs);color:var(--tinta-3);line-height:1.4}
.doc-nota-prov{margin-top:var(--e3);font-size:var(--t-xs);color:var(--tinta-2);line-height:1.5;
  padding-left:var(--e3);border-left:1px solid var(--linea)}

/* Chip de veredicto DENTRO del documento oficial. Es el único sitio donde
   sobrevive `estado()`: su texto está auditado contra la plantilla del Rector y
   un documento que va a una casa no se rediseña por gusto. */
.estado{font-size:var(--t-2xs);font-weight:650;text-transform:none;letter-spacing:0;
  padding:2px 8px;border-radius:var(--r-full)}
.estado.vivo{background:var(--ok-f);color:var(--ok)}
.estado.migrar{background:var(--nada-f);color:var(--nada)}
.doc-escala .sub-t{font-size:var(--t-2xs);font-weight:700;letter-spacing:.06em;
  text-transform:uppercase;color:var(--tinta-3);margin:var(--e4) 0 var(--e2)}

/* ============================================================================
   14. PIEZAS DE MÓDULO — horarios, cronograma, alerta temprana
   ========================================================================== */
.hora-celda{white-space:nowrap;vertical-align:top}
.hora-rango{font-weight:650;color:var(--navy);font-size:var(--t-xs);font-variant-numeric:tabular-nums}
.hora-n{font-size:var(--t-2xs);color:var(--tinta-3);margin-top:1px}
td .mat-chip{display:inline-block;font-size:var(--t-2xs);font-weight:600;padding:2px 7px;
  border-radius:3px;margin-bottom:2px}

/* Tinte de materia TOKENIZADO (.mat-c1…7, estable por nombre desde el JS).
   Antes iba por CSSOM con pasteles fijos, ilegibles en tema oscuro; con clase,
   el tema re-tinta solo. Contraste AA en los dos temas. */
.mat-c1{--mat-fondo:#e7eaf6;background:var(--mat-fondo);color:#2b3488}
.mat-c2{--mat-fondo:#fbf1da;background:var(--mat-fondo);color:#8a5f0d}
.mat-c3{--mat-fondo:#e3f6ec;background:var(--mat-fondo);color:#177146}   /* 5,35:1 — el --ok daba 4,45 aquí */
.mat-c4{--mat-fondo:#edebf7;background:var(--mat-fondo);color:#5b4a9c}
.mat-c5{--mat-fondo:#e7f0f6;background:var(--mat-fondo);color:#2b6b8f}
.mat-c6{--mat-fondo:#f5ede4;background:var(--mat-fondo);color:#7a5326}
.mat-c7{--mat-fondo:#f7eaf1;background:var(--mat-fondo);color:#8f3a6b}
[data-tema="oscuro"] .mat-c1{--mat-fondo:rgba(151,165,238,.16);background:var(--mat-fondo);color:#b9c6f5}
[data-tema="oscuro"] .mat-c2{--mat-fondo:rgba(232,176,75,.15);background:var(--mat-fondo);color:#e8c07a}
[data-tema="oscuro"] .mat-c3{--mat-fondo:rgba(76,195,138,.15);background:var(--mat-fondo);color:#8adab2}
[data-tema="oscuro"] .mat-c4{--mat-fondo:rgba(167,139,250,.16);background:var(--mat-fondo);color:#c6b7f7}
[data-tema="oscuro"] .mat-c5{--mat-fondo:rgba(125,211,252,.15);background:var(--mat-fondo);color:#a8ddf7}
[data-tema="oscuro"] .mat-c6{--mat-fondo:rgba(221,190,147,.16);background:var(--mat-fondo);color:#dfc49e}
[data-tema="oscuro"] .mat-c7{--mat-fondo:rgba(244,167,206,.15);background:var(--mat-fondo);color:#f0bcd6}

/* Fila de campos de la envoltura de un módulo (selectores + acciones) */
.fila-campos{display:flex;align-items:flex-end;gap:var(--e4);flex-wrap:wrap}
.acciones-doc{margin-left:auto;align-items:center}
@media(max-width:1000px){.acciones-doc{margin-left:0}}

/* Un select NUNCA dicta el ancho de la fila: su opción más larga (hay
   destrezas de 200 caracteres en el catálogo de materias) se recorta con
   elipsis dentro del control; el texto completo se lee en la lista desplegada,
   que el navegador deja crecer. `width:fit-content` conserva el arreglo de
   Firefox (§ final: el padding de la flecha sí entra en esa cuenta) y
   `min-width:0` desactiva el `min-width:fit-content` general, que aquí
   ganaría sobre el tope. */
.tabla-herr .select,.fila-campos .select{
  width:fit-content;min-width:0;max-width:min(340px,calc(100vw - 150px))}

/* Los tres filtros de captura comparten el mismo margen y se redistribuyen
   según el ancho del panel, también con el zoom del navegador. */
.insumos-controles{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,14rem),1fr));
  align-items:end;gap:var(--e4);padding:var(--e4) var(--e5)}
.insumos-controles .campo{width:100%}
.insumos-controles .campo > span{text-transform:none;letter-spacing:0;font-weight:600}
.insumos-controles .select{width:100%;max-width:100%;min-width:0}
.insumos-controles .cuenta{grid-column:1/-1;margin-left:0}
.insumos-zona > .seg{margin:var(--e4) var(--e5);max-width:calc(100% - var(--e5)*2);flex-wrap:wrap}

/* Grilla de captura de notas (Ingreso de evaluaciones). El spinner nativo del
   number se retira en los DOS navegadores (Firefox lo pinta aun con
   appearance:none): las flechas del teclado aquí navegan filas, no suman. */
.celda-nota .input.nota{min-height:26px;padding:3px 7px;-moz-appearance:textfield}
.celda-nota .input.nota::-webkit-outer-spin-button,
.celda-nota .input.nota::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
.celda-nota .err-nota{display:block;margin-top:3px;max-width:200px;color:var(--riesgo)}
.aviso.en-grilla{margin:var(--e3) var(--e4) 0}
.input[aria-invalid="true"],main input[aria-invalid="true"]{
  border-color:var(--riesgo);box-shadow:0 0 0 3px var(--riesgo-f)}

/* Fila que abre un detalle (Estudiantes → cajón): también con teclado */
.tabla tr.abre{cursor:pointer}
.tabla tr.abre:focus-visible{outline:0}
.tabla tr.abre:focus-visible td{background:var(--seleccion)}
.lista-tope{padding:var(--e2) var(--e4)}
.cajon-estado{display:flex;flex-direction:column;gap:4px;margin:var(--e3) 0}

/* La banda de cifras dentro de un panel sin aire se integra al marco: sin
   doble borde, dividida del contenido por un filete. */
.panel-cuerpo.sin-aire .cifras{border:0;border-bottom:1px solid var(--linea);border-radius:0}

.crono{display:grid;gap:var(--e4)}
.crono-sem{display:grid;grid-template-columns:64px 1fr;gap:var(--e4);align-items:start}
.crono-sem .marca{position:sticky;top:70px;text-align:center;background:var(--panel-2);
  border:1px solid var(--linea);border-radius:var(--r);padding:8px 4px}
.crono-sem .marca .s{display:block;font-size:var(--t-2xs);letter-spacing:.1em;
  text-transform:uppercase;color:var(--tinta-3)}
.crono-sem .marca .n{display:block;font-size:var(--t-lg);font-weight:700;color:var(--navy);line-height:1.1}
.crono-sem .marca .t{display:block;font-size:var(--t-2xs);color:var(--tinta-3);margin-top:2px}
.crono-tareas{display:grid;gap:var(--e2)}
.crono-t{display:flex;align-items:center;gap:var(--e3);padding:8px var(--e3);
  background:var(--panel);border:1px solid var(--linea);border-radius:var(--r);font-size:var(--t-xs)}
.crono-t .mat{flex:none;font-size:var(--t-2xs);font-weight:650;padding:2px 8px;border-radius:3px}
.crono-t .cal{flex:none;margin-left:auto;color:var(--tinta-3);font-size:var(--t-2xs)}

/* Alerta temprana: fila de tabla, no tarjeta con degradado */
.senales{display:flex;gap:5px;flex-wrap:wrap}
.sen{font-size:var(--t-2xs);font-weight:650;padding:1px 7px;border-radius:3px;white-space:nowrap;
  border:1px solid transparent}
.sen-asistencia{background:#e9eff7;color:#245683;border-color:#cfddec}
.sen-comportamiento{background:var(--alerta-f);color:var(--alerta);border-color:#ebdcb6}
.sen-calificaciones{background:var(--riesgo-f);color:var(--riesgo);border-color:#f0cfcd}
.enlace-nom{background:none;border:0;padding:0;font:inherit;font-weight:600;color:var(--navy-logo);
  cursor:pointer;text-align:left}
.enlace-nom:hover{text-decoration:underline;text-underline-offset:2px}

/* Ficha de módulo aún no construido */
.modulo-vacio{max-width:640px}
.flujo{display:flex;align-items:center;gap:var(--e2);flex-wrap:wrap}
.paso{display:inline-flex;align-items:center;gap:6px;background:var(--panel-2);
  border:1px solid var(--linea);border-radius:var(--r);padding:5px 10px;font-size:var(--t-xs)}
.paso b{display:grid;place-items:center;width:16px;height:16px;border-radius:50%;
  background:var(--navy);color:#fff;font-size:9px;font-weight:700}
.campos{display:flex;flex-wrap:wrap;gap:5px}
.ficha{font-size:var(--t-2xs);background:var(--panel-2);color:var(--tinta-2);
  border:1px solid var(--linea);border-radius:3px;padding:3px 8px}

.pie-inst{
  text-align:center;color:var(--tinta-3);font-size:var(--t-2xs);
  padding:var(--e5) 0 var(--e2);margin-top:var(--e5);border-top:1px solid var(--linea);
}
.pie-inst b{color:var(--navy-logo);font-weight:650}

/* Alerta temprana: era una tarjeta con degradado lateral; ahora es una fila.
   Vivía en el puente de vocabulario, pero no es vocabulario viejo: es una pieza
   de módulo con su propia forma, así que se queda aquí al borrarse el puente. */
.alertas{display:grid;gap:0;border-top:1px solid var(--linea);
  /* con el dato real son 35 filas = 2.300 px de pantalla; se desplaza
     dentro de su caja, como las tablas largas (§6 `.tabla-marco.alto`) */
  max-height:520px;overflow:auto}
.alerta-f{display:flex;align-items:center;gap:var(--e3);padding:8px var(--e4);
  border-bottom:1px solid var(--linea);background:var(--panel)}
.alerta-f:hover{background:var(--realce)}
.alerta-f.alto .af-prom{color:var(--riesgo)}
.alerta-f.medio .af-prom{color:var(--alerta)}
.af-izq{display:flex;flex-direction:column;gap:1px;min-width:190px}
.af-nom{background:none;border:0;padding:0;text-align:left;font-size:var(--t-xs);
  font-weight:600;color:var(--dato-a);cursor:pointer}
.af-nom:hover{text-decoration:underline;text-underline-offset:2px}
.af-curso{font-size:var(--t-2xs);color:var(--tinta-3)}
.af-senales{display:flex;gap:5px;flex-wrap:wrap;flex:1}
.af-prom{flex:none;font-size:var(--t-md);font-weight:700;color:var(--tinta);
  min-width:56px;text-align:right;font-variant-numeric:tabular-nums}

/* ============================================================================
   15. RESPONSIVO — estructural: los menús de la cinta se vuelven una lista a
   pantalla completa y la rejilla se apila. Nada de tipografía fluida.
   ========================================================================== */
@media(max-width:1400px){
  /* Medido con SEIS menús (Secretaría entró en 7.1): marca 200 + 6 menús ~640 +
     buscador 196 + tema/densidad 66 + sesión 180 + respiros ≈ 1.330 px. Por
     debajo el buscador se montaba encima de la sesión. Se recorta primero lo
     prescindible (el rótulo del buscador, el nombre de la sesión), nunca la
     navegación. */
  .buscar-abrir{min-width:0}
  .buscar-abrir .et,.buscar-abrir .kbd{display:none}
}
@media(max-width:1260px){
  .sesion .quien{display:none}
  .cinta-marca .txt span{display:none}
}
@media(max-width:1100px){
  .col-3,.col-4,.col-5{grid-column:span 6}
  .col-7,.col-8,.col-9{grid-column:1/-1}
  .menu-b{padding:0 8px;font-size:var(--t-xs)}
}

/* A menos de 1.000 px no caben cinco menús en una línea: modo cajón. */
@media(max-width:1000px){
  .hamb{display:inline-flex;order:-1}
  .cinta{padding:0 var(--e3);gap:var(--e2);flex-wrap:wrap}
  .cinta-marca .txt{display:none}
  /* Los menús salen del flujo de la cinta y se despliegan como una lista
     seccionada a pantalla completa bajo ella. */
  .menus{
    display:none;position:fixed;inset:52px 0 0 0;
    flex-direction:column;gap:0;background:var(--panel);overflow-y:auto;padding:var(--e2) 0;
  }
  #app.cajon .menus{display:flex}
  #app.cajon::after{content:"";position:fixed;inset:0;background:rgba(8,12,40,.4);z-index:var(--z-velo)}
  .menu-grupo{border-bottom:1px solid var(--linea)}
  .menu-b{
    height:auto;padding:11px var(--e4);width:100%;justify-content:space-between;
    color:var(--tinta);border-bottom:0;font-size:var(--t-md);font-weight:600;
  }
  .menu-b:hover,.menu-b[aria-expanded="true"]{background:var(--panel-2);color:var(--tinta)}
  .menu-b.activo{color:var(--navy-logo)}
  .menu-pop{
    position:static;max-width:none;min-width:0;border:0;border-radius:0;box-shadow:none;
    background:var(--panel-2);max-height:none;padding:2px var(--e2) var(--e2);
  }
  /* 🔴 GOTCHA: los FLEX ITEMS respetan z-index aunque sean position:static, y
     `#app` es flex. Con `z-index:30` heredado de la regla base, esta fila empataba
     con la cinta y —al venir después en el DOM— tapaba el menú desplegado: los
     toques sobre los ítems iban a parar aquí. Se baja explícitamente. */
  .contexto{position:static;z-index:auto;padding:var(--e3)}
  .filtros{width:100%}
  .filtro{flex:1 1 132px}
  main{padding:var(--e3)}
  .rejilla{gap:var(--e3)}
  .col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9{grid-column:1/-1}
  .buscar-abrir{min-width:0}
  .buscar-abrir .et,.buscar-abrir .kbd{display:none}
  .sesion .quien{display:none}
  .frescura{display:none}
  .cifras{grid-template-columns:repeat(2,1fr)}
  .cifra{border-left:1px solid var(--linea);border-top:1px solid var(--linea)}
  .cifra:nth-child(-n+2){border-top:0}
  .cifra:nth-child(odd){border-left:0}
}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;
    animation-delay:0s!important;transition-duration:.001ms!important;transition-delay:0s!important}
}

/* ============================================================================
   16. IMPRESIÓN — el boletín y la sábana se imprimen y se ARCHIVAN.
   Aquí manda el papel: A4, tipografía en puntos, una sola página cuando cabe.
   Lo que es de pantalla (selectores, botones, avisos) no existe en el papel.
   ========================================================================== */
@page{ size:A4; margin:12mm 14mm; }

@media print{
  :root{--lienzo:#fff}
  html,body{background:#fff}
  /* Fuera del papel: armazón, controles y cualquier pieza marcada de pantalla. */
  .cinta,.contexto,.tabla-herr,.pagina,.paleta,.btn,.aviso,.excep,.hamb,.menu-pop,
  .solo-pantalla,.doc-acciones,.pie-inst,.frescura{display:none!important}
  #app{display:block}
  main{padding:0}
  .rejilla{display:block}
  .card,.panel,.doc{border:0!important;border-radius:0!important;overflow:visible!important;
    box-shadow:none!important;padding:0!important;margin:0!important;background:#fff!important}
  /* Solo se imprime lo que ES un documento. Un panel sin cabecera oficial es
     pantalla, no papel. */
  main .card:not(:has(.doc-cab)):not(.imprimible),
  main .panel:not(:has(.doc-cab)):not(.imprimible){display:none!important}

  .doc{font-size:9.5pt;color:#000}
  .doc-cab{padding-bottom:6pt;border-bottom:1.6pt solid #000;margin-bottom:8pt;gap:10pt}
  .doc-cab .escudo,.doc-cab .escudo img{width:46pt;height:46pt}
  .doc-cab .inst .pais{font-size:6.5pt;color:#333}
  .doc-cab .inst .col{font-size:13pt;color:#000}
  .doc-cab .inst .codigos{font-size:6.5pt;color:#333}
  .doc-cab .inst .tit{font-size:8pt;color:#000}
  .doc-cab .meta-doc{font-size:7.5pt;color:#333}

  .doc-alumno{padding:5pt 0 7pt;margin-bottom:7pt;gap:22pt;border-bottom:.5pt solid #999}
  .da-k,.dr-k,.doc-sub{font-size:6.5pt;color:#444}
  .da-v{font-size:9.5pt;color:#000}
  .da-campo:first-child .da-v{font-size:11.5pt}

  .doc-tabla table{font-size:8.6pt}
  .doc-tabla th{font-size:6.5pt;padding:3pt 4pt;border-bottom:.8pt solid #000;color:#000}
  .doc-tabla td{padding:2.6pt 4pt;border-bottom:.4pt solid #bbb;color:#000}
  .doc-tabla tbody tr:nth-child(even) td{background:#f4f4f7!important;
    -webkit-print-color-adjust:exact;print-color-adjust:exact}
  .doc-tabla thead{display:table-header-group}   /* la cabecera se repite por página */
  .doc-tabla tr{page-break-inside:avoid}

  .doc-resumen{margin-top:7pt;padding:6pt 8pt;border:.5pt solid #999;background:#fff!important;
    page-break-inside:avoid}
  .dr-v{font-size:17pt;color:#000}
  .dr-n{font-size:6.5pt;color:#444}
  .sem{border:.4pt solid #666!important;background:#fff!important;color:#000!important;
    font-size:7pt;padding:1pt 4pt}

  .doc-nota-prov{margin-top:5pt;font-size:7pt;color:#333;border-left:.5pt solid #999;padding-left:5pt}
  .doc-escala{margin-top:7pt;padding:5pt 7pt;border:.4pt solid #bbb;background:#fff!important;
    page-break-inside:avoid}
  .esc-item{font-size:6.8pt;margin-right:14pt;color:#000}
  .doc-lugar{margin-top:10pt;font-size:8pt;color:#000}
  .doc-firmas{margin-top:16pt;gap:18pt;page-break-inside:avoid}
  .doc-firmas .firma{min-width:120pt}
  .doc-firmas .firma .linea{border-top:.6pt solid #000}
  .doc-firmas .firma .firmante{font-size:8pt}
  .doc-firmas .firma .rol{font-size:6.8pt;color:#333}

  /* Nada de tablas con scroll en papel */
  .tabla-marco,.scroll,.doc-tabla{overflow:visible!important;max-height:none!important}
  table.tabla{font-size:8.6pt}
  .tabla thead th{background:#fff;border-bottom:.8pt solid #000;color:#000}
  a[href]::after{content:""}   /* sin URLs impresas tras los enlaces */
}

/* 🔴 Firefox y el ancho de los <select> ---------------------------------------
   Con `appearance:none`, Firefox calcula el ancho intrínseco del select por su
   opción más larga SIN sumar el `padding-right:26px` que le añadimos para la
   flecha. Resultado: el texto pierde esos 26 px y se corta contra la flecha
   («Selecciona un curso-para|»). Chromium sí lo suma, así que el defecto solo se
   ve en Firefox — que es el navegador del colegio.
   Tres medidas, en orden de importancia:
     1. `text-overflow: ellipsis`: si algo no cabe (un nombre largo de estudiante),
        se corta con puntos suspensivos, nunca a media palabra.
     2. `min-width: fit-content` dentro del bloque solo-Firefox: ahí sí entra el
        padding en la cuenta y el control crece lo necesario.
     3. Los textos de las opciones se acortaron («Elegir curso…»): la etiqueta ya
        dice qué se elige, repetirlo en el control era lo que desbordaba.
   `-moz-orient` solo existe en Firefox: es la forma estable de aislarlo. */
.select,main select,.filtro select{text-overflow:ellipsis}
@supports (-moz-orient: inline){
  .select,main select,.filtro select{min-width:fit-content;padding-right:28px}
  .filtro select{min-width:max(132px,fit-content)}
}

/* ============================================================================
   17. DENSIDAD — cómoda / compacta (V7 §4.4). Solo cambia el alto de fila de
   las tablas; lo fija tema.js en <html data-densidad> y persiste por persona.
   ========================================================================== */
:root[data-densidad="compacta"]{
  --fila-pad:5px;
  --aire-rejilla:10px;
  --aire-panel:10px;
  --aire-cifra:7px;
}

/* Cifras KPI: dígitos con ss01 y tracking -0.02em (V7 §4.3). */
.cifra .kpi-v{font-feature-settings:"ss01";letter-spacing:-.02em}

/* ============================================================================
   18. MATRIZ — el color de la celda va por CLASE, no por CSSOM con hex fijos:
   así el tema oscuro y la capa de mando re-tintan sin tocar el JS. El número
   sigue escrito en la celda; el color solo refuerza.
   ========================================================================== */
/* Los cuatro tonos de la matriz, para superficie CLARA. Contraste medido sobre su
   propio fondo, no sobre el papel: la tinta va dentro de la celda teñida. */
.matriz td .v.m-alto{background:#d5ebdf;color:#123320}
.matriz td .v.m-bien{background:#ebf4ef;color:#123320}
.matriz td .v.m-cerca{background:#f8ebd2;color:#5c3d06}
.matriz td .v.m-mal{background:#f6d9d7;color:#7a1f1a}
.matriz td.sin .v{color:var(--tinta-3)}[data-tema="oscuro"] .matriz td .v.m-alto{background:rgba(76,195,138,.26);color:#a9e8c9}[data-tema="oscuro"] .matriz td .v.m-bien{background:rgba(76,195,138,.13);color:#9bddbc}[data-tema="oscuro"] .matriz td .v.m-cerca{background:rgba(232,176,75,.16);color:#efc276}[data-tema="oscuro"] .matriz td .v.m-mal{background:rgba(239,139,132,.16);color:#f4a8a2}/* Sobre oscuro las celdas se separan por línea,
no por el blanco del panel. */
[data-tema="oscuro"] .matriz td{border-color:var(--panel)}/* ============================================================================
   19. COMPONENTES SOBRE OSCURO — lo que los tokens no alcanzan: piezas con hex
   propio (botón primario,
kbd,
fallo,
aviso,
señales) re-tintadas para AA.
   ========================================================================== */
[data-tema="oscuro"] .btn-1{background:#314a97}/* blanco 8,
21:1 */
[data-tema="oscuro"] .btn-1:hover{background:#3e59b4}[data-tema="oscuro"] .btn-1:active{background:#28407f}[data-tema="oscuro"] .btn-riesgo{background:#b3261e}/* El contador urgente de la cinta: sobre oscuro --riesgo es salmón claro y el
   texto blanco dejaba de leerse; el globo conserva el rojo pleno (5,
9:1). */
[data-tema="oscuro"] .menu-b .cnt{background:#b3261e;color:#fff}[data-tema="oscuro"] .seg button[aria-pressed="true"]{
  background:var(--seleccion);color:var(--tinta);box-shadow:none}[data-tema="oscuro"] .kbd{
  background:var(--panel-2);border-color:var(--linea-fuerte);color:var(--tinta-2)}[data-tema="oscuro"] .fallo{
  background:var(--riesgo-f);border-color:rgba(239,139,132,.4);color:#f4b8b3}[data-tema="oscuro"] .fallo b{color:var(--riesgo)}[data-tema="oscuro"] .aviso{
  background:var(--alerta-f);border-color:rgba(232,176,75,.4);color:#efcb93}[data-tema="oscuro"] .aviso .ic{color:var(--alerta)}[data-tema="oscuro"] .aviso .cerrar{color:#efcb93}[data-tema="oscuro"] .sen-asistencia{
  background:rgba(151,165,238,.16);color:#b9c6f5;border-color:transparent}[data-tema="oscuro"] .sen-comportamiento{border-color:transparent}[data-tema="oscuro"] .sen-calificaciones{border-color:transparent}[data-tema="oscuro"] .etiq.navy{
  background:var(--seleccion);color:var(--navy-logo);border-color:transparent}[data-tema="oscuro"] .etiq.oro{
  background:var(--alerta-f);color:var(--oro-txt);border-color:transparent}[data-tema="oscuro"] .pos.top{background:var(--seleccion);border-color:transparent;color:var(--oro-txt)}[data-tema="oscuro"] .select:hover,
[data-tema="oscuro"] .input:hover,
[data-tema="oscuro"] main select:hover,
[data-tema="oscuro"] main input:hover{border-color:rgba(255,255,255,.34)}[data-tema="oscuro"] .btn-2:hover{border-color:rgba(255,255,255,.34)}/* El <select> nativo hereda colores del sistema operativo: sobre oscuro se le
   fija panel y tinta,
y la flecha SVG del fondo pasa a la tinta secundaria. */
[data-tema="oscuro"] .select,
[data-tema="oscuro"] .filtro select,
[data-tema="oscuro"] main select{
  background-color:var(--panel);color:var(--tinta);
  /* `no-repeat` explícito: `main select` alcanza también <select> sin la clase
     `.select` (que es quien lo trae en la base), y sin esto la flecha SVG se
     repetía en MOSAICO de fondo en oscuro (el zigzag). Red de seguridad. */
  background-repeat:no-repeat;background-position:right 9px center;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23A5AACB' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}[data-tema="oscuro"] .select option,
[data-tema="oscuro"] .filtro select option,
[data-tema="oscuro"] main select option{
  background:#14172b;color:#e8eaf6}[data-tema="oscuro"] .input,
[data-tema="oscuro"] main input[type="text"],
[data-tema="oscuro"] main input[type="search"],
[data-tema="oscuro"] main input[type="number"],
[data-tema="oscuro"] main input[type="password"],
[data-tema="oscuro"] main input[type="date"]{
  background-color:var(--panel);color:var(--tinta)}/* El documento oficial en pantalla oscura: la cabecera institucional sigue
   legible (en papel manda §16,
que ya fuerza blanco y negro). */
[data-tema="oscuro"] .doc-cab .inst .col{color:var(--tinta)}[data-tema="oscuro"] .doc-cab{border-bottom-color:var(--navy-logo)}[data-tema="oscuro"] .dr-v{color:var(--tinta)}[data-tema="oscuro"] .doc-firmas .firma .linea{border-top-color:var(--tinta-2)}[data-tema="oscuro"] .doc-tabla th{border-bottom-color:var(--tinta-2)}[data-tema="oscuro"] .paso b{background:var(--seleccion);color:var(--tinta)}[data-tema="oscuro"] .crono-sem .marca .n{color:var(--tinta)}[data-tema="oscuro"] .hora-rango{color:var(--navy-logo)}[data-tema="oscuro"] .saltar{background:#314a97}[data-tema="oscuro"] .nota-honesta b{color:var(--navy-logo)}[data-tema="oscuro"] main .origen b{color:var(--navy-logo)}
/* La cinta en tema oscuro (capa de trabajo) conserva su navy de marca; el borde
   inferior gana presencia para separarla del lienzo oscuro. */
[data-tema="oscuro"] .cinta{border-bottom-color:rgba(255,255,255,.12)}
/* 🔴 Se retiró `[data-capa="mando"] .cinta{border-bottom-color:rgba(255,255,255,.10)}`
   junto con los otros 51 selectores de mando que heredaban colores del tema oscuro.
   Todos venían de cuando la capa de mando era una placa NAVY; la v9 la pasó a papel
   y las reglas se quedaron, pintando blanco translúcido y tintas claras sobre fondo
   claro. En la matriz de «Materias críticas» eso dejaba las notas a **1,42:1**
   medido en producción — ilegibles, y es lo que Hidalgo reportó como «no se ven
   claras las notas». El tema oscuro conserva las suyas intactas.
   La lección, por tercera vez hoy: cuando una superficie cambia, hay que re-medir
   TODO lo que se apoya en ella; y un selector que sobrevive a su superficie no da
   ningún error, solo deja de verse. */

/* ============================================================================
   20. MARCA VIVA — el PUENTE de Montessori (web/marca.js).
   ----------------------------------------------------------------------------
   🔴 V10. Aquí vivía la animación del ARCO: `pathLength="1"` + `stroke-dashoffset`
   de 1 a 0. Servía porque el arcoíris eran tres TRAZOS. El puente son tres
   RELLENOS, y sobre un relleno el trazo-guionado no hace nada: el mismo CSS
   habría dejado la marca quieta sin fallar en ningún sitio, que es la clase de
   avería que nadie reporta.

   El barrido se hace con `clip-path`, y de paso dice lo que la forma dice: un
   puente se cruza de izquierda a derecha. El estado BASE es la marca COMPLETA
   —sin clip—, así que si la animación no corre (pestaña de fondo, headless,
   reduced-motion) el logo se ve entero igual. Es la regla dura del proyecto: el
   contenido nunca depende de que la animación corra.
   ========================================================================== */
.marca-puente-svg{display:block}
/* Una pasada: la marca se cruza al aparecer.
   🔴 SIN `backwards`, y es la regla dura del proyecto, no una preferencia. Con
   `animation-fill-mode:backwards` el elemento adopta el fotograma `from` mientras
   la animación está pendiente — y `from` aquí es `inset(0 100% 0 0)`, o sea la
   marca recortada a nada. Donde la animación no llega a arrancar (sin compositor:
   pestaña de fondo, headless, una captura de QA) el logo se queda INVISIBLE. Pasó:
   la banda de bienvenida salió con un rectángulo blanco vacío donde va el puente.
   Ya había ocurrido antes en este proyecto con los accesorios de ZEO, por la misma
   causa de fondo: el estado base tiene que ser la marca COMPLETA, y la animación
   solo puede quitar de ahí mientras corre. */
@keyframes puente-cruza{from{clip-path:inset(0 100% 0 0)}}
.puente-dibuja{animation:puente-cruza 700ms var(--sale)}
/* Bucle del indicador de carga: entra por la izquierda y sale por la derecha,
   sin volver atrás — el ida y vuelta se lee como duda, y aquí lo que se está
   diciendo es «esto avanza». */
@keyframes puente-barre{
  0%{clip-path:inset(0 100% 0 0)}
  55%{clip-path:inset(0 0 0 0)}
  100%{clip-path:inset(0 0 0 100%)}
}
.puente-bucle{animation:puente-barre 1.6s var(--mueve) infinite}
@media (prefers-reduced-motion:reduce){
  .puente-dibuja,.puente-bucle{animation:none;clip-path:none}
}
.marca-carga{
  display:flex;flex-direction:column;align-items:center;gap:var(--e3);
  padding:64px var(--e5);color:var(--tinta-3);font-size:var(--t-xs);
}
.marca-carga p{margin:0}

/* Conmutadores de la cinta (tema y densidad) */
.cinta-ctrl{
  background:none;border:1px solid rgba(255,255,255,.18);border-radius:var(--r);
  color:var(--riel-txt);cursor:pointer;padding:6px;flex:none;display:inline-flex;
  transition:color var(--rapido) linear,background var(--rapido) linear,transform var(--rapido) var(--sale);
}
.cinta-ctrl:hover{color:var(--riel-tinta-act);background:var(--riel-activo)}
/* Conmutador activo (tema oscuro / densidad compacta): estado VISIBLE, no un
   icono suelto — aria-pressed lo mantiene tema.js. */
.cinta-ctrl[aria-pressed="true"]{color:var(--riel-tinta-act);background:var(--riel-fondo-act);
  border-color:rgba(255,255,255,.38)}
.cinta-ctrl svg{display:block}
/* El botón de tema muestra el glifo del tema al que CAMBIA. */
html[data-tema="claro"] .ic-sol{display:none}
html[data-tema="oscuro"] .ic-luna{display:none}

/* ============================================================================
   21. MOVIMIENTO (V7 §5.2) — cada pieza informa algo; nada más se anima.
   Todas las entradas son @keyframes con SOLO un `from{}`: el estado base es el
   final, así que si la animación no corre (pestaña de fondo, headless,
   reduced-motion) el contenido está completo igual. Ese es el arreglo del
   gotcha de Zeus (clase dentro de doble requestAnimationFrame = pantalla muda).
   ========================================================================== */

/* Cambio de pantalla — que es la misma app, no una recarga.
   View Transitions donde exista; el fallback es este fundido + 6 px. */
@keyframes ent-pantalla{from{opacity:0;transform:translateY(6px)}}
#contenido > .rejilla{animation:ent-pantalla var(--medio) var(--sale)}
/* V12 · entrada ESCALONADA de los paneles dentro de la rejilla: cada hoja aterriza
   una tras otra (40 ms), como el escalonado de las filas. Da la sensación de que la
   pantalla «se arma» en vez de aparecer de golpe — el detalle que se lee como caro.
   Solo los primeros 8; de ahí en adelante ya están a la vista. `backwards` mantiene
   el estado inicial antes del delay. */
@keyframes ent-panel{from{opacity:0;transform:translateY(10px)}}
#contenido > .rejilla > .panel,
#contenido > .rejilla > .cifras{animation:ent-panel var(--medio) var(--sale) backwards}
#contenido > .rejilla > *:nth-child(1){animation-delay:20ms}
#contenido > .rejilla > *:nth-child(2){animation-delay:70ms}
#contenido > .rejilla > *:nth-child(3){animation-delay:120ms}
#contenido > .rejilla > *:nth-child(4){animation-delay:170ms}
#contenido > .rejilla > *:nth-child(5){animation-delay:220ms}
#contenido > .rejilla > *:nth-child(6){animation-delay:270ms}
#contenido > .rejilla > *:nth-child(7){animation-delay:320ms}
#contenido > .rejilla > *:nth-child(8){animation-delay:370ms}
/* Las hojas responden al puntero: se elevan un punto. Es el mismo lenguaje del
   hover de las tejas, extendido a los paneles. Transición corta, solo sombra y
   desplazamiento (compositor), nunca layout. */
.panel{transition:box-shadow var(--normal) var(--sale),transform var(--normal) var(--sale)}
.panel:hover{box-shadow:var(--lift-2);transform:translateY(-2px)}
@media (prefers-reduced-motion:reduce){
  #contenido > .rejilla > .panel,#contenido > .rejilla > .cifras{animation:none}
  .panel{transition:none}
  .panel:hover{transform:none}
}
::view-transition-old(root){animation-duration:140ms;animation-timing-function:var(--sale)}
::view-transition-new(root){animation-duration:var(--medio);animation-timing-function:var(--sale)}

/* Filas de tabla al entrar — que la lista terminó de cargar.
   Escalonado de 40 ms, máximo 12 filas; de la 13 en adelante, sin animación. */
@keyframes ent-fila{from{opacity:0;transform:translateY(6px)}}
tbody.entra > tr{animation:ent-fila var(--normal) var(--sale) backwards}
tbody.entra > tr:nth-child(1){animation-delay:0ms}
tbody.entra > tr:nth-child(2){animation-delay:40ms}
tbody.entra > tr:nth-child(3){animation-delay:80ms}
tbody.entra > tr:nth-child(4){animation-delay:120ms}
tbody.entra > tr:nth-child(5){animation-delay:160ms}
tbody.entra > tr:nth-child(6){animation-delay:200ms}
tbody.entra > tr:nth-child(7){animation-delay:240ms}
tbody.entra > tr:nth-child(8){animation-delay:280ms}
tbody.entra > tr:nth-child(9){animation-delay:320ms}
tbody.entra > tr:nth-child(10){animation-delay:360ms}
tbody.entra > tr:nth-child(11){animation-delay:400ms}
tbody.entra > tr:nth-child(12){animation-delay:440ms}
tbody.entra > tr:nth-child(n+13){animation:none}

/* Menú de la cinta — de dónde salió: origen en su disparador. */
@keyframes ent-pop{from{opacity:0;transform:scale(.98)}}
.menu-pop:not([hidden]){animation:ent-pop 150ms var(--sale);transform-origin:top left}
/* En móvil el menú es una lista estática dentro del cajón: sin origen que valga. */
@media(max-width:1000px){.menu-pop:not([hidden]){animation:none}}

/* Línea de gráfico — dirección temporal (izq→der). pathLength=1 en el path. */
@keyframes traza{from{stroke-dashoffset:1}}
.grafico .linea.dibuja{stroke-dasharray:1 1;stroke-dashoffset:0;animation:traza 480ms var(--mueve)}

/* Columnas del histograma — magnitud: crecen desde el eje. */
@keyframes crece-y{from{transform:scaleY(0)}}
.grafico .col{transform-box:fill-box;transform-origin:bottom;animation:crece-y var(--medio) var(--sale)}

/* Barras y medidores horizontales — magnitud relativa: crecen desde el origen. */
@keyframes crece-x{from{transform:scaleX(0)}}
.barra-celda .relleno,.med-val,.reparto > span,.trans-b .sig{
  transform-origin:left;animation:crece-x var(--medio) var(--sale)}

/* Fila que se guarda (V7 §5.2) — destello de 1 s en verde muy suave: un velo
   barre la fila con clip-path y se desvanece con opacity (solo propiedades
   permitidas). El estado base es la fila normal: sin animación no queda nada
   pintado, y el guardado se confirma igual por el aviso. */
.tabla tbody td{position:relative}
@keyframes destello-guardada{
  0%{clip-path:inset(0 0 100% 0);opacity:1}
  40%{clip-path:inset(0);opacity:1}
  100%{clip-path:inset(0);opacity:0}
}
.tabla tbody tr.guardada > td::after{
  content:"";position:absolute;inset:0;background:var(--ok-f);pointer-events:none;
  animation:destello-guardada 1s linear forwards;
}

/* Botón — que el sistema oyó el clic. La paleta de comandos queda FUERA:
   se usa cien veces al día y no se anima (regla dura §5.1). */
.btn:active,.seg button:active,.menu-b:active,.excep-f:active,.atajo:active,
.buscar-abrir:active,.hamb:active,.sesion .salir:active,.cinta-ctrl:active,
.enlace:active,.enlace-nom:active,.pagina .ctrl .btn:active{transform:scale(.97)}

/* ============================================================================
   22. CAJÓN DE DETALLE (V7 §8.2) — panel lateral derecho sobre la lista.
   Entra con --cajon en 300 ms (única excepción a los 240 del catálogo: la curva
   iOS necesita el recorrido); sale en 160 ms. El estado abierto es la CLASE,
   no la animación: sin motion el panel está completo igual. Solo transform y
   opacity. `html.con-cajon` congela el scroll del fondo sin perder su posición.
   ========================================================================== */
html.con-cajon{overflow:hidden}
.cajon-velo{
  position:fixed;inset:0;z-index:var(--z-cajon);background:rgba(8,12,40,.42);
  opacity:0;transition:opacity var(--normal) linear;
}
.cajon-velo.abierto{opacity:1}
.cajon-velo.arrastrando{transition:none}
.cajon-panel{
  position:fixed;top:0;right:0;bottom:0;z-index:var(--z-cajon);
  max-width:calc(100vw - 28px);
  background:var(--panel);border-left:1px solid var(--linea-fuerte);
  box-shadow:var(--flota-alta);
  display:flex;flex-direction:column;min-width:0;
  transform:translateX(103%);
  transition:transform 300ms var(--cajon);
  touch-action:pan-y;
}
.cajon-panel.abierto{transform:none}
.cajon-panel.sale{transition:transform 160ms var(--sale)}
.cajon-panel.arrastrando{transition:none;user-select:none;cursor:grabbing}
.cajon-cab{
  display:flex;align-items:center;gap:var(--e3);flex:none;
  padding:var(--e3) var(--e4);border-bottom:1px solid var(--linea);cursor:grab;
}
.cajon-asa{width:4px;height:32px;border-radius:var(--r-full);background:var(--linea-fuerte);flex:none}
.cajon-cab .tt{flex:1;min-width:0}
.cajon-cab h2{font-size:var(--t-md);font-weight:650;color:var(--tinta);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cajon-cab .sub{font-size:var(--t-2xs);color:var(--tinta-3);margin-top:1px}
.cajon-x{background:none;border:0;color:var(--tinta-3);cursor:pointer;flex:none;
  padding:5px 8px;border-radius:var(--r);font-size:var(--t-sm);line-height:1}
.cajon-x:hover{background:var(--realce);color:var(--tinta)}
.cajon-cuerpo{flex:1;min-height:0;overflow-y:auto;padding:var(--e4);overscroll-behavior:contain}
.cajon-cuerpo .cifras{margin-bottom:var(--e4)}
.cajon-pie{
  flex:none;display:flex;gap:var(--e2);flex-wrap:wrap;
  padding:var(--e3) var(--e4);border-top:1px solid var(--linea);background:var(--panel-2);
}
/* Bloque de datos dentro del cajón (reusa el vocabulario da-k/da-v del doc) */
.cajon-ficha{display:flex;gap:var(--e5);flex-wrap:wrap;
  padding-bottom:var(--e3);border-bottom:1px solid var(--linea);margin-bottom:var(--e4)}

/* ============================================================================
   23. AVISOS (toast, V7 §5.2) — pila inferior-derecha, máximo 3. Entra y sale
   por el MISMO borde (240/160 ms): deslizar hacia ese borde = descartar.
   ========================================================================== */
.avisos{
  position:fixed;right:16px;bottom:16px;z-index:var(--z-aviso);
  display:flex;flex-direction:column;gap:8px;align-items:flex-end;
  pointer-events:none;max-width:min(380px,calc(100vw - 24px));
}
.aviso-t{
  pointer-events:auto;display:flex;align-items:center;gap:var(--e3);max-width:100%;
  background:var(--panel);color:var(--tinta);border:1px solid var(--linea-fuerte);
  border-left:3px solid var(--navy-logo);border-radius:var(--r-lg);
  box-shadow:var(--flota);padding:9px var(--e3) 9px var(--e4);font-size:var(--t-sm);
  opacity:0;transform:translateX(calc(100% + 24px));
  transition:transform 160ms var(--sale),opacity 160ms linear;
  touch-action:pan-y;
}
.aviso-t.entra{opacity:1;transform:none;
  transition:transform var(--medio) var(--sale),opacity var(--medio) linear}
.aviso-t.arrastrando{transition:none;user-select:none}
.aviso-t.ok{border-left-color:var(--ok)}
.aviso-t.alerta{border-left-color:var(--alerta)}
.aviso-t.riesgo{border-left-color:var(--riesgo)}
.aviso-t .tx{min-width:0;line-height:1.45}
.aviso-acc{background:none;border:0;cursor:pointer;color:var(--navy-logo);
  font:inherit;font-weight:700;font-size:var(--t-xs);
  padding:3px 7px;border-radius:var(--r);flex:none;white-space:nowrap}
.aviso-acc:hover{background:var(--realce)}
.aviso-x{background:none;border:0;color:var(--tinta-3);cursor:pointer;
  padding:3px 5px;border-radius:var(--r);flex:none;line-height:1}
.aviso-x:hover{background:var(--realce);color:var(--tinta)}

/* ============================================================================
   24. HOJA DE ATAJOS (?) y DIÁLOGO DE CONFIRMACIÓN — <dialog> nativos.
   Sin animación: los dos son pasos de teclado (regla dura §5.1).
   ========================================================================== */
/* 🔴 `hoja-atajos`, NO `atajos`: la clase `.atajos` ya es la rejilla de enlaces
   del Resumen (§11) y su display:grid auto-fit partía el diálogo en dos
   columnas — la cabecera en una, el cuerpo en otra, y las teclas FUERA de la
   caja. Nombre propio = cero herencia accidental. */
dialog.hoja-atajos{
  border:0;padding:0;border-radius:var(--r-lg);width:min(560px,calc(100vw - 32px));
  background:var(--panel);color:var(--tinta);box-shadow:var(--flota-alta);margin:auto;
}
dialog.hoja-atajos::backdrop{background:rgba(8,12,40,.42)}
.atajos-cab{display:flex;align-items:center;gap:var(--e2);
  padding:var(--e3) var(--e5);border-bottom:1px solid var(--linea)}
.atajos-cab h2{font-size:var(--t-md);font-weight:650;flex:1;min-width:0}
.atajos-cab .cajon-x{padding:5px 8px}
.atajos-cuerpo{padding:var(--e4) var(--e5) var(--e5);
  display:grid;grid-template-columns:1fr 1fr;gap:var(--e3) var(--e6)}
@media(max-width:640px){.atajos-cuerpo{grid-template-columns:1fr}}
.atajos-g{min-width:0}
.atajos-g h3{font-size:var(--t-2xs);font-weight:700;letter-spacing:.07em;
  text-transform:uppercase;color:var(--tinta-3);margin:var(--e2) 0 var(--e1)}
.atajo-f{display:flex;align-items:center;gap:var(--e3);padding:5px 0;
  font-size:var(--t-xs);color:var(--tinta-2)}
.atajo-f > span:first-child{min-width:0}
.atajo-f .teclas{margin-left:auto;display:flex;gap:4px;flex:none;align-items:center}
.atajo-f .teclas i{font-style:normal;color:var(--tinta-3);font-size:var(--t-2xs)}

dialog.confirma{
  border:0;padding:0;border-radius:var(--r-lg);width:min(400px,calc(100vw - 32px));
  background:var(--panel);color:var(--tinta);box-shadow:var(--flota-alta);margin:auto;
}
dialog.confirma::backdrop{background:rgba(8,12,40,.42)}
.confirma-cuerpo{padding:var(--e5)}
.confirma-cuerpo h2{font-size:var(--t-md);font-weight:650}
.confirma-cuerpo p{font-size:var(--t-xs);color:var(--tinta-2);margin-top:var(--e2);line-height:1.55}
.confirma-botones{display:flex;justify-content:flex-end;gap:var(--e2);margin-top:var(--e5)}

/* ============================================================================
   25. SECRETARÍA (esqueleto que ENSEÑA) y grupo «Acciones» de la paleta.
   ========================================================================== */
.construccion{display:flex;align-items:center;gap:var(--e2);flex-wrap:wrap;margin-bottom:var(--e3)}
.bloque-nota{margin-top:var(--e2)}
.paleta-mas{padding:7px var(--e4);font-size:var(--t-2xs);color:var(--tinta-3)}

/* ============================================================================
   26. SECRETARÍA (fase 7.4) — zona de soltar, lote de subida, vista previa y
   la celda que abre una corrección. Todo con los tokens de las dos capas: la
   bandeja se ve igual de bien sobre el lienzo claro que en tema oscuro.
   ========================================================================== */
.oculto-visual{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}

.soltar{
  margin-top:var(--e4);padding:var(--e8) var(--e4);text-align:center;cursor:pointer;
  border:1.5px dashed var(--linea-fuerte);border-radius:var(--r-lg);background:var(--panel-2);
  display:flex;flex-direction:column;gap:6px;align-items:center;
  transition:border-color var(--rapido) linear,background var(--rapido) linear;
}
.soltar b{font-size:var(--t-sm);color:var(--tinta)}
.soltar span{font-size:var(--t-2xs);color:var(--tinta-3);max-width:56ch}
.soltar:hover,.soltar:focus-visible{border-color:var(--navy-logo);background:var(--realce)}
/* `.encima` mientras se arrastra: sin señal, soltar el archivo es un acto a ciegas */
.soltar.encima{border-color:var(--navy-logo);background:var(--seleccion);border-style:solid}
.soltar.inhabilitado{opacity:.55;cursor:not-allowed}

.lote{margin-top:var(--e4);display:grid;gap:1px;background:var(--linea)}
.lote-f{
  display:grid;grid-template-columns:1fr 72px 90px minmax(150px,1fr);gap:var(--e3);
  align-items:center;padding:7px var(--e3);background:var(--panel);font-size:var(--t-xs);
}
.lote-n{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:550}
.lote-p{text-align:right;color:var(--tinta-3);font-variant-numeric:tabular-nums;font-size:var(--t-2xs)}
.lote-b{height:5px;border-radius:3px;background:var(--panel-2);overflow:hidden;border:1px solid var(--linea)}
.lote-b i{display:block;height:100%;width:0;background:var(--navy-logo);
  transition:width var(--medio) var(--sale)}
.lote-b i.va{width:70%} .lote-b i.fin{width:100%}
.lote-e{font-size:var(--t-2xs);color:var(--tinta-3)}
.lote-f.bien .lote-e{color:var(--ok)} .lote-f.bien .lote-b i{background:var(--ok)}
.lote-f.igual .lote-e{color:var(--tinta-2)}
.lote-f.mal .lote-e{color:var(--riesgo)} .lote-f.mal .lote-b i{background:var(--riesgo)}

.doc-prev{display:block;max-width:100%;margin-top:var(--e4);border:1px solid var(--linea);
  border-radius:var(--r);background:var(--panel-2)}

/* La nota en la pantalla de corrección: parece una celda, pero es un botón, y
   se ve que lo es. Deshabilitada cuando el rol no puede corregir: mejor que
   dejar escribir y devolver un 403 después. */
.nota-btn{
  background:none;border:1px solid transparent;border-radius:var(--r);
  padding:2px 8px;font:inherit;font-variant-numeric:tabular-nums;color:var(--tinta);
  cursor:pointer;min-width:52px;text-align:right;
}
.nota-btn:hover:not(:disabled){border-color:var(--linea-fuerte);background:var(--realce)}
.nota-btn:disabled{cursor:default;color:var(--tinta-2)}

/* ============================================================================
   27. ARCO — la mascota-asistente por voz. Flotante, marca Montessori.
   ========================================================================== */
.arco-fab{
  position:fixed;right:22px;bottom:22px;z-index:var(--z-pop, 90);
  width:62px;height:62px;border-radius:var(--r-full);border:0;cursor:pointer;
  background:radial-gradient(120% 120% at 50% 20%, #fff, #eef1fb);
  box-shadow:0 8px 24px rgba(3,4,94,.28), 0 2px 6px rgba(3,4,94,.18);
  display:grid;place-items:center;padding:0;
  transition:transform var(--rapido) var(--sale), box-shadow var(--rapido) linear;
}
.arco-fab:hover{transform:translateY(-2px) scale(1.04)}
.arco-fab:active{transform:scale(.97)}
.arco-fab:focus-visible{outline:2px solid var(--oro);outline-offset:3px}
/* 🔴 V10 · el alto sigue la PROPORCIÓN del logo (361×168 = 2,15:1), no la del
   arcoíris que había antes (44×30 = 1,47:1). Con las medidas viejas el puente
   salía achatado un 45 %: la manera más rápida de arruinar un logo es meterlo en
   una caja que no es la suya, y aquí el CSS gana a los atributos del SVG. */
.arco-fab-svg{width:42px;height:20px;overflow:visible}
/* el arco "respira"; cuando Arco habla, late */
@keyframes arco-respira{0%,100%{transform:translateY(0)}50%{transform:translateY(-1.5px)}}
.arco-fab-svg{animation:arco-respira 3.2s ease-in-out infinite}
:root.arco-habla .arco-fab-svg,:root.arco-habla .arco-cab-svg{animation:arco-late .6s ease-in-out infinite}
@keyframes arco-late{0%,100%{transform:scale(1)}50%{transform:scale(1.08)}}
@media (prefers-reduced-motion: reduce){.arco-fab-svg{animation:none}}

.arco-panel{
  position:fixed;right:22px;bottom:96px;z-index:var(--z-pop, 90);
  width:min(380px, calc(100vw - 32px));max-height:min(560px, calc(100vh - 140px));
  background:var(--panel);border:1px solid var(--linea-fuerte);border-radius:16px;
  box-shadow:0 18px 50px rgba(3,4,94,.28);display:flex;flex-direction:column;overflow:hidden;
  animation:arco-entra var(--medio) var(--sale);
}
.arco-panel[hidden]{display:none}
@keyframes arco-entra{from{opacity:0;transform:translateY(10px) scale(.98)}to{opacity:1;transform:none}}
.arco-cab{display:flex;align-items:center;gap:10px;padding:12px 14px;
  background:linear-gradient(180deg,#070826,#0b0d34);color:#fff}
.arco-cab-svg{width:36px;height:17px;flex:none;overflow:visible}
.arco-cab b{font-size:var(--t-md);display:block;letter-spacing:-.01em}
.arco-cab .arco-sub{font-size:var(--t-2xs);color:#a5aacb}
.arco-x{margin-left:auto;background:rgba(255,255,255,.12);border:0;color:#fff;
  width:26px;height:26px;border-radius:8px;cursor:pointer;font-size:13px}
.arco-x:hover{background:rgba(255,255,255,.22)}

.arco-charla{flex:1;overflow-y:auto;padding:14px;display:flex;flex-direction:column;gap:10px;
  background:var(--panel-2)}
.arco-msg{max-width:88%;font-size:var(--t-sm);line-height:1.5}
.arco-msg p{margin:0}
/* 🔴 La burbuja del agente se llamaba `.arco-msg.arco` y colisionaba con `.arco`
   —el MEDIDOR de arco de más abajo, que fija `width:62px;height:49px`—. Misma
   especificidad y el medidor va después, así que ganaba: la burbuja quedaba en
   62×49px y el texto salía a dos palabras por línea. El mensaje del usuario
   (`.arco-msg.yo`) nunca se rompió porque no lleva esa clase, y por eso el fallo
   parecía intermitente. Medido en producción: 62px de ancho, que es EXACTAMENTE
   el `width` del medidor. Se renombra el hablante a `.agente`; el medidor
   conserva `.arco`, que sí es un arco. */
.arco-msg.agente{align-self:flex-start;background:var(--panel);border:1px solid var(--linea);
  padding:10px 12px;border-radius:4px 14px 14px 14px;color:var(--tinta)}
.arco-msg.yo{align-self:flex-end;background:var(--navy-logo);color:#fff;
  padding:8px 12px;border-radius:14px 14px 4px 14px}
.arco-msg.agente b{color:var(--navy-logo);font-weight:700}
.arco-ficha{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:6px}
.arco-ficha b{font-size:var(--t-sm)}
.arco-curso{font-size:var(--t-2xs);color:var(--tinta-3)}
.arco-prom{margin-left:auto;font-weight:700;font-variant-numeric:tabular-nums;
  padding:2px 9px;border-radius:var(--r-full);font-size:var(--t-xs)}
.arco-prom.nivel-excelente,.arco-prom.nivel-bien{background:var(--ok-f);color:var(--ok)}
.arco-prom.nivel-justo{background:var(--alerta-f);color:var(--alerta)}
.arco-prom.nivel-riesgo{background:var(--riesgo-f);color:var(--riesgo)}
.arco-tags{display:flex;flex-wrap:wrap;gap:5px;margin-top:8px}
.arco-tag{font-size:var(--t-2xs);font-weight:650;padding:2px 8px;border-radius:var(--r-full)}
.arco-tag.fuerte{background:var(--ok-f);color:var(--ok)}
.arco-tag.floja{background:var(--riesgo-f);color:var(--riesgo)}
.arco-opciones{display:flex;flex-direction:column;gap:5px;margin-top:8px}
.arco-op{text-align:left;background:var(--panel-2);border:1px solid var(--linea);border-radius:8px;
  padding:7px 10px;cursor:pointer;font-size:var(--t-xs);color:var(--tinta)}
.arco-op span{color:var(--tinta-3);margin-left:6px}
.arco-op:hover{border-color:var(--navy-logo);background:var(--realce)}

.nota-trim{font-weight:700;font-size:var(--t-sm)}
.tareas-mini{display:flex;flex-wrap:wrap;gap:3px 6px;margin-top:4px;justify-content:flex-end}
.tareas-mini span{font-size:var(--t-2xs);color:var(--tinta-3);white-space:nowrap}
.tareas-mini b{color:var(--tinta);font-weight:650}

.form-cit{display:flex;flex-direction:column;gap:11px}
.campo-cit{display:flex;flex-direction:column;gap:4px}
.cit-fila{display:flex;gap:11px}
.cit-fila .campo-cit{flex:1}
.form-cit textarea.input{resize:vertical;font-family:inherit}

/* F5 · redaccion asistida. El texto PROPUESTO por el asistente y el APROBADO por una
   persona NO se ven igual, y no es decoracion: lo propuesto lo escribio una maquina y
   lo aprobado lo firma un docente. Si se vieran igual, la pantalla estaria invitando a
   mandar lo primero creyendo que es lo segundo. */
.cit-editor{min-height:220px;line-height:1.55}
.cit-editor.cit-propuesta{border-left:3px solid var(--alerta);background:var(--alerta-f)}
.cit-editor.cit-aprobado{border-left:3px solid var(--ok);background:var(--ok-f)}
.cit-marca:empty{display:none}
.cit-marca.cit-alerta{color:var(--riesgo);font-weight:650}
.cit-acciones{display:flex;flex-wrap:wrap;gap:var(--e2)}
.cit-pendiente{border-color:var(--alerta)}

.arco-descargas{display:flex;flex-wrap:wrap;gap:6px;margin-top:9px}
.arco-descarga{display:inline-flex;align-items:center;gap:6px;text-decoration:none;
  background:var(--navy-logo);color:#fff;border-radius:var(--r-full);
  padding:6px 12px;font-size:var(--t-2xs);font-weight:650}
.arco-descarga svg{flex:none}
.arco-descarga:hover{background:var(--navy-logo-2,var(--navy-logo));filter:brightness(1.08)}

.arco-barra{display:flex;gap:8px;padding:10px 12px;border-top:1px solid var(--linea);background:var(--panel)}
.arco-input{flex:1;border:1px solid var(--linea-fuerte);border-radius:var(--r-full);
  padding:9px 14px;font-size:var(--t-sm);color:var(--tinta);background:var(--panel-2)}
.arco-input:focus{outline:0;border-color:var(--navy-logo);box-shadow:0 0 0 3px rgba(49,74,151,.16)}
.arco-mic{flex:none;width:40px;border:0;border-radius:var(--r-full);cursor:pointer;
  background:var(--navy-logo);color:#fff;display:grid;place-items:center}
.arco-mic:hover:not(:disabled){background:var(--navy)}
.arco-mic:disabled{opacity:.4;cursor:not-allowed}
.arco-mic.escucha{background:var(--riesgo);animation:arco-late .8s ease-in-out infinite}

@media print{.arco-fab,.arco-panel{display:none !important}}

/* ============================================================================
   28. AVATAR de persona — monograma de iniciales, color estable por nombre.
   Ocho tintes de marca; texto oscuro AA sobre cada tinte. No es una cara
   inventada: distingue de un vistazo y cede el sitio a la foto real si la hay.
   ========================================================================== */
.avatar-p{display:inline-grid;place-items:center;flex:none;border-radius:var(--r-full);
  font-weight:700;letter-spacing:.02em;line-height:1;overflow:hidden;
  font-variant-numeric:tabular-nums;user-select:none;
  /* 🔴 UNA sola celda con nombre. El avatar lleva dos hijos —el monograma de
     iniciales y, si existe, la foto encima— y un `inline-grid` sin plantilla los
     habría puesto en DOS FILAS: las iniciales arriba y la cara debajo, con la
     fila deformada. Nombrar la celda y meter a los dos en ella es lo que los
     superpone, y es lo que permite que el monograma quede DEBAJO como respaldo
     cuando la imagen no carga. */
  grid-template-areas:"retrato"}
.avatar-p > *{grid-area:retrato}
/* Las iniciales, cuando hay foto encima: ocupan la misma celda y quedan DEBAJO.
   Sin este span el monograma era un nodo de texto y la rejilla lo mandaba a una
   segunda fila (ver el comentario de `avatar()` en ui.js). */
.avatar-ini{display:grid;place-items:center;width:100%;height:100%}
.avatar-p.av-xs{width:20px;height:20px;font-size:8px}
.avatar-p.av-s{width:26px;height:26px;font-size:10px}
.avatar-p.av-m{width:34px;height:34px;font-size:13px}
/* `xl` es el de la FICHA del docente: ahí la cara es el sujeto de la pantalla, no
   un adorno de fila. Una clase que el JS usa y el CSS no define deja el avatar del
   tamaño por defecto sin avisar de nada. */
.avatar-p.av-xl{width:calc(2 * var(--e8) + var(--e4));height:calc(2 * var(--e8) + var(--e4));
  font-size:var(--t-xl);border-radius:var(--r-lg)}

/* --- FICHA DEL DOCENTE ------------------------------------------------------ */
.ficha-doc-enc{display:flex;align-items:center;gap:var(--e4);margin-bottom:var(--e4)}
.ficha-doc-tt{min-width:0}
.ficha-doc-tt b{display:block;font-family:var(--display);font-size:var(--t-xl);
  font-weight:600;letter-spacing:-.02em;line-height:1.15;color:var(--tinta)}
.ficha-doc-tt span{display:block;font-size:var(--t-sm);color:var(--tinta-3);margin-top:3px}
.ficha-doc-rol{display:grid;gap:1px;margin:var(--e4) 0 var(--e3);
  background:var(--panel);border-radius:var(--r-xl);box-shadow:var(--lift);overflow:hidden}
.ficha-doc-rol-i{padding:11px var(--e4);border-top:1px solid var(--linea)}
.ficha-doc-rol-i:first-child{border-top:0}
.ficha-doc-rol-i b{display:block;font-size:var(--t-xs);font-weight:650;color:var(--tinta);
  letter-spacing:.02em}
.ficha-doc-rol-i span{display:block;font-size:var(--t-sm);color:var(--tinta-2);margin-top:2px}
/* Lo que NO consta se ve distinto de lo que consta: si un hueco se pinta igual que
   un dato, se lee como dato. Sin barra lateral de color (§8): tinta y fondo. */
.ficha-doc-rol-i.sin{background:var(--panel-2)}
.ficha-doc-rol-i.sin b{color:var(--tinta-2)}
.ficha-doc-rol-i.sin span{color:var(--tinta-3)}
.ficha-doc-nota{font-size:var(--t-xs);color:var(--tinta-3);margin-bottom:var(--e4)}
.ficha-doc-nota b{color:var(--tinta-2)}
.avatar-p.av-l{width:46px;height:46px;font-size:17px}
.agenda-selectores{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,14rem),1fr));
  gap:var(--e4);margin:var(--e4) 0}
.agenda-selectores .campo,.agenda-selectores select{min-width:0;width:100%;max-width:100%}
.agenda-consolidado{margin:var(--e4) 0}
.agenda-entrega{padding:var(--e4) 0;border-bottom:1px solid var(--linea);margin-bottom:var(--e4)}
.agenda-actividad-texto,.agenda-actividad-check{grid-column:1/-1}
.agenda-actividad-check{display:flex;align-items:center;gap:var(--e2);font-size:var(--t-sm)}
.avatar-img{width:100%;height:100%;object-fit:cover;
  /* Cubre el monograma por completo: si quedara medio píxel de borde se vería un
     anillo de color alrededor de la cara. */
  border-radius:inherit}
/* Los ocho tintes: fondo suave + texto del mismo matiz oscurecido (AA). */
.av-1{background:#dde4f5;color:#274690} .av-2{background:#f6e6c6;color:#8a5a00}
.av-3{background:#cfebdd;color:#166a47} .av-4{background:#f3d8d6;color:#9e2b23}
.av-5{background:#dce8f0;color:#245683} .av-6{background:#e7e0f2;color:#5b3f86}
.av-7{background:#fbe0cf;color:#9a4a16} .av-8{background:#d6eaec;color:#166270}
:root:not([data-theme="light"]) .av-1,:root[data-theme="dark"] .av-1{background:#2a3557;color:#c6d2f2}
:root:not([data-theme="light"]) .av-2,:root[data-theme="dark"] .av-2{background:#4a3b1a;color:#f0d79b}
:root:not([data-theme="light"]) .av-3,:root[data-theme="dark"] .av-3{background:#1e4635;color:#9ee0c0}
:root:not([data-theme="light"]) .av-4,:root[data-theme="dark"] .av-4{background:#4e2420;color:#f2b3ad}
:root:not([data-theme="light"]) .av-5,:root[data-theme="dark"] .av-5{background:#243b4e;color:#aed0ec}
:root:not([data-theme="light"]) .av-6,:root[data-theme="dark"] .av-6{background:#382b52;color:#d2c2f0}
:root:not([data-theme="light"]) .av-7,:root[data-theme="dark"] .av-7{background:#4e3220;color:#f2c4a0}
:root:not([data-theme="light"]) .av-8,:root[data-theme="dark"] .av-8{background:#1e4650;color:#a6dbe2}

.persona-chip{display:inline-flex;align-items:center;gap:8px;min-width:0}
.persona-txt{display:flex;flex-direction:column;min-width:0;line-height:1.25}
.persona-nom{font-size:var(--t-xs);color:var(--tinta);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.persona-sub{font-size:var(--t-2xs);color:var(--tinta-3);white-space:nowrap}

/* Avatar del docente en la celda de horario y en enlaces de persona */
.hora-doc{display:flex;align-items:center;gap:5px;margin-top:3px;font-size:var(--t-2xs);color:var(--tinta-3)}
.hora-doc span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.persona-enlace{display:inline-flex;align-items:center;gap:7px}

/* ============================================================================
   GABI · CONTABLE — ENTORNO PROPIO A PÁGINA COMPLETA
   Decisión de Hidalgo (2026-08-13): GABI no es un desplegable del menú
   académico sino un ERP propio. Al entrar (#gabi), [data-entorno="gabi"] oculta
   el chrome académico (cinta + fila de contexto) y el entorno ocupa la pantalla.
   Referencia de UX: MIDAS (barra lateral de módulos → secciones → listados). NO
   clona sus colores: usa la identidad del sistema (navy de marca, tokens de tema).
   Tema claro/oscuro: hereda los tokens (data-capa=trabajo), no se fuerza oscuro.
   ========================================================================== */
[data-entorno="gabi"] .riel,
[data-entorno="gabi"] .riel-velo,
[data-entorno="gabi"] .cinta,
[data-entorno="gabi"] .contexto{display:none!important}
[data-entorno="gabi"] #app{
  grid-template-columns:minmax(0,1fr);height:100vh;min-height:100vh;overflow:hidden;
}
[data-entorno="gabi"] #contenido{padding:0;display:flex;min-height:0;flex:1}

.gabi-shell{
  flex:1;display:grid;grid-template-columns:var(--gabi-riel,266px) minmax(0,1fr);
  min-height:0;background:var(--lienzo);
}

/* --- Barra lateral ---------------------------------------------------------- */
.gabi-riel{
  background:var(--riel);color:var(--riel-txt);min-height:0;overflow-y:auto;
  overscroll-behavior:contain;display:flex;flex-direction:column;
  border-right:1px solid var(--riel-linea);
}
.gabi-riel-cab{
  display:flex;align-items:center;gap:10px;padding:14px 16px;position:sticky;top:0;
  background:var(--riel);z-index:2;border-bottom:1px solid var(--riel-linea);text-decoration:none;
}
.gabi-riel-cab .marca{
  font-weight:800;letter-spacing:.02em;color:var(--panel);background:var(--navy-logo);
  padding:5px 9px;border-radius:var(--r);font-size:var(--t-md);flex:none;
}
.gabi-riel-cab .txt{line-height:1.15;min-width:0}
.gabi-riel-cab .txt b{display:block;color:var(--tinta);font-size:var(--t-sm);font-weight:650;
  font-family:var(--display);font-stretch:88%;letter-spacing:-.015em}
.gabi-riel-cab .txt i{display:block;font-style:normal;font-size:var(--t-2xs);color:var(--riel-txt-2)}

.gabi-nav{padding:8px 8px 24px;flex:1}
.gabi-nav-busca{padding:4px 4px 8px;position:sticky;top:61px;z-index:1;background:var(--riel)}
.gabi-nav-busca input{
  width:100%;height:34px;border:1px solid var(--riel-linea);border-radius:var(--r);
  background:var(--panel);color:var(--tinta);padding:0 10px;font:inherit;font-size:var(--t-xs);
  outline:none;
}
.gabi-nav-busca input::placeholder{color:var(--riel-txt-2)}
.gabi-nav-busca input:focus{border-color:var(--navy-logo);box-shadow:0 0 0 2px var(--seleccion)}
.gabi-nav-busca span{display:block;padding:4px 2px 0;color:var(--riel-txt-2);font-size:10px}
.gabi-inicio{
  display:flex;align-items:center;gap:9px;padding:9px 10px;border-radius:var(--r);
  color:var(--riel-txt);text-decoration:none;font-size:var(--t-sm);margin-bottom:4px;
}
.gabi-inicio:hover{background:var(--riel-activo);color:var(--tinta)}
.gabi-inicio.activo{background:var(--riel-fondo-act);color:var(--riel-tinta-act);font-weight:600}
.gabi-inicio .gi{width:16px;height:16px;flex:none;opacity:.85}

.gabi-grupo-rot{
  padding:12px 10px 5px;font-size:var(--t-2xs);font-weight:700;letter-spacing:.1em;
  text-transform:uppercase;color:var(--riel-txt-2);
}
.gabi-mod-caja{margin-bottom:1px}
.gabi-mod{
  display:flex;align-items:center;gap:9px;width:100%;text-align:left;cursor:pointer;
  background:none;border:0;color:var(--riel-txt);font:inherit;font-size:var(--t-sm);
  padding:8px 10px;border-radius:var(--r);
  transition:background var(--rapido) linear,color var(--rapido) linear;
}
.gabi-mod:hover{background:var(--riel-activo);color:var(--tinta)}
.gabi-mod .gi{width:16px;height:16px;flex:none;opacity:.8}
.gabi-mod .tx{
  flex:1;min-width:0;display:flex;flex-direction:column;align-items:flex-start;gap:3px;
}
/* El nombre del módulo se lee ENTERO: baja de línea antes que recortarse. Un
   «Cuentas p…» obliga a adivinar de qué módulo se trata, que es justo lo que la
   barra curada existe para evitar. Tope de 2 líneas por si un nombre se dispara. */
.gabi-mod .lbl{
  max-width:100%;overflow:hidden;white-space:normal;overflow-wrap:break-word;
  line-height:1.25;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-clamp:2;
}
.gabi-mod .cnt{
  font-size:10px;font-weight:700;color:var(--riel-txt-2);font-variant-numeric:tabular-nums;flex:none;
}
.gabi-mod .fl{width:11px;height:11px;flex:none;opacity:.6;transition:transform var(--rapido) ease-out}
.gabi-mod[aria-expanded="true"]{color:var(--riel-tinta-act);background:var(--riel-activo)}
.gabi-mod[aria-expanded="true"] .fl{transform:rotate(-180deg)}
/* Chip «principal» del módulo destacado (Cuentas por Cobrar).
   🔴 Decía «sobre el riel oscuro va un azul claro de marca, legible en claro y
   oscuro» y no lo era: con el riel en crema, ese azul claro medía 1,84:1. El color
   de una etiqueta no se puede elegir contra una superficie que ya cambió — sale del
   token que sigue al tema. */
.gabi-destacar{
  font-size:9px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;flex:none;
  color:var(--navy-logo);border:1px solid var(--linea-fuerte);
  border-radius:var(--r-full);padding:1px 6px;
}
[data-tema="oscuro"] .gabi-destacar{color:#9cace8;border-color:rgba(156,172,232,.45)}
.gabi-mod-sec{padding:2px 0 6px 8px;margin-left:12px;border-left:1px solid var(--riel-linea)}
.gabi-mod-sec[hidden]{display:none}
.gabi-grupo[hidden],.gabi-mod-caja[hidden],.gabi-seccion[hidden],.gabi-scr[hidden]{display:none}
.gabi-sec-rot{
  display:flex;align-items:center;gap:6px;width:100%;padding:8px 10px 5px;
  border:0;background:none;cursor:pointer;text-align:left;font:inherit;font-size:10px;
  font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--riel-txt-2);
}
.gabi-sec-rot span:first-child{flex:1;min-width:0}
.gabi-sec-rot .cnt{font-size:9px;letter-spacing:0;font-variant-numeric:tabular-nums;opacity:.8}
.gabi-sec-rot svg{width:9px;height:9px;flex:none;transition:transform var(--rapido) ease-out}
.gabi-sec-rot[aria-expanded="true"]{color:var(--riel-tinta-act)}
.gabi-sec-rot[aria-expanded="true"] svg{transform:rotate(-180deg)}
.gabi-seccion-lista[hidden]{display:none}
.gabi-scr{
  display:flex;align-items:center;gap:8px;padding:6px 10px;border-radius:var(--r);
  color:var(--riel-txt);text-decoration:none;font-size:var(--t-xs);line-height:1.3;
}
.gabi-scr:hover{background:var(--riel-activo);color:var(--tinta)}
.gabi-scr .pt{
  width:6px;height:6px;border-radius:50%;flex:none;
  border:1px solid var(--riel-txt-2);background:transparent;
}
.gabi-scr .pt.listo{background:var(--verde-marca);border-color:var(--verde-marca)}
/* Datos reales frente a maqueta (2026-09-25). El punto lleno = lee la base; el vacío =
   maqueta con datos de ejemplo. El chip del módulo lo dice con palabras. */
.gabi-reales{
  font-size:9px;font-weight:600;letter-spacing:.02em;flex:none;
  color:var(--verde-marca);border:1px solid currentColor;
  border-radius:var(--r-full);padding:1px 6px;
}
.gabi-sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
.gabi-inicio-cartera .gabi-nota{margin:0;color:var(--tinta-2)}
.gabi-scr .nm{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.gabi-scr[aria-current="page"]{background:var(--oro);color:#1b1300;font-weight:600}
.gabi-scr[aria-current="page"] .pt{border-color:#1b1300}
.gabi-scr[aria-current="page"] .pt.listo{background:#1b1300;border-color:#1b1300}

/* --- Columna principal ------------------------------------------------------ */
.gabi-main{display:flex;flex-direction:column;min-width:0;min-height:0;background:var(--lienzo)}
/* 🔴 El margen lateral de GABI vive en UN token y lo usan la cabecera y el cuerpo.
   Estaban puestos por separado —cabecera `0 16px`, cuerpo `var(--e5)` (20px)— y en
   pantalla eso deja el título 4 px a la izquierda del contenido que encabeza y el
   grupo de la derecha 4 px más afuera que el borde de los paneles. Cuatro píxeles no
   se leen como una decisión: se leen como que la pantalla está torcida, que es
   exactamente lo que se reportó («sigue descuadrado»).
   Dos números distintos para el mismo margen se separan solos en el siguiente
   cambio; un token no puede desalinearse consigo mismo. */
.gabi-cab{
  display:flex;align-items:center;gap:12px;padding:0 var(--gabi-inset);height:56px;
  flex:none;background:var(--panel);border-bottom:1px solid var(--linea);
}
.gabi-menu-b{
  display:none;background:none;border:0;cursor:pointer;color:var(--tinta-2);padding:6px;
  border-radius:var(--r);
}
.gabi-menu-b svg{width:20px;height:20px}
.gabi-cab-tt{min-width:0;flex:1}
.gabi-cab-tt b{display:block;font-size:var(--t-md);font-weight:650;color:var(--tinta);line-height:1.2}
.gabi-miga{display:flex;align-items:center;gap:6px;font-size:var(--t-2xs);color:var(--tinta-3);
  overflow:hidden;white-space:nowrap;text-overflow:ellipsis;margin-top:1px}
.gabi-miga .sep{opacity:.6}
.gabi-miga .act{color:var(--tinta-2);font-weight:600}
.gabi-cab-der{display:flex;align-items:center;gap:8px;flex:none}
.gabi-cab-ic{
  display:flex;align-items:center;justify-content:center;width:32px;height:32px;flex:none;
  border:1px solid var(--linea-fuerte);border-radius:var(--r);background:var(--panel);
  color:var(--tinta-2);cursor:pointer;text-decoration:none;
}
.gabi-cab-ic:hover{background:var(--panel-2);color:var(--tinta)}
.gabi-cab-ic svg{width:16px;height:16px}
.gabi-quien{display:flex;flex-direction:column;line-height:1.15;text-align:right;max-width:180px}
.gabi-quien b{font-size:var(--t-xs);font-weight:650;color:var(--tinta);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.gabi-quien i{font-style:normal;font-size:10px;color:var(--tinta-3);text-transform:capitalize}
.gabi-volver{
  display:inline-flex;align-items:center;gap:6px;padding:6px 11px;border-radius:var(--r);
  background:var(--navy);color:#fff;font-size:var(--t-xs);font-weight:600;text-decoration:none;flex:none;
}
.gabi-volver:hover{background:#0a0c7a}
.gabi-volver svg{width:14px;height:14px}
.gabi-volver[hidden]{display:none}

.gabi-cuerpo{flex:1;overflow:auto;min-height:0;padding:var(--gabi-inset)}
.gabi-cuerpo:focus{outline:none}
.gabi-pant{display:flex;flex-direction:column;gap:var(--aire-rejilla)}
.gabi-pant .rejilla{gap:var(--aire-rejilla)}
.gabi-fuentes{display:flex;align-items:center;gap:6px;flex-wrap:wrap;color:var(--tinta-3);font-size:var(--t-2xs)}
.gabi-fuentes .rot{text-transform:uppercase;letter-spacing:.05em;font-weight:650}
.gabi-fuentes .fuente{padding:2px 7px;border:1px solid var(--linea-fuerte);border-radius:var(--r-full);
  background:var(--panel);color:var(--tinta-2);font-weight:600}
.gabi-fuentes code{margin-left:auto;color:var(--tinta-3);font-size:10px;overflow-wrap:anywhere}
/* La ruta original de MIDAS acompaña al código oficial: es trazabilidad,
   no información de trabajo, así que cede el `margin-left:auto` y baja de tono. */
.gabi-fuentes code.tenue{margin-left:0;opacity:.72}

/* --- Rótulo «datos de ejemplo» ---------------------------------------------- */
.gabi-demo{
  display:flex;align-items:center;gap:9px;padding:9px 13px;border-radius:var(--r);
  background:var(--alerta-f);border:1px solid color-mix(in srgb,var(--alerta) 34%,transparent);
  color:var(--alerta);font-size:var(--t-xs);font-weight:550;line-height:1.4;
}
.gabi-demo .pt{color:var(--alerta);font-size:10px;flex:none;line-height:1}

/* --- Chip de estado (borrador) ---------------------------------------------- */
.gabi-chip{
  font-size:var(--t-2xs);font-weight:700;letter-spacing:.03em;text-transform:uppercase;
  padding:2px 9px;border-radius:var(--r-full);
}
.gabi-chip.borrador{background:var(--alerta-f);color:var(--alerta);
  border:1px solid color-mix(in srgb,var(--alerta) 34%,transparent)}

/* --- Buscador del listado / acciones de fila -------------------------------- */
.gabi-busca{min-width:220px}
.gabi-busca .input{width:100%}
.gabi-acc-cel{text-align:right;white-space:nowrap;width:1%}
.gabi-acc-cel .btn{padding:3px 8px;font-size:var(--t-2xs)}

/* --- Filtros de consulta/reporte -------------------------------------------- */
.gabi-filtros{
  display:flex;flex-wrap:wrap;align-items:flex-end;gap:var(--e3);padding:var(--aire-panel);
  border-bottom:1px solid var(--linea);
}
.gabi-filtros .campo{min-width:170px}

/* --- Totales / cifras del proceso ------------------------------------------- */
.gabi-totales{
  display:flex;flex-wrap:wrap;gap:var(--e5);padding:var(--aire-panel);align-items:flex-end;
}
.gabi-total{display:flex;flex-direction:column;gap:2px}
.gabi-total .k{font-size:var(--t-2xs);color:var(--tinta-3);text-transform:uppercase;letter-spacing:.05em}
.gabi-total .v{font-size:var(--t-lg);font-weight:650;color:var(--tinta);font-variant-numeric:tabular-nums}
.gabi-total .u{font-size:var(--t-2xs);color:var(--tinta-3)}
.gabi-total.principal .v{font-size:var(--t-cifra);color:var(--navy-logo)}

/* Cabecera del proceso: campos en rejilla fluida */
.gabi-cab-forma,.gabi-forma{display:flex;flex-wrap:wrap;gap:var(--e3)}
.gabi-cab-forma .campo{min-width:200px;flex:1 1 200px}
.gabi-forma .campo{width:100%}
.gabi-impacto{margin-top:var(--e4);padding:var(--e4);border-top:1px solid var(--linea);color:var(--tinta-2)}
.gabi-impacto b{display:block;color:var(--tinta);font-size:var(--t-sm);margin-bottom:4px}
.gabi-impacto p{font-size:var(--t-sm);line-height:1.5}

/* --- Portada del entorno ---------------------------------------------------- */
.gabi-inicio-cuerpo p{color:var(--tinta-2);line-height:1.55;margin-bottom:var(--e4)}
.gabi-portada-enlaces{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:var(--e3)}
.gabi-portada-e{
  display:flex;flex-direction:column;gap:2px;padding:12px 14px;border-radius:var(--r);
  border:1px solid var(--linea-fuerte);background:var(--panel-2);text-decoration:none;
  transition:border-color var(--rapido) linear,transform var(--rapido) var(--sale);
}
.gabi-portada-e:hover{border-color:var(--navy-logo)}
.gabi-portada-e:active{transform:scale(.99)}
.gabi-portada-e b{color:var(--tinta);font-weight:600;font-size:var(--t-sm)}
.gabi-portada-e span{color:var(--tinta-3);font-size:var(--t-2xs)}

/* Botón de entorno en la cinta (flecha «salir/abrir»). */
.menu-b-entorno{text-decoration:none}
.menu-b-entorno .fl.abrir{width:13px;height:13px;opacity:.7}

/* --- Responsive: la barra lateral se vuelve cajón ---------------------------- */
@media (max-width:900px){
  .gabi-shell{grid-template-columns:minmax(0,1fr)}
  .gabi-riel{
    position:fixed;top:0;left:0;bottom:0;width:280px;z-index:var(--z-cajon);
    transform:translateX(-100%);transition:transform var(--medio) var(--cajon);
    box-shadow:var(--flota);
  }
  .gabi-shell.riel-abierto .gabi-riel{transform:translateX(0)}
  .gabi-menu-b{display:flex}
  /* el inset se estrecha para los dos a la vez: cabecera y cuerpo */
  .gabi-shell{--gabi-inset:var(--e3)}
  .gabi-quien{display:none}
  .gabi-volver span{display:none}
  .gabi-volver{padding:6px 8px}
}

/* ===========================================================================
   INSTRUMENTO DE MANDO (dirección v7, 2026-08-13)
   Decisión de Hidalgo: la v6 «se ve muy básica». El dato deja de ser una cifra
   en una caja y pasa a ser un INSTRUMENTO: placa navy, arco de escala, tramos
   contables y numeración tabular.

   Por qué placa navy y no otra cosa: DESIGN.md §3 mide que el dorado da 2,45:1
   sobre blanco (prohibido para texto) y 7,25:1 sobre navy. Sobre placa navy el
   oro por fin es legible, así que el instrumento no es un capricho estético —
   es la única superficie donde el color de marca puede llevar el dato.
   Sigue mandando §6: el arco es el ÚNICO gesto decorativo, y aquí ni siquiera
   decora, mide.
   =========================================================================== */
.cifras.instrumento{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));
  background:var(--navy);border:0;border-radius:var(--r-xl);overflow:hidden;
  box-shadow:var(--lift);
  /* Retícula tenue: da textura de panel sin dibujar nada encima del dato. */
  background-image:
    linear-gradient(rgba(255,255,255,.045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.045) 1px, transparent 1px);
  background-size:22px 22px, 22px 22px;
}
.cifras.instrumento > .cifra{padding:var(--aire-cifra) var(--e5);min-width:0;
  border-left:1px solid rgba(255,255,255,.13);border-radius:0;background:none;color:#fff}
.cifras.instrumento > .cifra:first-child{border-left:0}
.cifras.instrumento .kpi-k{color:#9fa8d4;letter-spacing:.1em;font-size:var(--t-2xs);
  font-weight:650;text-transform:uppercase;display:block;margin-bottom:7px}
.cifras.instrumento .kpi-v{color:#fff;font-variant-numeric:tabular-nums}
.cifras.instrumento .kpi-v .u{color:#9fa8d4}
.cifras.instrumento .kpi-pie{color:#8b93be}
/* Sobre navy, los tonos de estado del §4 no se leen: se aclaran manteniendo el
   significado. El ícono y el texto del semáforo siguen intactos. */
.cifras.instrumento .kpi-v.ok{color:#5bd59a}
.cifras.instrumento .kpi-v.alerta{color:var(--oro)}
.cifras.instrumento .kpi-v.riesgo{color:#ff8a80}
.cifras.instrumento .delta.sube{color:#5bd59a}
.cifras.instrumento .delta.baja{color:#ff8a80}
.cifras.instrumento .delta.igual{color:#8b93be}
.cifras.instrumento .cifra.principal{background:rgba(255,255,255,.04)}
/* La placa navy re-declara las fichas del instrumento: ahí la pista tiene que
   ser blanco translúcido y el dorado SÍ escribe (7,25:1 sobre navy). Es la misma
   regla de la v1, y por eso el instrumento nació aquí. */
.cifras.instrumento{
  --instr-pista:rgba(255,255,255,.22);
  --instr-marca:rgba(255,255,255,.42);
  --instr-val:var(--oro);
}
.cifras.instrumento .arco.ok .arco-val,
.cifras.instrumento .escala.ok .tramo.on{stroke:#5bd59a;background:#5bd59a}
.cifras.instrumento .arco.riesgo .arco-val,
.cifras.instrumento .escala.riesgo .tramo.on{stroke:#ff8a80;background:#ff8a80}
.cifras.instrumento .escala-marca{background:#fff;opacity:.75}
.cifras.instrumento .kpi-escala{color:#8b93be}

.cifra.con-medidor{display:flex;flex-direction:column;justify-content:space-between}
/* Los medidores se alinean por ABAJO: los pies de cifra tienen largos distintos
   y sin esto cada instrumento queda a una altura, que es justo lo que hace que
   una fila de datos parezca desordenada. */
.kpi-escala{
  font-size:var(--t-2xs);color:#8b93be;margin-top:3px;
  font-variant-numeric:tabular-nums;letter-spacing:.02em;
}

/* --- El arco --------------------------------------------------------------- */
/* 🔴 V10 · el instrumento pasa a FICHAS. Estaba cableado a la placa navy con
   `rgba(255,255,255,…)` y el dorado del logo, así que solo funcionaba ahí: sobre
   papel la pista se volvía invisible y el valor dorado da 2,45:1, por debajo del
   3:1 que pide un gráfico que transporta información. Por eso el instrumento
   —que existe desde el 13-ago y es la mejor pieza de este sistema— vivía en UNA
   pantalla de 44. Con las fichas, el mismo componente sirve en las dos
   superficies y el valor toma un color medido en cada una. */
:root{
  --instr-pista:var(--linea-fuerte);   /* la pista, sobre papel */
  --instr-marca:var(--tinta-4);
  --instr-val:#2e5e4e;        /* 7,31:1 verde bosque — dato/instrumento sobre papel cálido */
}
.arco{width:62px;height:49px;display:block;margin-top:7px;overflow:hidden}
.arco-pista{fill:none;stroke:var(--instr-pista);stroke-width:6;stroke-linecap:round}
.arco-val{fill:none;stroke:var(--instr-val);stroke-width:6;stroke-linecap:round;
  transition:stroke-dasharray var(--medio,320ms) var(--sale,ease-out)}
.arco-marca{stroke:var(--instr-marca);stroke-width:1.2;stroke-linecap:round}
.arco.ok .arco-val{stroke:var(--ok)}
.arco.alerta .arco-val{stroke:var(--alerta)}
.arco.riesgo .arco-val{stroke:var(--riesgo)}

/* --- La escala segmentada -------------------------------------------------- */
/* Doce tramos que se pueden CONTAR. Un relleno continuo se lee como barra de
   progreso; los tramos se leen como instrumento. */
.escala{position:relative;display:flex;gap:2px;margin-top:8px;height:14px;align-items:stretch}
.escala .tramo{flex:1;background:var(--instr-pista);border-radius:1px;opacity:.5}
.escala .tramo.on{background:var(--instr-val);opacity:1}
.escala .tramo.medio{background:var(--instr-val);opacity:.45}
.escala.ok .tramo.on,.escala.ok .tramo.medio{background:var(--ok)}
.escala.alerta .tramo.on,.escala.alerta .tramo.medio{background:var(--alerta)}
.escala.riesgo .tramo.on,.escala.riesgo .tramo.medio{background:var(--riesgo)}
/* Marca de referencia (la mínima, la meta): una línea, no un color. */
.escala-marca{
  position:absolute;top:-3px;bottom:-3px;width:2px;background:var(--tinta);opacity:.55;
  transform:translateX(-1px);
}

@media (prefers-reduced-motion: reduce){
  .arco-val{transition:none}
}

/* --- Avance curricular: captura manual (2026-08-13) ------------------------ */
/* Sustituye a la pantalla que leía la hoja de planificación. Aquí se marca lo
   dictado; el porcentaje se cuenta solo. */
.avance-barra{display:flex;gap:var(--e4);align-items:flex-end;flex-wrap:wrap;
  padding-bottom:var(--e4);border-bottom:1px solid var(--linea);margin-bottom:var(--e4)}
.avance-resumen{margin-bottom:var(--e4)}
.avance-cifra{display:flex;align-items:baseline;gap:6px;margin-bottom:6px}
.avance-cifra b{font-size:var(--t-cifra);font-weight:650;letter-spacing:-.02em;
  color:var(--tinta);font-variant-numeric:tabular-nums}
.avance-cifra span{font-size:var(--t-sm);color:var(--tinta-3)}
/* La escala fuera de la placa navy: los tramos van en el azul del logo, que
   sobre fondo claro sí se lee (el oro NO — DESIGN.md §3). */
.avance-resumen .escala .tramo{background:var(--linea-fuerte)}
.avance-resumen .escala .tramo.on,
.avance-cab .escala .tramo.on{background:var(--navy-logo)}
.avance-cab .escala .tramo{background:var(--linea)}
.avance-resumen .escala.ok .tramo.on,.avance-cab .escala.ok .tramo.on{background:var(--ok)}
.avance-resumen .escala.riesgo .tramo.on,.avance-cab .escala.riesgo .tramo.on{background:var(--riesgo)}
.avance-resumen .escala-marca,.avance-cab .escala-marca{background:var(--tinta)}

.avance-fila{border:1px solid var(--linea);border-radius:var(--r);margin-bottom:6px;
  background:var(--panel);overflow:hidden}
.avance-fila.sin-plan{background:var(--panel-2)}
.avance-cab{
  display:flex;align-items:center;gap:var(--e3);width:100%;text-align:left;
  background:none;border:0;font:inherit;cursor:pointer;padding:10px var(--e4);color:var(--tinta);
}
.avance-cab:hover{background:var(--realce)}
.avance-cab .tx{flex:1;min-width:0}
.avance-cab .tx b{display:block;font-size:var(--t-sm);font-weight:600;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.avance-cab .tx .quien{display:block;font-size:var(--t-2xs);color:var(--tinta-3)}
.avance-cab .cuenta{font-size:var(--t-sm);font-weight:650;flex:none;
  font-variant-numeric:tabular-nums;color:var(--tinta-2)}
.avance-cab .escala{flex:none;width:120px;margin-top:0}
.avance-temas{padding:2px var(--e4) var(--e3);border-top:1px solid var(--linea)}
.avance-temas[hidden]{display:none}   /* un `display` de autor le gana a [hidden] */
.avance-tema{display:flex;align-items:flex-start;gap:9px;padding:6px 2px;
  font-size:var(--t-sm);cursor:pointer;border-radius:var(--r)}
.avance-tema:hover{background:var(--realce)}
.avance-tema .n{font-size:var(--t-2xs);color:var(--tinta-3);min-width:16px;
  font-variant-numeric:tabular-nums;padding-top:2px}
.avance-tema .tt{flex:1;min-width:0}
.avance-tema input:disabled{cursor:not-allowed}
.avance-tema:has(input:disabled){cursor:default;opacity:.75}

/* --- Seguimiento de aula (5.º EGB a 3.º BGU) ------------------------------------- */
/* Apartado propio. Se distingue del panel general a propósito: sus cifras miden
   hábitos de trabajo, no calificaciones, y no deben leerse como si fueran lo
   mismo. */
.seg-resumen{padding-bottom:var(--e4);border-bottom:1px solid var(--linea);margin-bottom:var(--e4)}
.seg-resumen .escala{max-width:340px}
.seg-resumen .escala .tramo{background:var(--linea-fuerte)}
.seg-resumen .escala .tramo.on{background:var(--navy-logo)}
.seg-resumen .escala.ok .tramo.on{background:var(--ok)}
.seg-dato{font-size:var(--t-sm);color:var(--tinta-2);margin-top:8px}
.seg-form{display:flex;flex-wrap:wrap;gap:var(--e3) var(--e4);align-items:flex-end;
  padding:var(--e4);background:var(--panel-2);border:1px solid var(--linea);
  border-radius:var(--r);margin-bottom:var(--e4)}
.seg-form .filtro{min-width:150px}
.seg-form .filtro.seg-chk{min-width:0;flex-direction:row;align-items:center;gap:7px;
  padding-bottom:7px}
.seg-form .filtro.seg-chk > span{order:2}

/* ===========================================================================
   RIEL IZQUIERDO (v7, 2026-08-13) — sustituye a la cinta de menús desplegables
   Decisión de Hidalgo: «me sigo perdiendo». La cinta escondía 33 destinos
   detrás de un clic; el riel los deja a la vista, abre el dominio en curso y
   resalta el módulo actual. La orientación deja de depender de recordar dónde
   se hizo clic.

   El patrón ya existía y estaba validado en GABI, así que estas reglas SE
   COMPARTEN con las suyas (selectores agrupados abajo) en vez de duplicarse:
   dos rieles con reglas propias derivan, y entonces el mismo producto tiene dos
   navegaciones distintas.
   =========================================================================== */
#app{display:grid;grid-template-columns:264px 1fr;min-height:100vh}
.lienzo-col{display:flex;flex-direction:column;min-width:0;min-height:100vh}
.riel{
  display:flex;flex-direction:column;min-height:100vh;
  background:var(--riel);border-right:1px solid var(--riel-linea);
  position:sticky;top:0;max-height:100vh;
}
.riel-marca{
  display:flex;align-items:center;gap:9px;flex:none;text-decoration:none;
  padding:13px var(--e4);border-bottom:1px solid var(--riel-linea);
}
.riel-marca .escudo{background:var(--panel);border:1px solid var(--linea);border-radius:var(--r);padding:3px 5px;display:flex;flex:none}
.riel-marca .txt{line-height:1.15;min-width:0}
.riel-marca .txt b{display:block;font-size:var(--t-sm);font-weight:650;color:var(--tinta);
  font-family:var(--display);font-stretch:88%;letter-spacing:-.015em}
.riel-marca .txt span{display:block;font-size:var(--t-2xs);color:var(--riel-txt-2);letter-spacing:.02em}
.riel-nav{flex:1;overflow-y:auto;padding:8px}
.riel-pie{flex:none;padding:9px var(--e4);border-top:1px solid var(--riel-linea)}
.riel-pie .sesion{border-left:0;padding-left:0}

/* --- Reglas COMPARTIDAS con el riel de GABI -------------------------------- */
.gabi-mod,
.riel-mod{
  display:flex;align-items:center;gap:9px;width:100%;text-align:left;cursor:pointer;
  background:none;border:0;color:var(--riel-txt);font:inherit;font-size:var(--t-sm);
  padding:8px 10px;border-radius:var(--r);text-decoration:none;
  transition:background var(--rapido) linear,color var(--rapido) linear;
}
.gabi-mod:hover,.riel-mod:hover{background:var(--riel-activo);color:var(--tinta)}
.riel-mod .gi{width:16px;height:16px;flex:none;opacity:.8}
.riel-mod .lbl{
  flex:1;min-width:0;overflow:hidden;white-space:normal;overflow-wrap:break-word;
  line-height:1.25;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-clamp:2;
}
.riel-mod .cnt{
  font-size:10px;font-weight:700;flex:none;padding:1px 6px;border-radius:var(--r-full);
  background:var(--riesgo-f);color:var(--riesgo);font-variant-numeric:tabular-nums;
}
.riel-mod .fl{width:11px;height:11px;flex:none;opacity:.6;transition:transform var(--rapido) ease-out}
.riel-mod[aria-expanded="true"]{color:var(--tinta);background:var(--realce)}
.riel-mod[aria-expanded="true"] .fl{transform:rotate(-180deg)}
.riel-mod .fl.abrir{opacity:.5;width:12px;height:12px}
.riel-mod-entorno[aria-current="page"]{background:var(--riel-activo);color:var(--tinta)}

.riel-sub{padding:2px 0 6px 8px;margin-left:12px;border-left:1px solid var(--riel-linea)}
.riel-sub[hidden]{display:none}   /* un `display` de autor le gana a [hidden] */
.riel-it{
  display:flex;align-items:center;gap:8px;padding:6px 10px;border-radius:var(--r);
  color:var(--riel-txt);text-decoration:none;font-size:var(--t-xs);line-height:1.3;
}
.riel-it:hover{background:var(--riel-activo);color:var(--tinta)}
.riel-it .nm{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.riel-it .cnt{font-size:10px;font-weight:700;flex:none;color:var(--riesgo);font-variant-numeric:tabular-nums}
/* El módulo activo se marca con FONDO. Una franja lateral de color es el tell
   de interfaz generada que el DESIGN.md ya retiró una vez. */
/* V9 · el activo se marca con el tinte del ACENTO y peso, no con dorado sólido:
   el dorado es ≤10 % de la pantalla y no es un color de selección. Sigue sin
   franja lateral, que es el tell que el DESIGN.md ya retiró una vez. */
.riel-it[aria-current="page"]{background:var(--seleccion);color:var(--navy-logo);font-weight:650}
.riel-it[aria-current="page"] .cnt{color:var(--riesgo)}

/* La cinta se queda, pero delgada: solo buscador, tema, densidad. */
.lienzo-col .cinta{position:sticky;top:0;z-index:var(--z-barra);justify-content:flex-end}
.lienzo-col .cinta .hamb{display:none}

/* --- Móvil: el riel se vuelve cajón --------------------------------------- */
.riel-velo{
  position:fixed;inset:0;background:rgba(32,35,41,.38);z-index:var(--z-velo);
}
.riel-velo[hidden]{display:none}
@media (max-width:900px){
  #app{grid-template-columns:1fr}
  .riel{
    position:fixed;top:0;bottom:0;left:0;width:270px;z-index:var(--z-barra);
    transform:translateX(-100%);transition:transform var(--medio) var(--sale);
  }
  #app.riel-abierto .riel{transform:none}
  .lienzo-col .cinta{justify-content:space-between}
  .lienzo-col .cinta .hamb{display:inline-flex}
}
@media (prefers-reduced-motion: reduce){
  .riel{transition:none}
}

/* ===========================================================================
   §16 · «HOY» — la entrada por rol (rediseño V8, fase 1)
   ---------------------------------------------------------------------------
   Deliberadamente NO es una rejilla de tarjetas iguales: eso es el tell de §2
   del rediseño y, peor, hace que las cuatro preguntas pesen lo mismo cuando no
   pesan lo mismo. Aquí la primera pregunta —la deuda propia— ocupa el ancho y
   las demás se reparten debajo; el bloque se separa por LÍNEA y aire, no por
   sombra ni por franja lateral de color.
   =========================================================================== */
.hoy{display:block;min-width:0}
.hoy-encabezado{margin:0 0 var(--e5)}
.hoy-encabezado h2{font-size:var(--t-xl);line-height:1.2;margin:0}
.hoy-saludo{margin:var(--e1) 0 0;color:var(--tinta-2);font-size:var(--t-md)}

.hoy-rejilla{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--e4);
  /* Sin esto, la fila iguala alturas y un bloque de una línea se estira al alto
     del vecino de ocho: media pantalla de caja vacía. Cada bloque mide lo que
     tiene que decir. */
  align-items:start}
.hoy-rejilla>.hoy-bloque:first-child{grid-column:1 / -1}
/* V10 · y el ÚLTIMO, cuando queda solo en su fila: si no, deja media pantalla en
   blanco a su derecha, que es el hueco que Hidalgo señaló en la captura.
   🔴 La primera versión de esta regla usaba `odd` y estaba al revés — se escribió
   razonando la paridad de cabeza en vez de contando, y el hueco siguió ahí.
   Contando: el primero ocupa la fila entera, así que los que se reparten en
   parejas son del 2.º en adelante. El último queda huérfano cuando quedan
   IMPARES después del primero, o sea cuando el total es PAR y él es
   `nth-child(even)`. Con cuatro bloques —el reparto del directivo— aplica; con
   tres, el 2.º y el 3.º llenan su fila y no debe aplicar. */
.hoy-rejilla>.hoy-bloque:last-child:nth-child(even){grid-column:1 / -1}

/* 🔴 V10 · el bloque DEJA DE SER UNA TARJETA. Lo era —hoja blanca con borde— y al
   meterle tejas dentro habrían quedado cajas con borde dentro de una caja con
   borde. Una tarjeta anidada no es un matiz de gusto: duplica el marco, roba dos
   veces el mismo padding y hace que la pantalla se lea como acumulación en vez de
   como jerarquía. Ahora la sección es cabecera + contenido sobre el papel, y la
   caja la ponen las tejas, que es donde el borde SÍ significa algo (esto es una
   cifra, aquella es otra).

   El estado ya no necesita el borde del bloque: lo dicen el sello (color + ícono +
   texto) y el tono de la teja. */
.hoy-bloque{
  min-width:0;display:flex;flex-direction:column;gap:var(--e3);
}
.hoy-bloque[hidden]{display:none}

.hoy-cab{display:flex;align-items:baseline;gap:var(--e3);flex-wrap:wrap}
.hoy-pregunta{margin:0;font-size:var(--t-lg);line-height:1.25;font-weight:600;
  color:var(--tinta);min-width:0;flex:1}

/* Semáforo del sistema: color + ícono + texto. Nunca color solo. */
.hoy-sello{display:inline-flex;align-items:center;gap:6px;flex:none;
  font-size:var(--t-2xs);font-weight:700;padding:2px 8px;border-radius:var(--r-full);
  white-space:nowrap}
.hoy-sello .ic{width:14px;height:14px}
.hoy-sello[data-estado="alerta"]{color:var(--alerta);background:var(--alerta-f)}
.hoy-sello[data-estado="dato"]{color:var(--ok);background:var(--ok-f)}
.hoy-sello[data-estado="vacio"]{color:var(--nada);background:var(--nada-f)}

.hoy-resumen{margin:0;color:var(--tinta);font-size:var(--t-md)}
/* El hueco de un bloque usa el componente `vacio` del sistema (con la marca), no
   un párrafo suelto: así el «todavía no hay dato» se ve igual en las 44 pantallas
   y no en dos dialectos. `.hoy-vacio` se retiró — era un párrafo con franja
   IZQUIERDA de 1 px, que además roza el patrón prohibido de franja lateral. */
.hoy-bloque .vacio{background:var(--panel);border:1px solid var(--linea);
  border-radius:var(--r-lg);padding:var(--e6) var(--e5)}
/* 🔴 Y el motivo se lee al MISMO tamaño que un resumen con dato. El componente
   compartido lo pone a `--t-xs` porque en una tabla vacía es una nota al pie;
   aquí NO lo es: es la respuesta a la pregunta del bloque. Al mover el hueco al
   componente común se me colaron dos puntos de menos, y lo cazó la prueba que
   existía justo para eso — «un motivo en letra pequeña se lee como una excusa al
   pie». Es la clase de regresión que ninguna captura delata. */
.hoy-bloque .vacio .vacio-por-que{font-size:var(--t-md);color:var(--tinta-2);max-width:60ch}

.hoy-lista{list-style:none;margin:0;padding:0;display:flex;flex-direction:column}
.hoy-item{display:flex;align-items:baseline;gap:var(--e3);flex-wrap:wrap;
  padding:8px 0;border-top:1px solid var(--linea);min-width:0}
.hoy-item:first-child{border-top:0}
.hoy-item-t{font-weight:600;color:var(--tinta);font-size:var(--t-md);min-width:0;
  overflow-wrap:anywhere}
a.hoy-item-t{color:var(--navy-logo);text-decoration:none}
a.hoy-item-t:hover,a.hoy-item-t:focus-visible{text-decoration:underline}
.hoy-item-d{color:var(--tinta-2);font-size:var(--t-sm);min-width:0;
  overflow-wrap:anywhere}

.hoy-ir{margin-top:auto;align-self:flex-start;color:var(--navy-logo);
  font-size:var(--t-sm);font-weight:600;text-decoration:none}
.hoy-ir:hover,.hoy-ir:focus-visible{text-decoration:underline}

@media (max-width:768px){
  .hoy-rejilla{grid-template-columns:1fr}
}


/* ===========================================================================
   §17 · V9 · TIPOGRAFÍA DE TÍTULOS
   ---------------------------------------------------------------------------
   La jerarquía la llevan la FORMA y el espacio, no un peso de 800. Los títulos
   van en Bricolage con el eje de anchura condensado y tracking negativo que
   crece con el cuerpo; el texto y TODA cifra siguen en Inter con `tabular-nums`,
   que es lo que impide que los números bailen al actualizarse.
   =========================================================================== */
/* 🔴 V11 · los títulos DEJAN la display condensada. Bricolage con el eje de
   anchura a 84 % es una cara de cartel, y un cartel en cada título de pantalla es
   exactamente lo que hacía que el producto se leyera institucional («no tan
   institucional», dicho dos veces). Ahora los títulos van en la misma cara que el
   texto —Hanken— un peso arriba y con tracking negativo: la jerarquía la hacen el
   TAMAÑO (34 contra 14,5) y el peso, que es como la hace una herramienta.
   Bricolage no se borra: se queda en la puerta y en el membrete, donde un cartel
   es lo correcto porque ahí no se trabaja, se entra (DESIGN.md §10). */
.contexto h1,.hoy-encabezado h2,.panel-cab .tt h2,.doc-tit,
.gabi-tit,.arco-cab b{
  font-family:var(--serif);
  letter-spacing:-.015em;
  font-weight:600;
}
.contexto h1,.hoy-encabezado h2{letter-spacing:-.02em}
.bienv b,.login-marca b{font-family:var(--display-marca);font-stretch:84%}
/* Una cifra NUNCA va en la display: Inter tiene tabular-nums y Bricolage no
   garantiza el mismo avance por dígito. */
.kpi-n,.cifra,.hoy-item-d,.tabla td.n,.med-num{
  font-family:var(--sans);font-variant-numeric:tabular-nums}

/* ===========================================================================
   §18 · V9 · LAS CUATRO APLICACIONES
   ---------------------------------------------------------------------------
   El riel deja de listar los siete grupos a la vez —40 destinos en una columna,
   que es de donde venía el «estamos muy regados»— y muestra la repisa en la que
   estás. Arriba, «Hoy» (la entrada, que no pertenece a ninguna aplicación) y el
   selector de las cuatro.
   =========================================================================== */
.riel-entrada{margin-bottom:var(--e2)}

.riel-apps{
  display:flex;flex-direction:column;gap:2px;
  padding:var(--e2) 0 var(--e3);
  margin-bottom:var(--e2);
  border-bottom:1px solid var(--riel-linea);
}
.riel-apps-rot{
  /* Un rótulo de sección, en minúscula y discreto: los antetítulos en versalitas
     sobre cada bloque son un tell, y el DESIGN.md los prohíbe. */
  font-size:var(--t-2xs);color:var(--tinta-3);padding:0 10px 4px;letter-spacing:0;
}
.riel-app{
  display:flex;align-items:center;gap:9px;padding:7px 10px;border-radius:var(--r);
  color:var(--riel-txt);text-decoration:none;font-size:var(--t-sm);line-height:1.25;
  transition:background var(--rapido) linear,color var(--rapido) linear;
}
.riel-app .gi{width:16px;height:16px;flex:none;opacity:.75}
.riel-app .lbl{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.riel-app:hover{background:var(--riel-activo);color:var(--tinta)}
/* La aplicación activa se marca con FONDO y peso — nunca con franja lateral. */
.riel-app[aria-current="page"]{
  background:var(--seleccion);color:var(--navy-logo);font-weight:650;
}
.riel-app[aria-current="page"] .gi{opacity:1}

/* ===========================================================================
   §19 · V9 · MIRADOR — cifras, cruces y descargos
   ---------------------------------------------------------------------------
   `.descargos` es lo que separa una MEDIDA de una acusación: toda cifra cruzada
   arrastra qué NO dice. Va discreto pero legible (no letra pequeña de contrato),
   porque quien lee la cifra tiene que leer también su límite.
   =========================================================================== */
/* 🔴 Aquí había una SEGUNDA definición de `.cifras`, con su propio `.kpi-l`:
   `display:flex`, otro gap y otro padding. Al ser el mismo selector y estar
   declarada después, le ganaba a la banda canónica del §5 y **le cambiaba el
   layout a la banda de KPI de TODO el sistema**, no solo a la del Mirador — de
   grid con carriles iguales a flex. Nadie lo vio porque el resultado sigue
   siendo una fila de cifras: el fallo no era que se rompiera, era que la retícula
   dejó de ser la que el sistema declara.

   El Mirador pasa a usar el vocabulario canónico (`.kpi-k`), que es lo que el QA
   venía señalando como «vocabulario viejo .kpi-l» y se leía como una minucia de
   nomenclatura. No lo era: era el síntoma de un selector duplicado.
   La lección: en una hoja de 2.800 líneas, un nombre de clase repetido no da
   ningún error — se lo lleva el último que lo escribe. */

.lectura{margin:0;padding:var(--e3) var(--e4);color:var(--tinta-2);font-size:var(--t-md)}

.descargos{
  margin:0;padding:var(--e3) var(--e4) var(--e4);
  border-top:1px solid var(--linea);color:var(--tinta-2);font-size:var(--t-sm);
}
.descargos b{display:block;color:var(--tinta-3);font-size:var(--t-2xs);margin-bottom:4px;
  font-weight:600}
.descargos ul{margin:0;padding-left:1.1em}
.descargos li{margin:2px 0;overflow-wrap:anywhere}

/* El recorte del documento: de dónde salió cada campo propuesto. Va bajo el
   control, en tono secundario — es evidencia para contrastar, no una etiqueta. */
.recorte{display:block;margin-top:3px;color:var(--tinta-3);font-size:var(--t-2xs);
  line-height:1.35;overflow-wrap:anywhere}

/* ===========================================================================
   §22 · V10 · BENTO — la cifra se lee de lejos
   ---------------------------------------------------------------------------
   La entrada era una lista de enlaces con el número dentro de una frase, al mismo
   cuerpo que el resto. Hidalgo: «no es nada impactante». La causa no era el CSS:
   el servidor mandaba prosa (`sistema/hoy.py::cifra` cuenta el arreglo).

   🔴 Y esto no es la rejilla de baldosas de color del ERP con el que nos comparan.
   Ese patrón —seis rectángulos IGUALES con icono, título y cifra— es el que el
   stack de diseño del proyecto marca como prohibido, y por un motivo que se puede
   comprobar mirando su propia pantalla: cuando todas las piezas pesan lo mismo,
   ninguna resalta, y el resultado se lee plano aunque esté lleno de color. Además,
   allí las seis baldosas dicen $0.00.

   Aquí el impacto lo hacen tres cosas y ninguna es cromo:
     1. TAMAÑO · una teja ocupa dos carriles y su cifra va a 3rem. Solo una.
     2. INSTRUMENTO · el arco de escala y los doce tramos que el sistema ya tiene
        desde el 13-ago, y que vivían en UNA pantalla de 44.
     3. AIRE · la sección respira y las tejas se aprietan; el contraste entre
        denso y abierto es lo que hace que algo destaque.

   El color de una cifra es su SEMÁFORO y nada más (DESIGN.md §4). Ningún color de
   marca toca un número.
   =========================================================================== */
.bento{
  display:grid;gap:var(--e3);
  /* `auto-fit` reparte solo de cuatro a dos a una sin ninguna consulta de medios.
     `minmax(min(100%,…))` porque un carril `1fr` nace en `auto` y el hijo se
     dimensiona a su contenido: el grid blowout que este proyecto ya pagó dos
     veces en móvil. */
  grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr));
  /* `stretch` y no `start`: las tejas de una fila igualan alto y así los
     instrumentos (que llevan `margin-top:auto`) se apoyan todos en la misma
     línea. Con `start` cada teja medía lo suyo y los arcos quedaban a alturas
     distintas — el detalle que hace que una fila de datos parezca desordenada
     sin que se pueda decir por qué. */
  align-items:stretch;
}
/* V11 · la teja también flota, y la GRANDE es un bloque de color como los de la
   banda: en «Hoy» la teja grande es la respuesta a la pregunta de la sección, así
   que es el foco de su bloque. Las demás se quedan en hoja blanca — un tablero
   entero de bloques de color no tiene foco, tiene ruido. Una por sección. */
.teja{
  min-width:0;display:flex;flex-direction:column;gap:2px;
  background:var(--panel);border:0;border-radius:var(--r-xl);box-shadow:var(--lift);
  padding:var(--e5);text-decoration:none;color:inherit;
  transition:box-shadow var(--rapido) linear,transform var(--resorte-t) var(--resorte);
}
a.teja:hover{box-shadow:var(--lift-2)}
/* La teja grande: dos carriles y la cifra a otra escala. Es la jerarquía entera
   del bento, y por eso el JS solo se la pone a una. */
.teja-g{grid-column:span 2}
@media (max-width:560px){.teja-g{grid-column:span 1}}

.teja-r{
  font-size:var(--t-2xs);font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  color:var(--tinta-3);line-height:1.35;
  /* Reserva dos líneas SIEMPRE. Los rótulos tienen largos muy distintos («50
     bloques» vs «con 3 o más materias bajo la mínima») y sin esto la cifra de una
     teja arrancaba 15 px más abajo que la de su vecina: la fila se leía torcida.
     Reservar es más barato que truncar, porque el rótulo dice qué es la cifra. */
  min-height:calc(2 * 1.35em);
}
.teja-v{
  display:flex;align-items:baseline;gap:5px;margin-top:2px;
  font-size:var(--t-cifra);font-weight:650;letter-spacing:-.025em;line-height:1.02;
  color:var(--tinta);font-variant-numeric:tabular-nums;font-feature-settings:"ss01";
}
.teja-g .teja-v{font-size:2.5rem}
.teja-v .teja-pre{font-style:normal;font-size:.62em;font-weight:600;color:var(--tinta-3)}
.teja-v .teja-u{font-style:normal;font-size:var(--t-sm);font-weight:500;color:var(--tinta-3);
  letter-spacing:0}
/* El tono es del SEMÁFORO: la cifra se tiñe solo cuando el servidor declaró un
   estado, nunca para decorar. */
.teja-v.ok{color:var(--ok)} .teja-v.alerta{color:var(--alerta)} .teja-v.riesgo{color:var(--riesgo)}
.teja-p{font-size:var(--t-xs);color:var(--tinta-2);margin-top:4px;line-height:1.4}
.teja-e{font-size:var(--t-2xs);color:var(--tinta-3);margin-top:2px;
  font-variant-numeric:tabular-nums}
a.teja:hover{border-color:var(--linea-fuerte);transform:translateY(-1px)}
a.teja:focus-visible{outline:2px solid var(--navy-logo);outline-offset:2px}
/* El instrumento se alinea abajo: los pies tienen largos distintos y sin esto
   cada arco queda a una altura, que es lo que hace que una fila de datos parezca
   desordenada. */
.teja .arco,.teja .escala{margin-top:auto}
/* La línea de la escala existe SIEMPRE, con o sin texto: es lo que hace que el
   `margin-top:auto` de arriba cumpla lo que promete. Sin ella, una teja con pie de dos
   líneas y otra con pie y escala apoyaban su instrumento a alturas distintas. */
.teja-e{min-height:1.35em}
.teja-e.vacia{visibility:hidden}
.teja .arco{margin-left:-4px}
/* En la teja grande el instrumento crece con la cifra. Un arco de 62 px al lado
   de un número de 40 px se lee como un adorno; a 88 px se lee como el medidor
   que es. */
.teja-g .arco{width:88px;height:70px}

/* --- RANKING ---------------------------------------------------------------
   Ocho tejas son ocho énfasis, o sea ninguno: cuando hay muchas filas con número
   lo que se quiere leer es el ORDEN y la distancia del primero al resto. La barra
   lo dice de un vistazo y la cifra va escrita al lado — una barra sin número
   obliga a estimar, y aquí el número es el punto. */
.rank{list-style:none;margin:0;padding:0;display:grid;gap:1px}
.rank-f{
  display:grid;grid-template-columns:minmax(0,1fr) minmax(60px,26%) auto;
  align-items:center;gap:var(--e3);padding:7px 0;
  border-top:1px solid var(--linea);
}
.rank-f:first-child{border-top:0}
.rank-t{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-size:var(--t-md);font-weight:600;color:var(--tinta)}
/* Con cara, la fila es cara + nombre en una línea. El recorte por elipsis se mueve
   al nombre: si se dejara en el contenedor, `overflow:hidden` recortaría el avatar
   antes que el texto. */
.rank-t:has(.avatar-p){display:flex;align-items:center;gap:var(--e2);overflow:visible}
.rank-nom{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
a.rank-t{color:var(--navy-logo)}
a.rank-t:hover,a.rank-t:focus-visible{text-decoration:underline}
.rank-riel{display:block;height:9px;background:var(--panel-2);border-radius:2px;
  overflow:hidden}
.rank-val{display:block;height:100%;background:var(--instr-val);border-radius:2px;
  transform-origin:left center;animation:rank-crece var(--resorte-t) var(--resorte)}
.rank-val.ok{background:var(--ok)} .rank-val.alerta{background:var(--alerta)}
.rank-val.riesgo{background:var(--riesgo)}
@keyframes rank-crece{from{transform:scaleX(0)}}
.rank-n{font-size:var(--t-md);font-weight:650;color:var(--tinta);
  font-variant-numeric:tabular-nums;text-align:right;min-width:2.5ch}
/* El detalle de la fila baja al ancho completo: en la misma línea competía con la
   barra y en móvil la partía. */
.rank-d{grid-column:1 / -1;font-size:var(--t-xs);color:var(--tinta-3);margin-top:-2px}
@media (prefers-reduced-motion:reduce){
  .teja,.rank-val{transition:none;animation:none}
  a.teja:hover{transform:none}
}

/* ===========================================================================
   §20 · V9-b · EL ARCOÍRIS ORDENA LA SUITE
   ---------------------------------------------------------------------------
   Encargo de Hidalgo, 2026-08-17: «le quitaste la temática del colegio».
   Estaba en lo cierto y era medible — sobre la captura de la pantalla de
   entrada, el dorado institucional ocupaba el 0,05 % de los píxeles y el navy
   el 2,07 %; el 96,23 % era neutro. El PDF del boletín, en cambio, ya salía con
   cabecera navy, chip dorado y sello: el papel IMPRESO era institucional y la
   PANTALLA no. Esto sube la pantalla a lo que el documento ya hacía.

   La v2 hizo bien en retirar el navy como superficie (dos temperaturas en una
   misma aplicación, que es de donde venía el peso). Su error fue no poner nada
   de la marca en su lugar. Aquí la marca vuelve como SEÑAL, no como superficie:
   un color del logo por aplicación, en la banda de la cabecera y en el punto
   del riel. El papel no se toca.

   🔴 Por qué cuatro acentos no contradicen «un solo acento»: nunca se ven dos a
   la vez. Se está en UNA repisa, y su color es el único cromático de la
   pantalla. El acento sigue siendo uno; lo que cambia es cuál, y ese cambio ES
   la información. En «Hoy» —que no es de nadie— no manda un color: manda el
   arcoíris entero.

   Lo que esta sección NO hace, porque el DESIGN.md §8 lo prohíbe y ya se
   retiró una vez: franja lateral de color en el ítem activo. El activo se
   marca con fondo y peso, como hasta ahora.
   =========================================================================== */
[data-app="aula"]      {--app:var(--app-aula);      --app-txt:var(--app-aula-txt)}
[data-app="secretaria"]{--app:var(--app-secretaria);--app-txt:var(--app-secretaria-txt)}
[data-app="gabi"]      {--app:var(--app-gabi);      --app-txt:var(--app-gabi-txt)}
[data-app="mirador"]   {--app:var(--app-mirador);   --app-txt:var(--app-mirador-txt)}

/* --- LA CINTA (V10) -------------------------------------------------------
   Aquí había una banda PLANA de 3 px con el color de la repisa. Devolvía el
   color y no decía nada más: una barra de color es lo que produce cualquier
   plantilla, y el encargo era justamente que dejara de parecerlo.

   Ahora es la cinta del puente: `web/cinta-marca.svg` como MÁSCARA, rellena con
   el color de la aplicación. La forma es la del logo —tres cintas que arrancan
   gruesas a la izquierda, suben, barren adelgazándose y terminan cortadas en
   recto—, así que la misma información («en qué repisa estoy») se dice con el
   gesto de la casa. No se puede defaultear a esto: sale de este logo.

   Se pinta con máscara y no con la imagen a color porque un SVG referenciado no
   hereda las fichas del documento; con máscara el color vive en el CSS, donde el
   tema oscuro y las cuatro repisas ya lo resuelven solos. `-webkit-` primero: sin
   él, Safari < 15.4 pinta el rectángulo entero del color y la firma se convierte
   en la barra que veníamos de quitar. */
.contexto::before{
  content:"";position:absolute;inset:0 0 auto;height:var(--cinta-alto);
  /* 🔴 `background-color`, NO el atajo `background`. La primera versión ponía el
     atajo aquí y una imagen en el atajo de la regla de «Hoy», y transicionaba
     `background`: cambiar de repisa interpolaba entre una IMAGEN y un COLOR, que
     no es interpolable, y la cinta se quedaba en el estado intermedio —vacío—
     de forma intermitente. La forma es constante y lo único que cambia es el
     color: eso sí se interpola, y de paso el cambio de repisa se lee como que
     la MISMA cinta cambia de color, que es lo que se quiere decir. */
  background-color:var(--app);
  background-repeat:no-repeat;background-size:100% 100%;
  -webkit-mask:url("/web/cinta-marca.svg") 0 0/100% 100% no-repeat;
  mask:url("/web/cinta-marca.svg") 0 0/100% 100% no-repeat;
  transition:background-color var(--medio) linear;
}
.contexto{position:sticky;padding-top:calc(var(--e3) + var(--cinta-alto))}

/* «Hoy» no pertenece a ninguna aplicación: ahí la cinta va con los TRES colores
   del logo, que es el puente completo. Es la única pantalla que muestra la marca
   entera, y es la que ve todo el colegio al entrar.
   🔴 Aquí la máscara se RETIRA. La máscara lleva una sola cinta —la exterior—
   porque una repisa lleva una; dejarla puesta sobre la imagen a color recortaba
   las otras dos y la entrada salía en azul plano, con la marca completa a un
   `mask` de distancia. La prueba lo cazó buscando los tres colores del logo en
   la columna, no comprobando que «hubiera color». */
[data-app=""] .contexto::before,
html:not([data-app]) .contexto::before{
  background-color:transparent;
  background-image:url("/web/cinta-puente.svg");
  -webkit-mask:none;mask:none;
}

/* --- LA BIENVENIDA de la entrada (V10 fase 3) ------------------------------
   El membrete del colegio, en la única pantalla que no es de nadie. Aquí la
   marca manda: no hay dato que proteger ni tarea que estorbar, y la mitad
   inferior de la entrada estaba en blanco porque los bloques del rol son de
   alturas distintas.

   🔴 La banda es NAVY y no contradice a la v2. Lo que la v2 retiró —con razón—
   fue el navy como superficie de TRABAJO: dos temperaturas dentro de una misma
   tarea. Esto es un membrete acotado, sin ningún dato dentro y sin nada que
   capturar encima. El precedente es del propio producto: el boletín en PDF ya
   sale con cabecera navy y chip dorado, así que el papel impreso venía siendo
   institucional y la pantalla no.

   Sobre navy, el dorado del logo SÍ escribe (7,25:1) — es la regla de la v1 que
   nunca dejó de ser cierta: `#dd9817` no puede sobre claro y puede sobre navy.
   El blanco da 17,75:1. Medido, no supuesto. */
.bienv{
  background:var(--navy);border-radius:var(--r-xl);
  padding:var(--e4);
  margin-bottom:var(--aire-rejilla);
  /* V12 · sobre la mesa marfil, el membrete navy es un OBJETO apoyado, no una franja
     que parte la pantalla: se le da la misma elevación en capas que a las hojas
     (`--lift-2`) más un canto interior cálido, para que se lea como una placa que
     descansa sobre el papel y no como un bloque corporativo pegado al borde. */
  box-shadow:var(--lift-2), inset 0 1px 0 rgba(255,255,255,.06);
  /* Sin cinta al pie de la banda. La primera versión remataba la banda con la
     cinta tricolor por `border-image`, y en la captura se leía como un BORRÓN de
     colores de 12 px: la cinta necesita apoyarse en una superficie clara para que
     el adelgazamiento se distinga, y sobre el navy sus tres colores se juntaban.
     El sitio de la cinta es la cabecera y la tarjeta de cada repisa, sobre papel.
     La banda cierra sola con su radio — la lección es que un recurso de marca no
     sirve en todos los fondos por ser de marca. */
}
.bienv-cab{display:flex;align-items:center;gap:var(--e3)}
/* 🔴 Badge CLARO, también aquí. Los tres colores del logo son fijos y su cinta
   exterior #314a97 da 2,16:1 contra el navy: sobre la banda desaparecería justo
   la cinta principal. Un logo no se re-tinta para caber; el fondo le hace sitio.
   Misma regla y mismo motivo que el badge del riel. */
.bienv-escudo{
  display:inline-flex;align-items:center;justify-content:center;
  background:#fff;border-radius:var(--r-lg);padding:8px 10px;flex:none;
}
.bienv-escudo svg{width:64px;height:auto}
.bienv-tt{min-width:0}
.bienv-inst{
  margin:0;font-size:var(--t-2xs);letter-spacing:.06em;text-transform:uppercase;
  color:#c6ccea;                     /* 10,9:1 sobre navy */
}
.bienv-h{
  margin:2px 0 0;font-family:var(--display);font-size:1.375rem;font-weight:650;
  font-stretch:84%;letter-spacing:-.024em;color:#fff;line-height:1.1;
}
.bienv-lema{margin:3px 0 0;font-size:var(--t-md);color:var(--oro)}

/* Los accesos de Operativo y ERP comparten fila; en móviles estrechos se apilan.
   minmax(0,…) permite que el texto se ajuste sin ensanchar la página. */
.bienv-apps{
  display:grid;gap:var(--e3);margin-top:var(--e3);
  grid-template-columns:repeat(2,minmax(0,1fr));
}
.bienv-app{
  display:block;background:var(--panel);border-radius:var(--r-lg);
  padding:0 var(--e3) var(--e3);overflow:hidden;
  transition:transform var(--resorte-t) var(--resorte),box-shadow var(--rapido) linear;
}
/* Cada tarjeta lleva LA CINTA de su repisa: el mismo recurso que la cabecera, en
   su color. Así la tarjeta y la cabecera de esa aplicación dicen lo mismo con la
   misma forma, y entrar no cambia de idioma. */
.bienv-app-cinta{
  display:block;height:var(--cinta-alto);margin:0 calc(-1 * var(--e3)) var(--e2);
  background-color:var(--app);
  -webkit-mask:url("/web/cinta-marca.svg") 0 0/100% 100% no-repeat;
  mask:url("/web/cinta-marca.svg") 0 0/100% 100% no-repeat;
}
.bienv-app-n{
  display:block;font-family:var(--display);font-size:var(--t-lg);font-weight:650;
  font-stretch:88%;letter-spacing:-.014em;color:var(--tinta);
}
.bienv-app-s{display:block;margin-top:2px;font-size:var(--t-xs);color:var(--tinta-2)}
a.bienv-app:hover{transform:translateY(-2px);box-shadow:var(--flota)}
a.bienv-app:focus-visible{outline:2px solid var(--oro);outline-offset:2px}
/* La repisa cerrada NO es un enlace y se ve que no lo es. El motivo va escrito
   en el sitio del subtítulo: un candado sin frase deja adivinando. */
.bienv-app.cerrada{background:var(--panel-2);opacity:.72}
.bienv-app.cerrada .bienv-app-n{color:var(--tinta-2)}
@media (min-width:960px){
  .bienv{display:grid;grid-template-columns:minmax(0,1fr) minmax(280px,1fr);
    gap:var(--e4);align-items:center}
  .bienv-apps{margin-top:0}
}
@media (max-width:420px){
  .bienv-apps{grid-template-columns:1fr}
}
@media (prefers-reduced-motion:reduce){
  .bienv-app{transition:none}
  a.bienv-app:hover{transform:none}
}

/* --- LA PORTADA DE APLICACIÓN (V10 fase 2) ---------------------------------
   La franja de bienvenida al aterrizar en una repisa. Mismo membrete navy que la
   entrada, y por el mismo motivo: aquí no hay dato debajo ni nada que capturar
   encima, así que es el único sitio de una pantalla de trabajo donde el color
   puede ocupar superficie.

   🔴 Las cuatro son navy y la repisa la dice su CINTA, no el fondo. Lo contrario
   —cada banda del color de su aplicación— se descartó midiendo: el blanco sobre
   el verde de Secretaría da 3,10:1 y sobre el dorado de GABI 2,45:1, los dos bajo
   AA. Para arreglarlo habría que aclarar esos dos colores hasta que dejaran de ser
   los del logo, y entonces la banda ya no sería de la marca. Sobre navy el blanco
   da 17,75:1 y el dorado del logo 7,25:1 — es la regla de la v1 que sigue siendo
   cierta: `#dd9817` no escribe sobre claro y sí sobre navy.

   La cinta va a 16 px, algo más que en la cabecera: es la señal de repisa y aquí
   tiene sitio para que el adelgazamiento se lea.

   Y la franja es DELGADA a propósito. La primera versión medía 124 px y llevaba la
   pantalla de Estudiantes al 12 % de píxeles de marca, por encima del 6-9 % que el
   plan fija para una pantalla densa. El techo no es un capricho: encima de una
   tabla de 879 filas, cada píxel de membrete es alto que la tabla no tiene, y una
   pantalla de trabajo tiene que ser más callada que la entrada. Medido después:
   7,4 %. */
.portapp{
  position:relative;background:var(--navy);border-radius:var(--r-xl);
  margin:var(--aire-rejilla) var(--e5) 0;overflow:hidden;
}
.portapp-cinta{
  display:block;height:14px;background-color:var(--app);
  -webkit-mask:url("/web/cinta-marca.svg") 0 0/100% 100% no-repeat;
  mask:url("/web/cinta-marca.svg") 0 0/100% 100% no-repeat;
}
/* 🔴 Excepción medida: la cinta de Aula es `#314a97` y sobre el navy da 2,16:1 —
   la señal que identifica la repisa desaparecería justo en una de las cuatro. Se
   sube al azul luminoso del sistema (8,56:1 sobre navy), que es el MISMO matiz y
   el que ya usa el tema oscuro para este papel. El resto no lo necesita: el
   dorado da 7,25:1 y el verde 5,70:1. Mirador comparte el navy con el fondo, así
   que también toma la variante luminosa. */
.portapp[data-app="aula"] .portapp-cinta,
.portapp[data-app="mirador"] .portapp-cinta{background-color:#9db2f5}
.portapp-fila{
  display:flex;align-items:center;gap:var(--e3);
  padding:var(--e2) var(--e5) var(--e3);
}
/* Badge claro, por lo mismo que en el riel y en la entrada: los tres colores del
   logo son fijos y su cinta exterior no se lee contra el navy. */
.portapp-escudo{
  display:inline-flex;align-items:center;justify-content:center;
  background:#fff;border-radius:var(--r);padding:6px 9px;flex:none;
}
.portapp-tt{min-width:0}
.portapp-h{
  margin:1px 0 0;font-family:var(--display);font-size:var(--t-xl);font-weight:650;
  font-stretch:84%;letter-spacing:-.022em;color:#fff;line-height:1.1;
}
.portapp-s{margin:2px 0 0;font-size:var(--t-sm);color:var(--oro)}
@media (max-width:720px){
  .portapp{margin:var(--e3) var(--e3) 0}
  .portapp-fila{padding:var(--e3) var(--e4) var(--e4)}
}

/* La miga ya decía en qué aplicación estás; ahora lo dice también en su color.
   Usa el token `-txt`, nunca el de relleno: sobre papel el dorado no escribe. */
.miga b{color:var(--app-txt)}

/* El punto del riel: identifica cada repisa ANTES de entrar, que es cuando hace
   falta. 7 px, alineado al final de la fila para que no empuje el rótulo. */
.riel-app{position:relative;padding-right:24px}
.riel-app::after{
  content:"";position:absolute;right:10px;top:50%;transform:translateY(-50%);
  width:7px;height:7px;border-radius:50%;background:var(--pt-app,transparent);
  /* En el ítem inactivo el punto va a media tinta: presente, no gritando. */
  opacity:.55;transition:opacity var(--rapido) linear;
}
.riel-app[aria-current="page"]::after{opacity:1}
.riel-app[data-app="aula"]      {--pt-app:var(--app-aula)}
.riel-app[data-app="secretaria"]{--pt-app:var(--app-secretaria)}
.riel-app[data-app="gabi"]      {--pt-app:var(--app-gabi)}
.riel-app[data-app="mirador"]   {--pt-app:var(--app-mirador)}
/* La repisa activa se rotula en SU color, no en el navy genérico. */
.riel-app[aria-current="page"]{color:var(--app-txt)}

@media (prefers-reduced-motion:reduce){
  .contexto::before{transition:none}
}

/* --- El baño de la repisa -------------------------------------------------
   La banda de 3 px y el punto de 7 px devolvían el color, pero medido daban
   +0,4 puntos de píxel cromático: se ve de cerca y no «de lejos», que era el
   pedido. La cabecera entera toma ahora un baño del color de su repisa —muy
   diluido, 6 %— que cubre ~150 px a todo el ancho.

   🔴 Es un TINTE, no una segunda temperatura: el baño se mezcla contra el mismo
   panel blanco, así que la superficie sigue siendo una sola y el peso que la v2
   retiró no vuelve. La hoja de trabajo (donde se captura la nota) NO se tiñe:
   el color vive en la cabecera y en el riel, nunca bajo el dato.
   El contraste del título sobre el baño está medido en el navegador, no
   supuesto — `test_marca_por_aplicacion.py` lo vuelve a medir en cada corrida. */
.contexto{background:color-mix(in oklab, var(--app) 6%, var(--panel))}
/* En «Hoy» no hay repisa: la cabecera se queda en la hoja limpia y el arcoíris
   de la banda hace todo el trabajo. Sin esto heredaría el tinte del navy. */
html:not([data-app]) .contexto,[data-app=""] .contexto{background:var(--panel)}

/* --- La marca institucional del riel --------------------------------------
   Decía «Montessori · Sistema Académico». El colegio se llama Unidad Educativa
   Particular Montessori y su lema está en el logo y en el boletín impreso; en
   pantalla no aparecía por ningún lado. Ahora la cabecera del riel dice lo
   mismo que la cabecera del PDF, que es lo que el colegio reconoce como suyo. */
.riel-marca .txt em{
  display:block;font-size:var(--t-2xs);font-style:normal;color:var(--oro-txt);
  letter-spacing:.01em;margin-top:1px;
}

/* ===========================================================================
   §21 · LA PUERTA (login) — v9-b, 2026-08-17
   ---------------------------------------------------------------------------
   Es la pantalla que más gente ve del sistema, y era la única que seguía en la
   v1: fondo gris-azul frío, tipografía del navegador, y el producto se llamaba
   «Tablero Académico» —nombre que ya no usa nadie— mientras el sistema al que
   daba paso se llama «Sistema Académico». Entrar significaba cambiar de
   producto a mitad de camino.

   Aquí la marca SÍ manda: no hay dato que proteger ni tarea que estorbar, así
   que es el sitio donde el arcoíris puede ser grande. Es la excepción
   deliberada a la mesura del resto — dentro se trabaja, en la puerta se entra.
   =========================================================================== */
.acceso-body{background:var(--lienzo);margin:0}
.acceso{
  min-height:100dvh;                      /* dvh, no vh: en iOS la barra mueve el vh */
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:var(--e5);padding:var(--e6) var(--e4);
}
.acceso-hoja{
  width:100%;max-width:420px;background:var(--panel);
  border:1px solid var(--linea);border-radius:var(--r-lg);
  padding:var(--e8) var(--e6) var(--e6);text-align:center;
}
/* El puente de la portada. `height:auto` con las medidas puestas en el HTML:
   el navegador reserva el hueco antes de cargar el SVG y la tarjeta no salta. */
.acceso-marca{display:block;margin:0 auto var(--e4);max-width:100%;height:auto}
.acceso-inst{
  margin:0;font-size:var(--t-2xs);color:var(--tinta-3);
  letter-spacing:.04em;text-transform:uppercase;
}
.acceso-tit{
  margin:4px 0 0;font-family:var(--display);font-size:var(--t-xl);font-weight:650;
  font-stretch:84%;letter-spacing:-.022em;color:var(--tinta);line-height:1.15;
}
/* El lema en dorado legible sobre la hoja blanca: 4,60:1 — el único sitio donde
   el dorado escribe, y por eso usa la variante `-txt`, nunca el #dd9817. */
.acceso-lema{margin:2px 0 var(--e5);font-size:var(--t-sm);color:var(--oro-txt)}
.acceso-form{display:grid;gap:var(--e3);text-align:left}
.acceso-form label{
  display:grid;gap:4px;font-size:var(--t-xs);font-weight:600;color:var(--tinta-2);
}
.acceso-form input{
  font:inherit;font-size:var(--t-md);color:var(--tinta);
  background:var(--panel);border:1px solid var(--linea-fuerte);border-radius:var(--r);
  padding:9px 11px;width:100%;
  transition:border-color var(--rapido) linear,box-shadow var(--rapido) linear;
}
.acceso-form input:focus{
  outline:none;border-color:var(--navy-logo);
  box-shadow:0 0 0 3px var(--navy-f);
}
.acceso-btn{
  margin-top:var(--e2);font:inherit;font-size:var(--t-md);font-weight:600;
  color:#fff;background:var(--navy);border:0;border-radius:var(--r);
  padding:11px 18px;cursor:pointer;
  transition:background var(--rapido) linear,transform var(--rapido) linear;
}
.acceso-btn:hover{background:var(--navy-logo)}
.acceso-btn:active{transform:translateY(1px)}
/* El error solo aparece cuando el servidor lo declara (?e=1): un mensaje de
   fallo visible antes de intentar nada acusa a quien todavía no hizo nada. */
.acceso-error{display:none;margin:0;font-size:var(--t-xs);font-weight:600;color:var(--riesgo)}
.acceso-error.show{display:block}
.acceso-pie{margin:0;font-size:var(--t-2xs);color:var(--tinta-3);text-align:center}

@media (prefers-reduced-motion:reduce){
  .acceso-btn,.acceso-form input{transition:none}
  .acceso-btn:active{transform:none}
}

/* La marca del riel: el puente dentro de su badge claro. Ancho fijo y alto
   automático — el puente es apaisado (361×168) y forzarle una caja cuadrada lo
   deformaría, que es la manera más rápida de arruinar un logo. */
.marca-puente{display:block;width:34px;height:auto}

/* 🔴 El badge de la marca es SIEMPRE claro, también en tema oscuro.
   El logo tiene tres colores fijos —no se re-tinta, es la marca del colegio— y
   su cinta azul #314a97 da 1,78:1 contra el panel oscuro: desaparece. Sobre
   blanco da 8,21:1. Un logo no se adapta al tema; el tema le hace sitio.
   Medido el 2026-08-17 sobre el badge real del riel. */
:root[data-tema="oscuro"] .riel-marca .escudo{
  background:#fff;border-color:rgba(255,255,255,.22);
}

/* ===========================================================================
   SEGUIMIENTO ESTUDIANTIL · la libreta de un alumno
   ---------------------------------------------------------------------------
   Pedido de Hidalgo (2026-08-20): por estudiante, «como si fuera una libreta».
   La disposición es la de una libreta de verdad: a la izquierda la nómina para
   elegir a quién abrir, a la derecha su hoja. El foco es la HOJA — la lista es
   navegación y por eso ocupa 260 px y no compite.
   =========================================================================== */
.seg-cab{margin-bottom:var(--e4)}
.seg-marco{display:grid;grid-template-columns:260px minmax(0,1fr);gap:var(--e5);
  align-items:start}
.seg-col-lista{position:sticky;top:var(--e4);display:flex;flex-direction:column;
  gap:var(--e3);max-height:calc(100dvh - 140px)}
.seg-lista{overflow-y:auto;display:flex;flex-direction:column;gap:2px;
  background:var(--panel);border-radius:var(--r-lg);box-shadow:var(--lift);
  padding:var(--e2)}
.seg-lista > *{flex-shrink:0}
.seg-col-lista > .input{width:100%;min-height:var(--e8);flex-shrink:0}
.seg-col-lista > .campo{min-width:0;width:100%;flex-shrink:0;align-items:stretch}
.seg-col-lista > .campo .select,.seg-col-lista > .campo .input{width:100%;min-width:0;max-width:100%}
/* El curso encabeza su grupo: sin él la nómina de 400 nombres es una lista plana. */
.lista-grupo{font-size:var(--t-2xs);font-weight:650;letter-spacing:.08em;
  text-transform:uppercase;color:var(--tinta-4);padding:var(--e3) var(--e3) 4px}
.lista-item{
  appearance:none;border:0;background:transparent;cursor:pointer;text-align:left;
  font:500 var(--t-sm)/1.35 var(--sans);color:var(--tinta-2);
  padding:8px var(--e3);border-radius:var(--r);min-height:34px;
}
.lista-item:hover{background:var(--realce);color:var(--tinta)}
.lista-item.act{background:var(--riel-fondo-act);color:var(--riel-tinta-act);font-weight:600}
.seg-libreta{display:flex;flex-direction:column;gap:var(--e4);min-width:0}
.libreta-cab h2{font-family:var(--display);font-size:var(--t-xl);font-weight:600;
  letter-spacing:-.02em;color:var(--tinta);margin:0}
.libreta-cab p{font-size:var(--t-sm);color:var(--tinta-3);margin:2px 0 0}
/* Captura en una línea: es un formulario de trabajo, no un trámite. */
.form-linea{display:flex;flex-wrap:wrap;gap:var(--e3);align-items:flex-end;
  padding-top:var(--e4);margin-top:var(--e4);border-top:1px solid var(--linea)}
.form-linea label{display:flex;flex-direction:column;gap:4px;min-width:0}
.form-linea label > span{font-size:var(--t-2xs);font-weight:600;color:var(--tinta-3)}
.form-linea input[type=text],.form-linea input[type=number],.form-linea input[type=date]{
  font:400 var(--t-sm)/1 var(--sans);color:var(--tinta);background:var(--panel-2);
  border:1px solid var(--linea);border-radius:var(--r);padding:8px 10px;min-height:36px;
  max-width:180px}
.form-linea input[type=checkbox]{width:20px;height:20px;accent-color:var(--navy-logo)}
.form-linea input:focus-visible{outline:2px solid var(--navy-logo);outline-offset:1px}

.seg-formulario{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,12rem),1fr));
  gap:var(--e4);padding-top:var(--e4);margin-top:var(--e3)}
.seg-formulario label{gap:var(--e2)}
.seg-formulario label > span{font-size:var(--t-xs);color:var(--tinta-2)}
.seg-formulario input[type=text],.seg-formulario input[type=number],.seg-formulario input[type=date],
.seg-formulario textarea{width:100%;max-width:100%;min-width:0;box-sizing:border-box;
  font:400 var(--t-sm)/1.5 var(--sans);padding:var(--e2) var(--e3);background:var(--panel);
  color:var(--tinta);border:1px solid var(--linea-fuerte);border-radius:var(--r)}
.seg-formulario textarea{resize:vertical}
.seg-formulario textarea:focus-visible{outline:2px solid var(--navy-logo);outline-offset:1px}
.seg-campo-observaciones{grid-column:1/-1}
.seg-formulario > .btn{grid-column:1/-1;justify-self:end}

@media (max-width:900px){
  .seg-marco{grid-template-columns:1fr}
  .seg-col-lista{position:static;max-height:none}
  .seg-lista{max-height:240px}
}

/* Módulos principales (2026-08-20): Operativo y GABI. Antes eran cuatro
   «aplicaciones» con un color del logo cada una; con un solo módulo principal para el
   día a día, el color se queda arriba y los cinco submódulos se distinguen por nombre
   y orden — el logo tiene cuatro tonos y repetir uno haría que el color deje de
   orientar, que es justo lo que la prueba de marca prohíbe. */
[data-app="operativo"]{--app:var(--app-mirador); --app-txt:var(--app-mirador-txt)}
.riel-app[data-app="operativo"]{--pt-app:var(--app-mirador)}

/* ===========================================================================
   ICONOS QUE SE TRANSFORMAN · pedido de Hidalgo (2026-08-20)
   ---------------------------------------------------------------------------
   Cada icono trae dos o tres piezas y la transformación la hace CSS: al pasar el
   puntero, al enfocar con teclado y cuando el módulo está activo. No hay librería
   externa —la CSP prohíbe hosts externos y todo va inline— así que están escritos
   a mano con la misma rejilla y el mismo grosor que los demás.

   🔴 La regla que no se rompe: el movimiento INFORMA (algo se abrió, algo entró,
   algo late). Si no puede moverse —`prefers-reduced-motion`— la información tiene
   que estar igual en la forma, así que se deja el estado FINAL, no una animación
   más corta. Una animación que solo decora, en un sistema que alguien usa ocho
   horas, es ruido que cansa.
   =========================================================================== */
.ic-morph{overflow:visible}
.ic-morph > g{
  transform-box:fill-box;transform-origin:center;
  transition:transform .34s cubic-bezier(.2,.7,.3,1), opacity .24s ease;
}

/* --- Operativo: el latido. El anillo se expande y los tics aparecen. --------
   🔴 Acotado a `.op`. Sin la clase, el `opacity:.55` del anillo caía sobre la
   segunda pieza de TODOS los iconos —la flecha de Admisiones salía a media tinta—
   porque `.mp-b` es el nombre de posición, no de significado. Medido en el
   navegador: 0,55 donde debía ser 1. */
.ic-morph.op .mp-b{opacity:.55}
.ic-morph.op .mp-c{opacity:0;transform:scale(.7)}
.riel-app:hover .ic-morph.op .mp-b,.riel-app:focus-visible .ic-morph.op .mp-b,
.ic-morph.op.on .mp-b{opacity:1;transform:scale(1.08)}
.riel-app:hover .ic-morph.op .mp-c,.riel-app:focus-visible .ic-morph.op .mp-c,
.ic-morph.op.on .mp-c{opacity:.9;transform:scale(1)}

/* --- Los submódulos del riel: cada pieza se mueve a lo suyo ----------------- */
/* Dashboards: las cuatro placas se separan un pelo, como al recolocarse. */
.riel-mod:hover .ic-morph .mp-a,.riel-mod[aria-expanded="true"] .ic-morph .mp-a{
  transform:translate(-.9px,-.9px)}
.riel-mod:hover .ic-morph .mp-b,.riel-mod[aria-expanded="true"] .ic-morph .mp-b{
  transform:translate(.9px,-.9px)}
.riel-mod:hover .ic-morph .mp-c,.riel-mod[aria-expanded="true"] .ic-morph .mp-c{
  transform:translate(.9px,.9px)}
.riel-mod:hover .ic-morph .mp-d,.riel-mod[aria-expanded="true"] .ic-morph .mp-d{
  transform:translate(-.9px,.9px)}

/* Admisiones: la flecha cruza el umbral. */
.ic-morph.adm .mp-b{transform:translateX(-2.2px)}
.riel-mod:hover .ic-morph.adm .mp-b,
.riel-mod[aria-expanded="true"] .ic-morph.adm .mp-b{transform:translateX(0)}

/* Secretaría: la tapa levanta y las líneas del papel asoman. */
.ic-morph.sec .mp-b{opacity:0;transform:translateY(2px)}
.riel-mod:hover .ic-morph.sec .mp-b,
.riel-mod[aria-expanded="true"] .ic-morph.sec .mp-b{opacity:.85;transform:translateY(0)}

/* Académico: el libro se abre y el visto se dibuja. */
.ic-morph.aca .mp-a{transform:rotateY(0deg)}
.ic-morph.aca .mp-c{opacity:0;transform:scale(.6)}
.riel-mod:hover .ic-morph.aca .mp-a,
.riel-mod[aria-expanded="true"] .ic-morph.aca .mp-a{transform:translateX(-1.1px)}
.riel-mod:hover .ic-morph.aca .mp-b,
.riel-mod[aria-expanded="true"] .ic-morph.aca .mp-b{transform:translateX(1.1px)}
.riel-mod:hover .ic-morph.aca .mp-c,
.riel-mod[aria-expanded="true"] .ic-morph.aca .mp-c{opacity:1;transform:scale(1)}

/* DECE: el arco abraza a la persona. */
.ic-morph.dece .mp-b{opacity:0;transform:scale(.82)}
.riel-mod:hover .ic-morph.dece .mp-b,
.riel-mod[aria-expanded="true"] .ic-morph.dece .mp-b{opacity:.9;transform:scale(1)}

/* Inspección: el visto se DIBUJA sobre la tablilla. Informa de lo que hace el módulo
   —pasar por la lista y confirmarla—, no decora. */
.ic-morph.insp .mp-b{opacity:0;transform:scale(.7)}
.riel-mod:hover .ic-morph.insp .mp-b,
.riel-mod[aria-expanded="true"] .ic-morph.insp .mp-b{opacity:1;transform:scale(1)}

@media (prefers-reduced-motion:reduce){
  /* Sin transición, y en el estado FINAL: la forma sigue diciendo lo mismo. */
  .ic-morph > g{transition:none!important}
  .ic-morph .mp-b,.ic-morph .mp-c,.ic-morph .mp-d{opacity:1!important;transform:none!important}
  .ic-morph.op .mp-c{opacity:.9!important}
  .ic-morph.adm .mp-b,.ic-morph.sec .mp-b,.ic-morph.aca .mp-a,
  .ic-morph.aca .mp-b,.ic-morph.dece .mp-b,
  .ic-morph.insp .mp-b{transform:none!important;opacity:.9!important}
}

/* ===========================================================================
   HISTORIAL ACADÉMICO — el espinazo de los años (2026-08-20)
   ---------------------------------------------------------------------------
   Pedido de Hidalgo: «sube todos los datos en orden a la interfaz, todos los
   actualizados y los históricos».

   POR QUÉ FILAS Y NO TARJETAS. Son 30 años. Una rejilla de tarjetas idénticas
   está prohibida (§8) y además obliga a leer 30 veces el mismo molde para
   encontrar los que tienen datos; en filas, la columna de cifras se escanea de
   un tirón. La línea vertical que las une es NEUTRA (`--linea`) y vive en el
   contenedor: es estructura de archivo, no la franja lateral de color en el
   ítem activo que el §8 retiró — el activo se sigue marcando con fondo y peso.

   La jerarquía la llevan tres palancas juntas (§5): tamaño, peso y color. La
   cifra de notas es el foco de cada fila; su rótulo baja a --tinta-3. Ninguna
   cifra va en la display: Bricolage no garantiza el mismo avance por dígito.

   🔴 Y un año SIN notas no se pinta igual que uno vacío por error: conserva su
   motivo legible. Son 19 años cuyo boletín no existe en ningún sitio, y
   apagarlos del todo mandaría a buscar una carga que nunca va a existir.
   =========================================================================== */
.hist-barra{display:flex;align-items:center;gap:var(--e3);margin-bottom:var(--e3)}

.hist-espinazo{
  display:flex;flex-direction:column;
  margin-top:var(--e2);padding-left:var(--e4);
  border-left:1px solid var(--linea);           /* el espinazo: neutro, estructural */
}
.hist-anio{
  position:relative;display:grid;
  grid-template-columns:minmax(9rem,1.1fr) 5.5rem 5.5rem minmax(10rem,1.4fr);
  align-items:center;gap:var(--e3);
  width:100%;box-sizing:border-box;
  padding:9px var(--e3) 9px var(--e3);
  background:none;border:0;border-radius:var(--r);
  border-bottom:1px solid var(--linea);
  text-align:left;font:inherit;color:var(--tinta);cursor:pointer;
  transition:background-color var(--rapido) var(--curva);
}
.hist-espinazo .hist-anio:last-child{border-bottom:0}
/* la vértebra: el punto sobre el espinazo */
.hist-anio::before{
  content:"";position:absolute;left:calc(var(--e4) * -1 - var(--e3) - 3px);
  width:7px;height:7px;border-radius:50%;
  background:var(--linea-fuerte);
}
.hist-anio:not(.inerte)::before{background:var(--app)}
.hist-anio:not(.inerte):hover{background:var(--panel-2)}
.hist-anio:focus-visible{outline:2px solid var(--app);outline-offset:2px}
.hist-anio.enCurso{background:var(--seleccion)}
.hist-anio.inerte{cursor:default}

.hist-anio-id{display:flex;align-items:baseline;gap:var(--e2);min-width:0}
.hist-anio-id b{
  font-family:var(--display);font-stretch:84%;font-size:var(--t-md);
  font-weight:600;letter-spacing:-.01em;color:var(--tinta);
}
.hist-anio.inerte .hist-anio-id b{font-weight:500;color:var(--tinta-3)}
.hist-encurso{
  flex:none;font-size:var(--t-2xs);font-weight:650;letter-spacing:.02em;
  color:var(--app-txt);background:var(--panel-2);
  padding:1px 6px;border-radius:var(--r);border:1px solid var(--linea);
}

.hist-anio-cif{display:flex;flex-direction:column;align-items:flex-end;line-height:1.1}
.hist-anio-cif .n{
  font-family:var(--sans);font-variant-numeric:tabular-nums;
  font-size:var(--t-md);font-weight:600;color:var(--tinta);
}
.hist-anio.inerte .hist-anio-cif .n{font-weight:400;color:var(--tinta-4)}
.hist-anio-cif .et{font-size:var(--t-2xs);color:var(--tinta-3);font-weight:400}

.hist-anio-nota{display:flex;flex-direction:column;gap:1px;min-width:0}
.hist-modelo{font-size:var(--t-xs);font-weight:550;color:var(--tinta-2)}
.hist-alu{font-size:var(--t-2xs);color:var(--tinta-3);font-variant-numeric:tabular-nums}
/* el motivo: legible, no apagado. Es el dato que evita una búsqueda inútil. */
.hist-porque{font-size:var(--t-2xs);color:var(--tinta-3);line-height:1.4}

/* --- migas: cuatro niveles sin ellas es no saber dónde se está ------------ */
.hist-migas{
  /* 🔴 `grid-column:1/-1`: las migas viven DENTRO de `.rejilla`, que es una grilla de
     12 columnas, así que sin esto el <nav> ocupaba UNA columna — cada miga caía en su
     propia línea y «APOLO PACHECO OSCAR JOAQUIN» salía truncado a «APOLO PACHE». El
     síntoma parecía de `white-space` y la causa era el contenedor. */
  grid-column:1/-1;
  display:flex;align-items:center;flex-wrap:wrap;gap:6px;
  margin-bottom:var(--e3);font-size:var(--t-xs);
}
.hist-migas .sep{color:var(--tinta-4)}
.hist-migas .miga{
  background:none;border:0;padding:2px 4px;border-radius:var(--r);
  font:inherit;color:var(--app-txt);font-weight:550;cursor:pointer;
  transition:background-color var(--rapido) var(--curva);
}
.hist-migas .miga:hover{background:var(--panel-2)}
.hist-migas .miga:focus-visible{outline:2px solid var(--app);outline-offset:1px}
.hist-migas .miga.act{color:var(--tinta-2);font-weight:600;cursor:default}
.hist-migas .miga.act:hover{background:none}

/* --- ir a: acción de fila, discreta --------------------------------------- */
.hist-ir{
  background:none;border:1px solid var(--linea-fuerte);border-radius:var(--r);
  padding:3px 9px;font:inherit;font-size:var(--t-2xs);font-weight:550;
  color:var(--app-txt);cursor:pointer;white-space:nowrap;
  transition:background-color var(--rapido) var(--curva),
             border-color var(--rapido) var(--curva);
}
.hist-ir:hover{background:var(--panel-2);border-color:var(--app)}
.hist-ir:focus-visible{outline:2px solid var(--app);outline-offset:1px}

.hist-buscar{
  min-width:min(22rem,100%);padding:5px 9px;
  border:1px solid var(--linea-fuerte);border-radius:var(--r);
  background:var(--panel);color:var(--tinta);font:inherit;font-size:var(--t-xs);
}
.hist-buscar:focus-visible{outline:2px solid var(--app);outline-offset:1px}

/* una casilla sin nota es un guion tenue: no es un cero, y no debe leerse como dato */
.tabla td.n.vacia{color:var(--tinta-4)}

/* En pantalla angosta el espinazo se apila: cuatro columnas de 5,5rem no caben y
   la última quedaba recortada — una tabla recortada obliga a descubrir que se
   desplaza. */
@media (max-width:720px){
  .hist-anio{grid-template-columns:1fr auto;gap:2px var(--e3);padding:10px var(--e2)}
  .hist-anio-nota{grid-column:1 / -1}
  .hist-anio-cif{flex-direction:row;align-items:baseline;gap:5px}
  .hist-anio-cif .et{font-size:var(--t-2xs)}
}
@media (prefers-reduced-motion:reduce){
  .hist-anio,.hist-migas .miga,.hist-ir{transition:none}
}

/* Los años que solo tienen padrón de matrícula: fila mínima, en columnas.
   Su motivo se explica UNA vez arriba (repetirlo 19 veces era un muro de texto que
   el ojo aprende a saltarse, y hundía los cuatro años con datos). */
.hist-secas{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(11rem,1fr));
  gap:2px var(--e4);margin-top:var(--e2);
}
.hist-anio.seca{
  display:flex;align-items:baseline;justify-content:space-between;gap:var(--e2);
  padding:5px 0;border-bottom:1px solid var(--linea);border-radius:0;
}
.hist-anio.seca::before{content:none}
.hist-seca-anio{
  font-family:var(--sans);font-size:var(--t-xs);font-weight:550;color:var(--tinta-2);
  font-variant-numeric:tabular-nums;
}
.hist-seca-cif{display:flex;align-items:baseline;gap:4px;font-size:var(--t-2xs);
  color:var(--tinta-3)}
.hist-seca-cif b{font-weight:600;color:var(--tinta-2);font-variant-numeric:tabular-nums}
/* El asistente flotante vive fijo abajo a la derecha y tapaba la última fila de la
   rejilla: «2000-2001 357 m…» salía recortado, y el §9 no admite rótulos cortados.
   Se reserva su alto en el último panel de la pantalla. */
.hist-secas{padding-bottom:var(--e5,32px)}

/* Migas: una miga NO se parte por dentro. El nombre «APOLO PACHECO OSCAR JOAQUIN»
   salía en cuatro líneas porque los botones flex se encogen por defecto; el salto va
   ENTRE migas, no dentro de una. */
.hist-migas .miga{flex:0 0 auto;white-space:nowrap;max-width:min(28ch,100%);
  overflow:hidden;text-overflow:ellipsis}

/* Celda de materia CUALITATIVA: no es una cifra y no se alinea como tal. */
.tabla td.cual{text-align:right;font-size:var(--t-xs);color:var(--tinta-2);
  font-weight:550;white-space:nowrap}
.tabla td.cual.larga{
  text-align:left;font-weight:400;font-size:var(--t-2xs);color:var(--tinta-2);
  line-height:1.45;max-width:34ch;white-space:normal;
}

/* Buscador del historial: la caja manda y los filtros la acompañan en la misma fila.
   El caso que abre esta pantalla es el del mostrador —alguien que se graduó pidiendo un
   certificado—, así que la caja va primero y con el foco puesto. */
.hist-filtros{
  display:flex;align-items:center;flex-wrap:wrap;gap:var(--e2);
  margin-bottom:var(--e3);
}
.hist-filtros .hist-buscar{flex:1 1 18rem;min-width:0}
.hist-sel{
  flex:0 1 auto;max-width:100%;padding:5px 8px;
  border:1px solid var(--linea-fuerte);border-radius:var(--r);
  background:var(--panel);color:var(--tinta);font:inherit;font-size:var(--t-2xs);
}
.hist-sel:focus-visible{outline:2px solid var(--app);outline-offset:1px}

/* Lo que NO consta: lista con el motivo, no un hueco. Un hueco en la pantalla se lee
   como «este alumno no tiene eso», y quien emite un certificado necesita lo contrario. */
.hist-faltan{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;
  gap:var(--e2)}
.hist-faltan li{
  display:flex;flex-direction:column;gap:2px;padding-left:var(--e3);
  border-left:2px solid var(--linea-fuerte);
}
.hist-faltan b{font-size:var(--t-xs);font-weight:600;color:var(--tinta-2)}
.hist-faltan span{font-size:var(--t-2xs);color:var(--tinta-3);line-height:1.5;
  max-width:76ch}

/* --- Captura por curso del seguimiento (rejilla) --------------------------- */
.seg-cap-barra{
  display:flex;align-items:center;flex-wrap:wrap;gap:var(--e2);
  margin-bottom:var(--e3);
}
.seg-sel,.seg-in{
  padding:5px 8px;border:1px solid var(--linea-fuerte);border-radius:var(--r);
  background:var(--panel);color:var(--tinta);font:inherit;font-size:var(--t-xs);
  max-width:100%;
}
.seg-in{min-width:0}
.seg-in.n{width:6.5rem;text-align:right;font-variant-numeric:tabular-nums}
.seg-sel:focus-visible,.seg-in:focus-visible{outline:2px solid var(--app);outline-offset:1px}
.seg-chk{width:17px;height:17px;accent-color:var(--app)}
/* La celda editable ocupa su columna: un input de ancho fijo dentro de una tabla
   deja la fila descuadrada en cuanto una columna tiene texto largo. */
.tabla td .seg-in:not(.n){width:100%}
.seg-usadas{
  display:flex;align-items:center;flex-wrap:wrap;gap:6px;
  font-size:var(--t-2xs);color:var(--tinta-3);margin-bottom:var(--e2);
}
.seg-usada{
  background:var(--panel-2);border:1px solid var(--linea);border-radius:var(--r);
  padding:2px 7px;font:inherit;font-size:var(--t-2xs);font-weight:550;
  color:var(--app-txt);cursor:pointer;
  transition:border-color var(--rapido) var(--curva);
}
.seg-usada:hover{border-color:var(--app)}
.seg-usada:focus-visible{outline:2px solid var(--app);outline-offset:1px}
@media (prefers-reduced-motion:reduce){ .seg-usada{transition:none} }

/* Emitir un documento desde la ficha del estudiante. Tarjetas de acción, no una fila
   de botones: cada documento dice de qué año y con qué respaldo sale, porque emitir un
   certificado de calificaciones de un año sin notas es el error que hay que evitar
   ANTES de pulsar, no con un 404 después. */
.hist-docs{display:grid;grid-template-columns:repeat(auto-fit,minmax(15rem,1fr));
  gap:var(--e2);margin-top:var(--e2)}
.hist-doc{
  display:flex;flex-direction:column;gap:2px;text-decoration:none;
  padding:10px 12px;border:1px solid var(--linea-fuerte);border-radius:var(--r-xl);
  background:var(--panel);
  transition:border-color var(--rapido) var(--curva),
             background-color var(--rapido) var(--curva);
}
.hist-doc:hover{border-color:var(--app);background:var(--panel-2)}
.hist-doc:focus-visible{outline:2px solid var(--app);outline-offset:2px}
.hist-doc b{font-size:var(--t-xs);font-weight:600;color:var(--tinta)}
.hist-doc span{font-size:var(--t-2xs);color:var(--tinta-3)}
@media (prefers-reduced-motion:reduce){ .hist-doc{transition:none} }

/* ============================================================================
   XX. FICHA DEL PROFESOR — diálogo CENTRADO, el nombre como puerta, y el
       rendimiento medido. Pedido de Hidalgo (2026-09-01): «que cada vez que haya
       un nombre de un profesor haya un click y que salga en todo el medio de la
       pantalla el rendimiento de cada uno».
   ========================================================================== */

/* --- El nombre como puerta -------------------------------------------------
   No se disfraza de botón: sigue siendo el nombre, con el subrayado punteado que
   ya usa el resto del sistema para «esto lleva a algún sitio». Un botón relleno
   dentro de cada celda de una tabla de 60 filas convertiría la tabla en una
   parrilla de botones. §8 prohíbe la franja lateral de color: el estado de foco
   es anillo, no barra. */
.doc-abre{
  display:inline-flex;align-items:center;text-align:left;
  background:none;border:0;padding:0;margin:0;font:inherit;color:inherit;
  cursor:pointer;border-radius:var(--r);max-width:100%;min-width:0;
}
.doc-abre .persona-nom,
.doc-abre > .hora-doc > span{
  text-decoration:underline;text-decoration-style:dotted;
  text-decoration-color:var(--tinta-4);text-underline-offset:2px;
  transition:text-decoration-color var(--rapido) var(--curva),color var(--rapido) var(--curva);
}
.doc-abre:hover .persona-nom,
.doc-abre:hover > .hora-doc > span{color:var(--app);text-decoration-color:var(--app)}
.doc-abre:focus-visible{outline:2px solid var(--app);outline-offset:2px}
@media (prefers-reduced-motion:reduce){
  .doc-abre .persona-nom,.doc-abre > .hora-doc > span{transition:none}
}

/* --- El diálogo centrado ---------------------------------------------------
   `margin:auto` es lo que lo centra en los DOS ejes en un <dialog> nativo; no hace
   falta transform ni flex en el fondo. El alto se limita para que la ficha larga
   haga scroll DENTRO y el diálogo no se salga de la pantalla en un portátil. */
dialog.dialogo-c{
  border:0;padding:0;margin:auto;
  width:min(var(--dialogo-ancho,720px),calc(100vw - 32px));
  max-height:min(86vh,900px);
  display:flex;flex-direction:column;overflow:hidden;
  border-radius:var(--r-xl);background:var(--panel);color:var(--tinta);
  box-shadow:var(--flota-alta);
}
dialog.dialogo-c::backdrop{background:rgba(8,12,40,.42)}
.dialogo-c-cab{
  display:flex;align-items:flex-start;gap:var(--e3);flex:none;
  padding:var(--e5) var(--e5) var(--e3);border-bottom:1px solid var(--linea);
}
.dialogo-c-cab .tt{min-width:0;flex:1}
.dialogo-c-cab h2{
  font-family:var(--display);font-size:var(--t-lg);font-weight:600;
  letter-spacing:-.01em;color:var(--tinta);
}
.dialogo-c-cab .sub{font-size:var(--t-xs);color:var(--tinta-3);margin-top:3px}
.dialogo-c-x{
  flex:none;background:none;border:0;cursor:pointer;line-height:1;
  font-size:var(--t-md);color:var(--tinta-3);padding:var(--e1) var(--e2);
  border-radius:var(--r);transition:color var(--rapido) var(--curva),
                                       background-color var(--rapido) var(--curva);
}
.dialogo-c-x:hover{color:var(--tinta);background:var(--panel-2)}
.dialogo-c-x:focus-visible{outline:2px solid var(--app);outline-offset:2px}
.dialogo-c-cuerpo{padding:var(--e5);overflow-y:auto;flex:1;min-height:0}
@media (prefers-reduced-motion:reduce){ .dialogo-c-x{transition:none} }

/* --- Bloques de rendimiento de la ficha ------------------------------------ */
.rend-caja{display:grid;gap:1px;margin:var(--e4) 0;background:var(--linea);
  border-radius:var(--r-xl);overflow:hidden;box-shadow:var(--lift)}
.rend-i{background:var(--panel);padding:11px var(--e4)}
.rend-i > b{display:block;font-size:var(--t-xs);font-weight:650;color:var(--tinta);
  letter-spacing:.02em}
.rend-i > span{display:block;font-size:var(--t-sm);color:var(--tinta-2);margin-top:2px}
/* Lo que no consta se ve distinto de lo que consta (§ misma regla que ficha-doc). */
.rend-i.sin{background:var(--panel-2)}
.rend-i.sin > b{color:var(--tinta-2)}
.rend-i.sin > span{color:var(--tinta-3)}
/* La escala del claustro: dónde cae ESTE profesor entre el mínimo y el máximo.
   No es un adorno — es lo que evita que «puesto 43 de 48» se lea como un desastre
   cuando todo el claustro cabe en 1,6 puntos. */
.rend-escala{margin-top:var(--e3);position:relative;height:22px}
.rend-escala-via{position:absolute;left:0;right:0;top:9px;height:4px;
  border-radius:var(--r-full);background:var(--panel-2);
  box-shadow:inset 0 0 0 1px var(--linea)}
.rend-escala-med{position:absolute;top:4px;width:2px;height:14px;
  background:var(--tinta-4);border-radius:1px}
.rend-escala-yo{position:absolute;top:2px;width:12px;height:18px;margin-left:-6px;
  border-radius:var(--r-full);background:var(--panel);
  box-shadow:0 0 0 2px currentColor,var(--lift)}
.rend-escala-pies{display:flex;justify-content:space-between;
  font-size:var(--t-2xs);color:var(--tinta-3);margin-top:2px}
.rend-escala.tono-ok{color:var(--ok)}
.rend-escala.tono-alerta{color:var(--alerta)}
.rend-escala.tono-riesgo{color:var(--riesgo)}

/* Perfil directivo: denominadores junto al dato y asistencia en su propio bloque.
   Todo queda dentro del diálogo del perfil; no cambia las bandas del tablero. */
.perfil-docente{color:var(--tinta);font-size:var(--t-md)}
.perfil-docente h3{font-size:var(--t-lg);font-weight:650;line-height:1.3;margin:0 0 var(--e2)}
.perfil-docente h4{font-size:var(--t-md);font-weight:650;margin:0 0 var(--e3)}
.perfil-docente .perfil-nota{font-size:var(--t-sm);line-height:1.5;color:var(--tinta-2);margin:var(--e2) 0 0}
.perfil-docente .perfil-enc{display:grid;grid-template-columns:auto 1fr 1fr;align-items:center;gap:var(--e4);margin-bottom:var(--e4)}
.perfil-docente .perfil-corte{text-align:right}
.perfil-docente .perfil-nav{display:flex;flex-wrap:wrap;gap:var(--e2);position:sticky;top:calc(-1 * var(--e5));
  z-index:2;background:var(--panel);padding:var(--e3) 0;border-bottom:1px solid var(--linea);margin-bottom:var(--e4)}
.perfil-docente > section{scroll-margin-top:calc(var(--e8) + var(--e8));outline-offset:var(--e1)}
.perfil-docente > section + section{border-top:1px solid var(--linea);margin-top:var(--e6);padding-top:var(--e6)}
.perfil-docente .perfil-kpis{display:grid;grid-template-columns:1fr 1fr;gap:var(--e6);align-items:start}
.perfil-docente .perfil-captura{padding:var(--e5);border-radius:var(--r-xl);background:var(--navy-bloque);color:#fff}
.perfil-docente .perfil-captura h3,.perfil-docente .perfil-captura .perfil-nota{color:inherit}
.perfil-docente .perfil-captura > strong{display:block;font-family:var(--cifras);font-size:var(--t-2xl);font-weight:600;line-height:1.2}
.perfil-docente .perfil-captura small{font-size:var(--t-lg);font-weight:400}
.perfil-docente .perfil-alcance{padding:var(--e5) 0}
.perfil-docente .perfil-base{font-weight:600;font-size:var(--t-md);margin:var(--e3) 0 0;font-variant-numeric:tabular-nums}
.perfil-docente .perfil-contexto{display:grid;grid-template-columns:1.1fr 1fr;gap:var(--e6);align-items:start;margin-top:var(--e6)}
.perfil-docente .perfil-resultados,.perfil-docente .perfil-asistencia{min-width:0;padding:var(--e5);border:1px solid var(--linea);border-radius:var(--r-xl)}
.perfil-docente .perfil-asistencia{background:var(--panel-2)}
.perfil-docente .perfil-promedio{display:flex;align-items:baseline;gap:var(--e2);margin:var(--e4) 0 var(--e2)}
.perfil-docente .perfil-promedio strong{font-family:var(--cifras);font-size:var(--t-2xl);font-weight:600;line-height:1.2}
.perfil-docente .perfil-promedio span{font-size:var(--t-lg);color:var(--tinta-2)}
.perfil-docente .perfil-estado{display:flex;align-items:center;gap:var(--e2);font-size:var(--t-sm);font-weight:600;margin:var(--e2) 0}
.perfil-docente .perfil-estado > span:first-child{display:flex;flex:none;width:var(--e4);height:var(--e4)}
.perfil-docente .perfil-estado .ic{width:100%;height:100%}
.perfil-docente .perfil-estado.bajo{color:var(--riesgo)}
.perfil-docente .perfil-estado.sobre{color:var(--ok)}
.perfil-docente .perfil-comparacion{margin-top:var(--e4);padding-top:var(--e4);border-top:1px solid var(--linea)}
.perfil-docente .perfil-referencia{display:grid;grid-template-columns:1.2fr 1fr auto;gap:var(--e3);align-items:center;margin-top:var(--e2);font-size:var(--t-xs)}
.perfil-docente .perfil-referencia b{font-family:var(--cifras);font-variant-numeric:tabular-nums}
.perfil-docente .perfil-via{height:var(--e2);background:var(--linea);border-radius:var(--r);overflow:hidden}
.perfil-docente .perfil-via i{display:block;height:100%;background:var(--graf-trazo);border-radius:inherit}
.perfil-docente .perfil-asistencia-cifras{display:grid;grid-template-columns:1fr 1fr;gap:var(--e4);margin:var(--e5) 0}
.perfil-docente .perfil-asistencia-cifras dt{font-size:var(--t-sm);color:var(--tinta-2)}
.perfil-docente .perfil-asistencia-cifras dd{font-family:var(--cifras);font-size:var(--t-cifra);font-weight:600;margin:var(--e1) 0 0}
.perfil-docente .perfil-distribucion{display:grid;grid-template-columns:1fr 1fr;gap:var(--e6);align-items:start;margin-top:var(--e4)}
.perfil-docente .perfil-distribucion > div{min-width:0}
.perfil-celda-sub{display:block;margin-top:var(--e1);color:var(--tinta-2);font-size:var(--t-xs)}
.perfil-docente table{width:100%;table-layout:fixed}
.perfil-docente th,.perfil-docente td{white-space:normal;overflow-wrap:anywhere}
.perfil-docente .vacio{padding:var(--e5) 0}
@media(max-width:768px){
  .perfil-docente .perfil-enc{grid-template-columns:auto 1fr}
  .perfil-docente .perfil-corte{grid-column:1 / -1;text-align:left}
  .perfil-docente .perfil-kpis,.perfil-docente .perfil-contexto,.perfil-docente .perfil-distribucion{grid-template-columns:1fr}
}

/* ============================================================================
   XX+1. BAJO EL MÍNIMO — la nota que no alcanza, en actas y calificaciones.
       Pedido de Hidalgo (2026-09-01): «cuando el estudiante tenga menos a 7 en las
       actas y en las calificaciones que se resalte de manera automática de color
       rojo».
       🔴 §4 del DESIGN.md: NUNCA solo color — el rojo-verde es justo lo que falla
       en daltonismo. Así que la celda lleva rojo + anillo + un texto que lo dice
       (`title`/`aria-label`), y la cifra en negrita. Y §4 también prohíbe rellenar
       el bloque de color: aquí es anillo y tinta, no un rectángulo rojo.
   ========================================================================== */
.bajo-min{color:var(--riesgo);font-weight:650}
/* 🔴 EL COLOR SE REPITE CON MÁS ESPECIFICIDAD, y no es redundancia.
   Medido en el navegador: con solo `.bajo-min{color:var(--riesgo)}` la clase se
   aplicaba —el `title` estaba puesto, la celda existía— y el color salía
   `rgb(20,23,43)`, o sea TINTA. La causa es `.doc-tabla td{color:var(--tinta)}`
   (línea 1369): pesa (0,1,1) contra los (0,1,0) de una clase suelta, así que gana
   aunque esté 2.000 líneas antes. Es la misma trampa que costó un rediseño en Zeus:
   en CSS gana la MÁS ESPECÍFICA, no la última.
   Y esto solo se ve midiendo el colorcalculado en la página: leyendo el CSS, la regla
   está y parece correcta. */
td.bajo-min,th.bajo-min,.doc-tabla td.bajo-min,table.sabana td.bajo-min{
  color:var(--riesgo);font-weight:650;
  box-shadow:inset 0 0 0 1.5px var(--riesgo);
}
/* En un PDF/impresión las sombras no salen: ahí el aviso es el borde real. Y el
   bloque de impresión también pinta `.doc-tabla td{color:#000}`, así que aquí hace
   falta la misma especificidad o la nota baja saldría negra en el papel. */
@media print{
  td.bajo-min,th.bajo-min,.doc-tabla td.bajo-min,table.sabana td.bajo-min{
    box-shadow:none;border:1.5px solid #b3261e;color:#b3261e;font-weight:700;
  }
}

/* ============================================================================
   XX+2. CAPTURA POR CASILLAS — el encabezado de columna es la puerta a definirla.
   Encargo del 2026-09-01: al pulsar «Trabajo 1» se abre el formulario donde el
   docente escribe la descripción que después sale impresa en el documento semanal.
   ========================================================================== */
.doc.boletin-familiar td.bajo-min,.doc.boletin-familiar th.bajo-min{
  color:var(--tinta);font-weight:700;box-shadow:none;
}
.doc.boletin-familiar .bajo-min::after{content:" *";font-weight:700}
@media print{
  .doc.boletin-familiar td.bajo-min,.doc.boletin-familiar th.bajo-min{
    color:#000;border:0;border-bottom:1px solid #ddd;box-shadow:none;
  }
}
.casilla-cab{
  display:inline-flex;flex-direction:column;align-items:flex-end;gap:2px;
  background:none;border:0;padding:0;margin:0;font:inherit;color:inherit;
  cursor:pointer;border-radius:var(--r);text-align:right;
  transition:color var(--rapido) var(--curva);
}
.casilla-cab:hover{color:inherit;text-decoration:underline}
.casilla-cab:focus-visible{outline:2px solid currentColor;outline-offset:2px}
/* 🔴 Una casilla SIN definir se ve distinta de una definida. Diez columnas idénticas
   con notas dentro son diez notas que nadie sabe de qué son — que es exactamente el
   problema que el documento semanal viene a resolver. El aviso lleva TEXTO y no solo
   un tono: §4 del DESIGN.md. */
.casilla-pend{
  font-size:var(--t-2xs);font-weight:600;letter-spacing:.02em;
  color:var(--oro);text-transform:none;
}
/* La cabecera es navy en ambos temas; definir un trabajo no cambia su fondo. */
.casilla-cab.definida{color:inherit}
@media print{.casilla-pend{color:inherit}}
@media (prefers-reduced-motion:reduce){ .casilla-cab{transition:none} }

/* --- Horarios: secciones desplegables, cortes y la franja en curso ---------- */
.desplegable{border-radius:var(--r-xl);overflow:hidden}
.desplegable > summary{
  display:flex;align-items:baseline;gap:var(--e3);cursor:pointer;list-style:none;
  padding:var(--e3) 0;border-bottom:1px solid var(--linea);
}
.desplegable > summary::-webkit-details-marker{display:none}
/* El triángulo se dibuja aquí porque el nativo no se puede colorear ni alinear. */
.desplegable > summary::before{
  content:"";width:0;height:0;flex:none;align-self:center;
  border-left:5px solid var(--tinta-3);border-top:4px solid transparent;
  border-bottom:4px solid transparent;
  transition:transform var(--rapido) var(--curva);
}
.desplegable[open] > summary::before{transform:rotate(90deg)}
.desplegable > summary:focus-visible{outline:2px solid var(--app);outline-offset:2px}
.desp-tt{font-family:var(--display);font-size:var(--t-lg);font-weight:600;color:var(--tinta)}
.desp-sub{font-size:var(--t-xs);color:var(--tinta-3)}
.desp-cuerpo{padding-top:var(--e3)}
.horario-seccion{display:block;min-width:0}
.horario-seccion > summary{display:grid;grid-template-columns:auto minmax(0,1fr);
  gap:var(--e1) var(--e3);padding:var(--e5)}
.horario-seccion > summary::before{grid-row:1 / 3}
.horario-seccion .desp-sub{grid-column:2}
.horario-controles{padding:var(--e5);padding-bottom:0}
.horario-controles .campo{width:100%;max-width:36rem;min-width:0;align-items:stretch}
.horario-controles .select{width:100%;min-width:0;max-width:100%}
.horario-seccion .desp-cuerpo{padding:var(--e5);min-width:0}
.horario-seccion .vacio{align-items:flex-start;text-align:left;padding:var(--e4) 0}
.horario-seccion .vacio .fig{display:none}
/* RECREO y LUNCH son FILAS del documento oficial, no huecos. */
.franja-corte td{background:var(--panel-2)}
td.corte{
  text-align:center;font-size:var(--t-2xs);font-weight:650;letter-spacing:.08em;
  color:var(--tinta-3);text-transform:uppercase;
}
/* 🔴 La franja en curso. §4 del DESIGN.md: nunca solo color — lleva además el canto
   izquierdo marcado y el rango de horas ya visible en su celda. No parpadea: un
   parpadeo permanente en una pantalla que se mira todo el día cansa y se ignora. */
.franja-ahora td{background:var(--seleccion,rgba(24,86,133,.07))}
.franja-ahora td:first-child{box-shadow:inset 3px 0 0 var(--app)}
.hora-curso{font-size:var(--t-2xs);font-weight:650;color:var(--tinta-3);letter-spacing:.02em}
.horario-cuadricula .tabla{table-layout:fixed;width:100%;min-width:48rem;border-collapse:collapse}
.horario-cuadricula .tabla th:first-child{width:10em}
.horario-cuadricula .tabla th{text-align:center}
.horario-cuadricula .tabla td{white-space:normal;border:1px solid var(--linea);vertical-align:middle}
.horario-cuadricula .tabla td.horario-asignatura{padding:0;background:var(--mat-fondo)}
.horario-bloque{min-height:calc(var(--e8)*2);padding:var(--e3);display:flex;flex-direction:column;
  justify-content:center;gap:var(--e2);text-align:center;overflow-wrap:anywhere}
.horario-materia{font-size:var(--t-xs);font-weight:650;line-height:1.35}
.horario-docente{font-size:var(--t-2xs);line-height:1.4;color:var(--tinta-2)}
.horario-bloque .doc-abre{white-space:normal;display:block}
.horario-cuadricula .tabla .franja-corte td{background:var(--alerta-f);color:var(--alerta);
  padding:var(--e3);font-weight:600}
.horario-cuadricula .hora-celda{text-align:center}
.horario-cuadricula .hora-rango{white-space:nowrap}
@media (prefers-reduced-motion:reduce){ .desplegable > summary::before{transition:none} }

/* --- Reporte de inspección: la fila sin conteo se ve ------------------------
   En una tabla de 46 cursos, una celda vacía entre números pasa desapercibida — y esa
   fila saldrá en blanco en el documento oficial. §4: el tono va con el texto «inspector
   / docentes» de la columna Fuente al lado, no solo con el color. */
tr.pend-conteo td{background:var(--alerta-f)}
tr.pend-conteo td:first-child{box-shadow:inset 3px 0 0 var(--alerta)}

/* ---------------------------------------------------------------------------
   WP-12 · Rejilla de materias del curso (Evaluaciones)
   Pedido literal: «que salgan los cursos y dentro de los cursos que salga de una
   manera mucho más bonita lo que son las materias que conforma ese curso».
   La tarjeta existe para poder ELEGIR de un vistazo, así que lo que la distingue
   no es el adorno sino el estado de captura: cuánto lleva puesto esa materia.
   --------------------------------------------------------------------------- */
.materias-rejilla{
  display:grid; gap:10px; margin-top:4px;
  grid-template-columns:repeat(auto-fill,minmax(220px,1fr));
}
.materia-tarjeta{
  display:flex; flex-direction:column; gap:6px; text-align:left;
  padding:12px 13px 11px; border:1px solid var(--linea-fuerte); border-radius:10px;
  background:var(--panel-2,transparent); color:inherit; cursor:pointer;
  font:inherit; position:relative; overflow:hidden;
  transition:border-color .14s ease, transform .14s ease, box-shadow .14s ease;
}
.materia-tarjeta:hover{ border-color:var(--tinta-3); transform:translateY(-1px); }
.materia-tarjeta:focus-visible{ outline:2px solid var(--acento,#2c6bed); outline-offset:2px; }
.materia-tarjeta[aria-pressed="true"]{ border-color:var(--tinta); box-shadow:inset 0 0 0 1px var(--tinta); }
.materia-tarjeta .mt-nom{ font-weight:650; line-height:1.25; font-size:13px; }
.materia-tarjeta .mt-doc{ font-size:11.5px; color:var(--tinta-2); }
/* 🔴 «Sigue descuadrado». La tarjeta era un flex en columna sin bandas fijas, así que
   cada elemento caía donde le tocaba: un título de dos líneas («CÍVICA Y ACOMPAÑAMIENTO
   INTEGRAL EN EL AULA») o un chip «Cualitativa» empujaban la barra hacia abajo y en una
   misma fila había barras a cuatro alturas distintas. La rejilla ya iguala el ALTO de
   las tarjetas; lo que faltaba era anclar el contenido dentro.

   Dos anclas: el nombre reserva dos líneas —así el docente empieza siempre a la misma
   altura— y el pie se va al fondo con `margin-top:auto`. La barra, que es la señal que
   se lee a un metro, queda alineada en toda la fila. */
/* 🔴 El título va con SUELO Y TECHO: exactamente tres líneas.
   Solo con el suelo (`min-height:2.5em`, que es lo que había) las tarjetas de una fila
   se estiraban a la del título más largo y la corta quedaba con un vacío enorme entre
   el título y el pie. Medido en «Ingreso de evaluaciones» de PRE KINDER A, que trae
   114 destrezas: los títulos van de 2 a 6 líneas, las tarjetas de 134 a 199 px, y el
   hueco muerto llega a **118 px**. Los bordes SÍ estaban alineados —el detector la daba
   limpia— y aun así se lee torcida: lo que descuadra no es el borde, es el vacío.
   Tres líneas cubren enteros 82 de los 114 títulos; el resto se corta y el texto
   completo está en el `title` y en el `aria-label`, y el código de la destreza
   («CN.1.3.1.») va al principio, así que lo que identifica la fila nunca se pierde. */
.materia-tarjeta .mt-nom{
  min-height:3.75em;                    /* 3 líneas × line-height 1.25 */
  display:-webkit-box; -webkit-box-orient:vertical;
  -webkit-line-clamp:3; line-clamp:3; overflow:hidden;
}
.materia-tarjeta .mt-pie{ display:flex; align-items:center; gap:8px;
  margin-top:auto; padding-top:6px; }
.materia-tarjeta .mt-pct{ font-size:11.5px; color:var(--tinta-2); white-space:nowrap;
  font-variant-numeric:tabular-nums; }
/* La barra es la señal que se lee a un metro: cuánto falta por capturar. */
.materia-tarjeta .mt-barra{ flex:1; height:5px; border-radius:3px; background:var(--nada-f);
  overflow:hidden; }
.materia-tarjeta .mt-barra i{ display:block; height:100%; background:var(--ok-bloque); }
.materia-tarjeta.mt-parcial .mt-barra i{ background:var(--alerta-bloque); }
.materia-tarjeta.mt-cero   .mt-barra i{ background:var(--riesgo); }
/* Sin docente NO es «sin capturar»: lo primero lo arregla Secretaría, lo segundo el
   profesor. Se distinguen a la vista porque mandan a personas distintas. */
.materia-tarjeta.mt-sin-docente{ border-style:dashed; }
/* El chip va alineado a la izquierda y no estira su banda: `align-self` evita que
   ocupe todo el ancho de la tarjeta, que era lo que desalineaba la fila del docente. */
.materia-tarjeta .mt-etq{ align-self:flex-start; font-size:10.5px; padding:1px 6px; border-radius:999px;
  background:var(--nada-f); color:var(--tinta-2); align-self:flex-start; }
.materia-tarjeta .mt-etq.riesgo{ background:var(--riesgo-f); color:var(--riesgo); }

/* ---------------------------------------------------------------------------
   WP-10 · Comportamiento: el semáforo
   🔴 Nunca SOLO color. Un punto rojo junto al nombre de un niño es una etiqueta
   que ve cualquiera que abra la pantalla, y un color sin su número no se puede
   discutir: el tutor que quiera contradecirlo no tendría con qué. Por eso el
   componente siempre lleva la cifra y la palabra, y el color es el tercer canal.
   --------------------------------------------------------------------------- */
.sem-conducta{ display:inline-flex; align-items:center; gap:6px; white-space:nowrap; }
.sem-conducta .punto{ width:9px; height:9px; border-radius:50%; flex:none;
  background:var(--ok-bloque); }
.sem-conducta.alerta  .punto{ background:var(--alerta-bloque); }
.sem-conducta.riesgo  .punto{ background:var(--riesgo); }
.sem-conducta .cifra{ font-variant-numeric:tabular-nums; font-weight:650; }
.sem-conducta .palabra{ font-size:11.5px; color:var(--tinta-2); }
/* El umbral sin confirmar se DECLARA: hoy 3/6 son un valor por defecto que el colegio
   todavía no ha fijado, y presentarlo como criterio propio sería inventárselo. */
.umbral-provisional{ font-size:11px; color:var(--alerta); }
.conducta-motivos{ display:grid; gap:6px; grid-template-columns:repeat(auto-fill,minmax(210px,1fr)); }

/* ---------------------------------------------------------------------------
   WP-17 · Módulos fusionados: la cinta de vistas
   Tres módulos que decían lo mismo con tres nombres pasaron a ser uno con tres
   vistas. La cinta va ARRIBA del contenido y separada por una línea: tiene que
   leerse como «dónde estoy dentro de este módulo», no como una fila de botones.
   --------------------------------------------------------------------------- */
.fusion-cinta{ margin:0 0 14px; padding-bottom:12px; border-bottom:1px solid var(--linea); }
.fusion-hueco{ display:block; }
@media (max-width:640px){
  /* En móvil los rótulos numerados del cierre no caben en una fila. */
  .fusion-cinta .seg{ flex-wrap:wrap; }
}


/* ─── Barras de herramientas dentro de un panel SIN AIRE ─────────────────────
   Reporte de Hidalgo: «la palabra materia está casi pegada al lado».
   El panel de una pantalla de captura va `sin-aire` para que la tabla llegue de
   borde a borde — eso está bien—, pero entonces los RÓTULOS de los selectores
   quedan pegados al canto, mientras el texto de la tabla sí está metido porque la
   celda trae su propio relleno. No estaban desalineados (medido: los dos a x=284):
   les faltaba el mismo margen interno que a la celda, que es contra lo que se leen.
   Se lo damos aquí y solo aquí, sin devolverle el aire al panel. */
.panel-cuerpo.sin-aire > .tabla-herr,
.panel-cuerpo.sin-aire > .fila-campos,
.panel-cuerpo.sin-aire > .seg,
.panel-cuerpo.sin-aire > .nota-honesta{padding-left:var(--e4);padding-right:var(--e4)}
/* La nota al pie de una tabla iba pegada a la última fila: un párrafo que arranca a
   0 px del borde inferior de la tabla se lee como si fuera otra fila. */
.panel-cuerpo.sin-aire > .nota-honesta{margin-top:10px;padding-bottom:2px}

/* ─── Avance curricular: el semáforo del estado ──────────────────────────────
   §4 del DESIGN.md: nunca solo color. La palabra va siempre, y la cifra que la
   produce está en la columna «%» de la misma fila — un estado que no se puede
   contrastar con su número no se puede discutir. */
.sem-avance{display:inline-flex;align-items:center;gap:6px;white-space:nowrap;font-size:var(--t-xs)}
.sem-avance .punto{width:8px;height:8px;border-radius:50%;flex:none;background:var(--tinta-4)}
.sem-avance.cumplido .punto{background:var(--ok-bloque)}
.sem-avance.en_proceso .punto{background:var(--alerta-bloque)}
.sem-avance.pendiente .punto{background:var(--riesgo)}
.sem-avance.nada{color:var(--tinta-3)}
/* Las dos casillas de número van estrechas: son conteos de dos dígitos, no importes. */
.tabla td.celda-nota .input.nota{width:5.5em}

/* ─── Tamaño mínimo de lo que se pulsa ───────────────────────────────────────
   Auditado el 2026-09-02 contra las reglas del skill ui-ux-pro-max, midiendo en las
   siete pantallas de captura. El criterio aplicable en web es **WCAG 2.2 Target Size
   (Minimum): 24×24 px** — no los 44 pt de iOS ni los 48 dp de Android, que son de app
   nativa y el propio catálogo remite a la regla web.

   Resultado medido: UN solo elemento por debajo, el botón de salir a **23×23 px**, y
   estaba en las siete. Un píxel de diferencia, pero es el que separa cumplir de no
   cumplir, y encima es el control que se usa con prisa.

   Los demás (riel, pestañas, botones pequeños) cumplían de sobra a 27 px. Se suben a 32
   por otra razón, medida y distinta: esto se usa en la TABLETA de un aula, con el dedo
   y de pie. 27 px cumple la norma y aun así se falla al pulsar. */
.salir{min-width:32px;min-height:32px}
.riel-it{min-height:32px}
.seg button{min-height:32px}
.btn{min-height:32px}
.select,.input{min-height:32px}
/* Menos la casilla de nota: en la rejilla hay 200 y subirlas a 32 estira la tabla un
   tercio, que es lo contrario de lo que se busca — una lista de clase tiene que caber
   en la pantalla. Se escriben con teclado, no con el dedo, y siguen sobre los 24 de
   WCAG. La densidad es una decisión, no un descuido. */
.tabla td.celda-nota .input.nota{min-height:26px}

/* ─── Línea de contexto (§7-bis del DESIGN.md) ───────────────────────────────
   Lo que en un tablero sería una banda de cifras, en una pantalla de captura es esto:
   una línea. Dice lo mismo en 20 px en vez de 65, y no se pone por delante del
   formulario, que es a lo que se viene. */
.linea-contexto{
  margin:0 0 8px;font-size:var(--t-xs);color:var(--tinta-2);line-height:1.45;
}
.panel-cuerpo.sin-aire > .linea-contexto{padding:8px var(--e4) 0}
.resumen-alumno{margin-top:2px}
/* La línea de contexto cuando avisa de algo que hay que atender antes de firmar. El
   color acompaña a la cifra que ya está en el texto; nunca va solo (§4). */
.linea-contexto.aviso-pendiente{color:var(--alerta);font-weight:600}

/* De qué materias sale una fila de bloque (Inglés). Va bajo el nombre, en pequeño:
   es procedencia, no una materia más — si tuviera el mismo peso visual, la tabla
   parecería tener el doble de filas. */
.doc-tabla td .de-materias {
  display: block;
  margin-top: 2px;
  font-size: var(--t-2xs);
  font-weight: 400;
  line-height: 1.35;
  color: var(--tinta-3, #6b7280);
  max-width: 44ch;
}

/* Un 403 no es una avería: no lleva el rojo del fallo. Ámbar y con un candado, porque lo
   que informa es una REGLA funcionando, no algo roto. Y sin botón: ver `sinPermiso`. */
.sin-permiso {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 14px 16px;
  border-radius: var(--r);
  background: var(--ambar-bg, #fff6e5);
  color: var(--ambar-ink, #7a4d00);
  max-width: 62ch;
}
.sin-permiso .ic { width: 20px; height: 20px; flex: none; margin-top: 1px; }
.sin-permiso b { display: block; font-size: var(--t-sm); }
.sin-permiso span { display: block; margin-top: 3px; font-size: var(--t-xs); line-height: 1.45; }

/* ── Clave temporal: el único paso posible antes de entrar ───────────────────
   No es un diálogo más. Tapa la pantalla porque con la clave temporal no hay ninguna
   otra a la que ir, y no lleva botón de cerrar: cerrarlo devolvería a la persona a la
   caja de error que la trajo. */
.clave-fondo {
  position: fixed; inset: 0; z-index: 9000;
  display: grid; place-items: center;
  padding: var(--e4);
  background: rgba(10, 14, 30, .62);
  backdrop-filter: blur(2px);
}
.clave-caja {
  width: 100%; max-width: 430px;
  background: var(--panel); color: var(--tinta);
  border-radius: var(--r-lg); padding: var(--e5) var(--e5) var(--e4);
  box-shadow: 0 18px 48px rgba(10, 14, 30, .28);
}
.clave-caja h2 { margin: 0 0 6px; font-size: var(--t-lg); }
.clave-por-que {
  margin: 0 0 16px; font-size: var(--t-xs); line-height: 1.5;
  color: var(--tinta-2, #4b5068); max-width: 46ch;
}
.clave-campo { display: block; margin-bottom: 12px; }
.clave-campo > span { display: block; font-size: var(--t-xs); font-weight: 600; margin-bottom: 4px; }
.clave-campo input {
  width: 100%; box-sizing: border-box;
  padding: var(--e3) var(--e4); font-size: var(--t-sm);
  border: 1px solid var(--linea); border-radius: var(--r);
  background: var(--fondo); color: var(--tinta);
}
.clave-campo input:focus { outline: 2px solid var(--navy-logo); outline-offset: 1px; }
.clave-campo small { display: block; margin-top: 4px; font-size: var(--t-2xs); color: var(--tinta-3, #6b7280); }
/* Ámbar, no rojo: casi siempre es «las dos no coinciden», que es una corrección y no
   una avería. El rojo enseñaría que el sistema se rompe cuando la persona escribe. */
.clave-aviso {
  margin: 2px 0 12px; padding: var(--e3) var(--e4); border-radius: var(--r);
  font-size: var(--t-xs); line-height: 1.4;
  background: var(--ambar-bg, #fff6e5); color: var(--ambar-ink, #7a4d00);
}
.clave-pie { margin-top: 4px; }
.clave-pie button { width: 100%; }
.clave-nota {
  margin: 14px 0 0; font-size: var(--t-2xs); line-height: 1.45;
  color: var(--tinta-3, #6b7280);
}

/* ============================================================================
   MATRICULADOS — el apartado general (buscar un estudiante y ver su estado).
   Sustituye la entrada «Admisiones» del riel (2026-09-04).

   Solo estructura: el estado lo pinta `.sem` y las etiquetas `.etiq`, que ya
   existen. Un segundo componente de estado acabaría discrepando del primero.
   ========================================================================== */
.mat-identidad {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--e3); margin-bottom: var(--e4);
}
.mat-sellos { display: flex; flex-wrap: wrap; gap: var(--e2); align-items: center; }
.mat-bloque { margin-top: var(--e5); }
.mat-bloque h3 {
  margin: 0 0 var(--e2); font-size: var(--t-xs); font-weight: 700;
  color: var(--tinta-2);
}
/* El hueco se DECLARA. `--tinta-3` y no `--tinta-4`: 4,80:1 sobre el lienzo contra
   3,01:1, que en la tabla de contraste del DESIGN.md §3 solo vale para
   deshabilitado — y «no consta» es un dato, no un control apagado. */
.mat-hueco { color: var(--tinta-3); font-style: italic; }
.mat-fuente { color: var(--tinta-3); font-size: var(--t-2xs); }
.mat-aviso {
  margin-top: 3px; font-size: var(--t-2xs); line-height: 1.4; color: var(--alerta);
}

/* Lista dentro de una respuesta de Arco (p. ej. las materias que dicta un docente).
   Hereda el cuerpo del chat; solo recupera la sangría y el ritmo que el reset quita. */
.arco-lista { margin: var(--e1) 0 0; padding-left: 18px; }
.arco-lista li { margin: 2px 0; line-height: 1.45; }

/* --- EXPEDIENTE INTEGRAL (F4, 2026-09-16) ---------------------------------
   La pestaña «Expediente integral» dentro de Matriculados. Sin componentes
   nuevos de estado: reusa `.mat-bloque`, `.nota-honesta` y `.mat-hueco`. Lo
   único propio es el marco de la foto y la marca de nota bajo la mínima. */
.mat-pestanas { grid-column: 1 / -1; margin-bottom: var(--e3); }
.exp-identidad {
  display: flex; align-items: center; gap: var(--e4); margin-bottom: var(--e4);
  flex-wrap: wrap;
}
.exp-nombre { font-size: var(--t-lg); font-weight: 700; line-height: 1.2; }
.mat-acciones { display: flex; flex-wrap: wrap; gap: var(--e2); margin-bottom: var(--e3); }

/* 🔴 El marco de la foto EXISTE aunque no haya foto. Un hueco en blanco se lee como
   un fallo de carga y manda a recargar la pantalla; el marco con la palabra dentro
   dice que el sistema no la tiene, que es un hecho del colegio. Proporción 3,5×4,5
   (carné), la misma que se imprime en el PDF. */
.exp-foto {
  width: 84px; height: 108px; flex: none; object-fit: cover;
  border: 1px solid var(--linea); border-radius: 2px; background: var(--lienzo);
}
.exp-sin-foto {
  display: flex; align-items: center; justify-content: center; text-align: center;
  font-size: var(--t-2xs); letter-spacing: .06em; color: var(--tinta-3);
}

/* Cada afirmación con su fuente debajo. El `<b>` es la afirmación y la nota es de
   dónde sale: sin la segunda, la primera es una etiqueta sobre un menor que nadie
   puede contrastar. */
.exp-hallazgos { margin: var(--e2) 0 0; padding-left: 18px; }
.exp-hallazgos li { margin-bottom: var(--e2); line-height: 1.5; }
.exp-hallazgos li > span { display: block; margin-top: 2px; }

.exp-aviso {
  padding: var(--e2) var(--e3); border: 1px solid var(--linea);
  border-left: 3px solid var(--alerta); background: var(--lienzo);
  border-radius: 2px;
}

/* Nunca solo color (DESIGN.md §4): el asterisco lo pone el JS y es lo que sobrevive
   al daltonismo y a la fotocopia en blanco y negro. */
.exp-baja { color: var(--riesgo); font-weight: 700; }

/* Las dos opciones del apartado Matriculados. */
.mat-opcion { padding: var(--e3) 0; border-top: 1px solid var(--linea); }
.mat-opcion:first-child { border-top: 0; padding-top: 0; }
.mat-opcion-cab { display: flex; align-items: center; gap: var(--e2); flex-wrap: wrap; }
.mat-opcion .nota-honesta { margin: var(--e1) 0 var(--e2); }

/* Aviso de Arco cuando su cerebro no está disponible: se dice en el saludo, no al
   fallar la primera pregunta. Tono de alerta, no de error: el asistente sigue
   sirviendo para buscar por nombre. */
.arco-aviso {
  margin: var(--e2) 0 0; padding: var(--e2) var(--e3);
  border-radius: var(--r); background: var(--alerta-f); color: var(--alerta);
  font-size: var(--t-2xs); line-height: 1.45;
}

/* Expediente privado DECE: controles fluidos y texto documental íntegro. */
.dece-archivo .panel-cuerpo { padding: var(--e4, 16px); }
.dece-herramientas, .dece-acciones { display: flex; flex-wrap: wrap; gap: var(--e3); align-items: end; margin-bottom: var(--e4); }
.dece-herramientas .campo { flex: 1 1 240px; min-width: 0; }
.dece-herramientas p { flex: 1 1 280px; margin: 0; }
.dece-archivo .btn { max-width: 100%; white-space: normal; overflow-wrap: anywhere; text-align: left; }
.dece-archivo .campo, .dece-archivo .input, .dece-archivo .select { min-width: 0; max-width: 100%; }
.dece-formulario { display: grid; gap: var(--e4); padding: var(--e5) 0; margin: var(--e4) 0; border-block: 1px solid var(--linea); }
.dece-formulario .campo { width: 100%; }
.dece-formulario textarea { width: 100%; resize: vertical; }
.dece-fila, .dece-registro { padding: var(--e4) 0; border-bottom: 1px solid var(--linea); overflow-wrap: anywhere; }
.dece-fila { display: flex; flex-wrap: wrap; gap: var(--e3); align-items: center; }
.dece-fila p { width: 100%; margin: 0; }
.dece-texto { white-space: pre-wrap; overflow-wrap: anywhere; line-height: 1.6; }
.dece-meta { font-size: var(--t-2xs); }
.dece-mensaje:empty { display: none; }
.dece-documentos { margin-top: var(--e6); }
.dece-puntuaciones { width: 100%; table-layout: fixed; border-collapse: collapse; }
.dece-puntuaciones th, .dece-puntuaciones td { text-align: left; white-space: normal; overflow-wrap: anywhere; }
.dece-revision { overflow-wrap: anywhere; }
.cit-respuesta { margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--borde, #dce1ec); }
.cit-respuesta-texto { white-space: pre-wrap; overflow-wrap: anywhere; }

/* Informe de Inicial: descriptores, tres cortes y escalas literales. */
.inicial-tabla { width:100%; table-layout:fixed; margin:var(--e3) 0; }
.inicial-tabla th:first-child { width:64%; }
.inicial-tabla th,.inicial-tabla td { white-space:normal; overflow-wrap:anywhere; }
.inicial-tabla .inicial-ambito th { text-align:left; background:#eef1f5; }
.inicial-tabla tr { break-inside:avoid; }
.inicial-tabla thead { display:table-header-group; }
.inicial-recomendacion { white-space:pre-wrap; }
.inicial-escala td { text-align:left; white-space:normal; overflow-wrap:anywhere; }
@media (max-width:600px) {
  .inicial-tabla th:first-child { width:52%; }
  /* Escala de 4 del DESIGN.md §6: 5px no está en ella y no tiene token. El px que
     se mueve es invisible; tener una medida fuera de la escala no lo es. */
  .inicial-tabla th,.inicial-tabla td,.inicial-escala td { padding:var(--e2) var(--e1); }
}
.inicial-alerta { display:block; font-size:11px; font-style:italic; }

/* Catálogo de destrezas de Inglés de 1.º EGB: solo lista, sin escala ni casillas. */
.destrezas-ingles { margin-top:var(--e5); padding-top:var(--e3); border-top:1px solid var(--linea); }
.destrezas-ingles h4 { margin:var(--e3) 0 var(--e1); }
.destrezas-ingles-lista { margin:0 0 var(--e2); padding-left:1.4em; }
.destrezas-ingles-lista li { margin-top:var(--e1); overflow-wrap:anywhere; }
/* ============================================================================
   VISTA PREVIA DEL RIEL (F1 · 2026-09-16)
   ---------------------------------------------------------------------------
   Mirar el menú de otro rol sin dejar de ser uno mismo. Los tres estilos de
   aquí tienen un solo trabajo: que NADIE confunda mirar con ser.

   Se usa el ámbar de `--alerta-f`, el mismo del aviso de permiso, y no el rojo
   del fallo: aquí no hay avería. Y no es un aviso que se pueda cerrar — la
   banda no lleva aspa a propósito; su único botón sale de la vista previa.
   ========================================================================== */
.banda-previa{
  display:flex;align-items:center;gap:var(--e3);flex-wrap:wrap;
  margin:var(--e3) var(--e4) 0;padding:var(--e2) var(--e4);
  background:var(--alerta-f);border:1px solid var(--linea-fuerte);
  border-radius:var(--r);font-size:var(--t-xs);color:var(--tinta);
}
.banda-previa .ic{flex:none;font-size:var(--t-md);line-height:1}
.banda-previa p{margin:0;flex:1;min-width:14ch}
.banda-previa small{flex-basis:100%;color:var(--tinta-2);line-height:1.45}
.banda-previa .btn{flex:none;margin-left:auto}
/* Mientras dura la vista previa, el riel lleva un filete ámbar: el aviso está
   arriba y el riel abajo, y al hacer scroll la banda se va de la pantalla. */
:root[data-vista-previa] .riel{box-shadow:inset -2px 0 0 var(--oro-txt)}

/* El selector vive EN EL RIEL, encima de los módulos: lo que cambia es el riel, así
   que va con el riel — y en móvil entra y sale con el cajón en vez de disputarle
   espacio a la cabecera, que a 390 px no lo tiene. Va con rótulo visible —no solo
   `aria-label`— porque un desplegable de roles sin rótulo se lee como un filtro más. */
.previa-sel{
  display:flex;flex-direction:column;gap:var(--e1);font-size:var(--t-2xs);
  padding:var(--e2) var(--e3) 0;min-width:0;
}
.previa-sel .et{color:var(--riel-txt);white-space:nowrap;opacity:.85}
.previa-sel select{
  background:var(--riel-activo);color:var(--tinta);border:1px solid var(--riel-linea);
  border-radius:var(--r);padding:var(--e1) var(--e2);font:inherit;font-size:var(--t-xs);
  min-height:32px;width:100%;max-width:100%;
}

/* Un módulo CERRADO en el riel: no es un enlace, es un botón que explica. Se ve
   apagado y con candado; el `aria-disabled` dice lo mismo a quien no lo ve. */
.riel-it-cerrado{
  background:none;border:0;width:100%;text-align:left;cursor:pointer;
  font:inherit;opacity:.62;
}
.riel-it-cerrado:hover{background:var(--riel-activo);opacity:.85}
.riel-it .candado{flex:none;font-size:10px;line-height:1}

/* Y su tarjeta. Sin botón para pedir el acceso, a propósito: quien mira la
   vista previa está diseñando el reparto de cuentas, no tramitando el suyo. */
.cerrado-nominal{
  display:grid;gap:var(--e2);justify-items:start;
  margin:var(--e4);padding:var(--e5) var(--e4);
  background:var(--panel);border:1px solid var(--linea-fuerte);
  border-radius:var(--r-lg);max-width:62ch;
}
.cerrado-nominal .ic{font-size:var(--t-xl);line-height:1}
.cerrado-nominal h2{margin:0;font-size:var(--t-md);color:var(--tinta)}
.cerrado-nominal .mec{
  margin:0;font-size:var(--t-xs);color:var(--oro-txt);font-weight:600;
}
.cerrado-nominal p{margin:0;font-size:var(--t-xs);color:var(--tinta-2);line-height:1.55}

/* ─── INSPECCIÓN · rejilla de revisión y bandeja de justificaciones ──────────
   (2026-09-16)

   🔴 A 390 px una rejilla de estudiantes × horas NO se resuelve con
   `overflow-x`. Ese arreglo deja al inspector eligiendo entre ver el nombre o
   ver la hora, que son justo los dos datos que tienen que ir juntos para que la
   celda signifique algo — y encima el defecto que este repo ya pagó dos veces
   esta semana es precisamente el desborde horizontal.

   Lo que se hace en su lugar: por encima de 720 px la fila ES una fila (grid de
   `--rev-horas` columnas, fijado por el JS con `style.setProperty` porque la CSP
   no admite `style=`); por debajo, la fila deja de ser fila — la cabecera de
   horas desaparece y cada celda pasa a ser una ficha que lleva su hora escrita
   dentro. No es un reflujo automático: es otra lectura de la misma información. */
.rev-rejilla{display:flex;flex-direction:column;padding:var(--e2) var(--e4) var(--e4)}
.rev-fila{
  display:grid;gap:var(--e1);align-items:stretch;
  grid-template-columns:minmax(0,13rem) repeat(var(--rev-horas,1),minmax(0,1fr));
}
.rev-cab{border-bottom:1px solid var(--linea);margin-bottom:var(--e2)}
.rev-celda-cab{
  display:flex;flex-direction:column;min-width:0;
  padding:var(--e1) var(--e2);font-size:var(--t-xs);color:var(--tinta-2);
}
.rev-celda-cab .suave{overflow-wrap:anywhere}
.rev-nom{
  display:flex;align-items:center;min-width:0;overflow-wrap:anywhere;
  padding:var(--e1) var(--e2);font-size:var(--t-sm);color:var(--tinta);
}
.rev-celda{
  display:flex;flex-direction:column;align-items:flex-start;gap:var(--e1);
  min-width:0;padding:var(--e1) var(--e2);font:inherit;font-size:var(--t-xs);
  text-align:left;cursor:pointer;color:var(--tinta-2);
  background:var(--panel-2);border:1px solid var(--linea);border-radius:var(--r);
  transition:border-color .16s var(--mueve),background-color .16s var(--mueve);
}
.rev-celda:hover{background:var(--seleccion);border-color:var(--linea-fuerte)}
.rev-celda:focus-visible{outline:2px solid var(--navy-logo);outline-offset:var(--e1)}
/* La celda donde el docente e Inspección NO coinciden. El aviso va en el BORDE y
   en las dos etiquetas de dentro; nunca en un relleno de color (§4 del DESIGN.md:
   un `--alerta-f` repetido en media rejilla devuelve el crema que la v3 retiró). */
.rev-difiere{border-color:var(--alerta)}
/* Inconsistencias entre horas: se señalan, nunca se corrigen solas. */
.rev-cruces{margin:var(--e2) var(--e4);padding:var(--e2) var(--e3);border-left:3px solid var(--alerta)}
.rev-fila-cruce .rev-nom{box-shadow:inset 3px 0 0 var(--alerta)}
.rev-cruce-et{display:block;margin-top:4px;font-size:var(--t-2xs);color:var(--alerta);font-weight:500}
.rev-celda-sin{cursor:default;background:none;border-style:dashed;color:var(--tinta-3)}
.rev-par{display:flex;align-items:center;gap:var(--e2);flex-wrap:wrap}
.rev-lado{display:inline-flex;align-items:center;gap:var(--e1)}
.rev-lado i{font-style:normal;color:var(--tinta-3)}
.rev-confirma,.rev-just,.rev-sin{color:var(--tinta-3)}
.rev-just{font-weight:600}
.rev-hora-et{display:none;color:var(--tinta-3)}
.rev-leyenda{
  margin:0 0 var(--e2);padding:0 var(--e4);
  font-size:var(--t-xs);color:var(--tinta-3);
}
@media(max-width:719px){
  .rev-rejilla{padding:var(--e2)}
  /* 🔴 `.rev-fila.rev-cab` y no `.rev-cab` a secas: la cabecera lleva las DOS clases y
     la regla de `.rev-fila` que viene justo debajo la reabría por orden de archivo —
     medido en la captura de 390 px, donde la cabecera de horas seguía dibujada encima
     de las fichas. Gana la especificidad, no el orden; es la trampa que el cerebro ya
     tiene anotada de los skins de Zeus. */
  .rev-fila.rev-cab{display:none}
  .rev-fila{
    display:block;margin-bottom:var(--e2);padding:var(--e2);
    border:1px solid var(--linea);border-radius:var(--r-lg);
  }
  .rev-nom{padding:0 0 var(--e1);font-weight:600}
  .rev-celda{
    width:100%;flex-direction:row;align-items:center;justify-content:space-between;
    flex-wrap:wrap;margin-top:var(--e1);
  }
  .rev-hora-et{display:inline}
}

/* El formulario de las dos fichas (revisar una marca, resolver una solicitud).
   Es CAPTURA (§7-bis): el dato manda y no hay banda de cifras en ninguna parte. */
.rev-form{display:flex;flex-direction:column;gap:var(--e3)}
.rev-form textarea.input{resize:vertical;font-family:inherit}
.rev-dato{
  display:flex;flex-direction:column;gap:var(--e1);margin:0;
  font-size:var(--t-sm);color:var(--tinta);
}
.rev-dato .suave{font-size:var(--t-xs)}
.rev-opciones{
  display:flex;gap:var(--e4);flex-wrap:wrap;margin:0;
  padding:var(--e2) var(--e3);border:1px solid var(--linea);border-radius:var(--r);
}
.rev-opciones legend{padding:0 var(--e1);font-size:var(--t-xs);color:var(--tinta-2)}
.rev-op{display:inline-flex;align-items:center;gap:var(--e1);font-size:var(--t-sm)}
.rev-acciones{display:flex;gap:var(--e2);flex-wrap:wrap}
.insp-campo{display:flex;flex-direction:column;gap:var(--e1);min-width:0}

/* La bandeja de justificaciones a 390 px.

   🔴 Medido en la captura, no deducido: con la tabla normal el teléfono enseñaba hasta
   la columna «Respaldo» y dejaba el ESTADO y el botón «Resolver» fuera, detrás de un
   scroll lateral DENTRO de `.tabla-marco` — así que la página no desbordaba (la prueba
   de desborde pasaba en verde) y la pantalla estaba rota igual. Es el mismo defecto que
   el `overflow-x` de la rejilla: esconder la mitad y llamarlo responsive.

   Aquí la fila deja de ser fila: cada solicitud es una ficha y cada celda lleva delante
   el rótulo de su columna, que el JS pone en `data-et`. */
@media(max-width:719px){
  .jus-tabla{overflow:visible;max-height:none}
  .jus-tabla table,.jus-tabla tbody,.jus-tabla tr,.jus-tabla td{display:block;width:auto}
  .jus-tabla thead{display:none}
  .jus-tabla tbody tr{
    margin-bottom:var(--e2);padding:var(--e2);
    border:1px solid var(--linea);border-radius:var(--r-lg);
  }
  .jus-tabla tbody td{
    display:flex;gap:var(--e3);align-items:flex-start;justify-content:space-between;
    padding:var(--e1) 0;border:0;text-align:left;
  }
  .jus-tabla tbody td::before{
    content:attr(data-et);flex:none;color:var(--tinta-3);font-size:var(--t-xs);
  }
  .jus-tabla tbody td:not([data-et])::before{content:none}
}

/* ============================================================================
   BIENESTAR (HabilMind) — dashboards del screening emocional del DECE.
   Reusa la paleta cálida y los tokens de escala/espacio; no introduce medidas
   nuevas (el test de consistencia pone techo a font-size/radius/gap/padding).
   ========================================================================== */
.biem-barra{display:flex;align-items:center;gap:var(--e3);margin-bottom:var(--e2)}
.biem-barra-r{flex:0 0 40%;font-size:var(--t-sm);color:var(--tinta-2)}
.biem-barra .barra-celda{flex:1}
.biem-barra-v{flex:none;font-size:var(--t-sm);color:var(--tinta);font-variant-numeric:tabular-nums}
.biem-corte{padding:var(--e3) 0;border-bottom:1px solid var(--linea)}
.biem-corte:last-child{border-bottom:0}
.biem-corte-cab{display:flex;align-items:baseline;justify-content:space-between;gap:var(--e3);margin-bottom:var(--e2)}
.biem-corte-cab b{font-family:var(--serif);font-size:var(--t-md);color:var(--tinta)}
.biem-corte-n{font-size:var(--t-xs);color:var(--tinta-3);font-variant-numeric:tabular-nums}
.biem-obs{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--e2)}
.biem-obs li{display:flex;gap:var(--e3);align-items:flex-start;font-size:var(--t-sm);color:var(--tinta-2)}
.biem-obs-et{flex:none;font-size:var(--t-2xs);font-weight:600;padding:2px 7px;border-radius:var(--r);text-transform:uppercase;letter-spacing:.02em}
.biem-obs-ok .biem-obs-et{background:var(--ok-f);color:var(--verde-txt)}
.biem-obs-aten .biem-obs-et{background:var(--alerta-f);color:var(--oro-txt)}
.biem-busca{display:flex;gap:var(--e3);align-items:flex-end;margin-bottom:var(--e4);flex-wrap:wrap}
.biem-ind-tit{font-family:var(--serif);font-size:var(--t-lg);color:var(--tinta);margin:0 0 var(--e2)}
.biem-factor td{color:var(--tinta-3);font-style:italic}
.biem-pc{margin-left:var(--e2);color:var(--tinta-3);font-variant-numeric:tabular-nums}

/* Aviso que precede a una acción irreversible (deslizar para cerrar / sellar).
   Antes iba como style="" en línea y el CSP style-src 'self' lo bloqueaba: el
   aviso salía sin su ancho de lectura, pegado al control. */
.nota-previa-accion{max-width:46ch;margin:0 0 6px}

/* El botón flotante de Arco (62 px, anclado abajo a la derecha) tapaba lo último de
   la pantalla: en «Estudiantes» se comía el botón «Siguiente» de la paginación y no
   había forma de hacer scroll para destaparlo. Se reserva ese hueco al final del
   contenido para que lo último siempre pueda quedar a la vista. */
body:has(.arco-fab) #contenido{padding-bottom:max(var(--esp-6, 32px), 104px)}
/* 🔴 2026-09-30: la regla de arriba (`body:has(.arco-fab) #contenido`) pesa MÁS que
   `[data-entorno="gabi"] #contenido` y le ganaba: el ERP entero —riel incluido— terminaba
   104 px antes del borde y dejaba una franja vacía abajo («sigue el bug»). En el ERP el
   aire para la mascota va DENTRO de lo que se desplaza, no debajo del armazón. */
[data-entorno="gabi"] body:has(.arco-fab) #contenido,
[data-entorno="gabi"] #contenido{padding-bottom:0}
body:has(.arco-fab) .gabi-cuerpo{padding-bottom:max(var(--gabi-inset), 96px)}

/* ── ERP Contable (Tesorería, Contable, Nómina) ─────────────────────────────
   Los tres módulos nacieron con su propio vocabulario y se veían ajenos al resto.
   Se alinean aquí, sin tocar su lógica: filtros en fila con aire, «Columnas» como
   control plegable de la casa y los avisos con el tono de las notas honestas. */
.erp-mod .erp-arriba{padding-bottom:0}
.erp-mod .erp-arriba + .panel-cuerpo{padding-top:var(--e4)}
.erp-mod .erp-arriba > .aviso,.erp-mod .erp-arriba > .sub{margin-top:var(--e3)}
.erp-mod .mat-acciones{display:flex;flex-wrap:wrap;align-items:flex-end;gap:var(--e3);margin-bottom:var(--e3)}
.erp-mod .mat-acciones > .btn{min-height:34px}
.erp-mod .campo > label{font-size:var(--t-2xs, 11px);text-transform:uppercase;letter-spacing:.06em;color:var(--tinta-2);font-weight:600}
.erp-mod details.card{margin:0 0 var(--e3);border:1px solid var(--linea);border-radius:var(--r);padding:0}
.erp-mod details.card > summary{cursor:pointer;padding:6px 12px;font-size:var(--t-xs);font-weight:600;color:var(--tinta-2);list-style:none;display:inline-flex;align-items:center;gap:6px}
.erp-mod details.card > summary::-webkit-details-marker{display:none}
.erp-mod details.card > summary::before{content:"";width:6px;height:6px;border-right:1.6px solid currentColor;border-bottom:1.6px solid currentColor;transform:rotate(-45deg);transition:transform var(--rapido) var(--sale)}
.erp-mod details.card[open] > summary::before{transform:rotate(45deg)}
.erp-mod details.card[open]{padding-bottom:var(--e2)}
.erp-mod details.card > :not(summary){padding:0 12px}
.erp-mod details.card label.campo{flex-direction:row;align-items:center;gap:6px;margin:4px 14px 4px 0}
.erp-mod .panel-cuerpo > .sub,.erp-mod .panel-cuerpo .sub{color:var(--tinta-2);font-size:var(--t-xs);margin:var(--e2) 0}
.erp-mod .erp-hoja .tabla-marco{margin:0 calc(-1 * var(--e4, 16px))}
.erp-mod .panel-cuerpo > p.ok{color:var(--ok);font-weight:600;margin-top:var(--e3)}

/* Matrícula por curso (Matrículas históricas, 2026-09-25): 19 columnas de años. El
   curso queda fijo a la izquierda al desplazar, y la tabla se desplaza DENTRO de su
   marco: nunca empuja la página hacia los lados. */
.tabla-historico-curso{min-width:100%;width:max-content}
.tabla-historico-curso th:first-child,.tabla-historico-curso td:first-child{
  position:sticky;left:0;background:var(--panel);z-index:1;white-space:nowrap}
.tabla-historico-curso td.n,.tabla-historico-curso th.n{white-space:nowrap;font-variant-numeric:tabular-nums}
.tabla-historico-curso tr.total td{border-top:1px solid var(--linea-fuerte)}

/* El recuadro del nombre en el pie del riel lleva a «Mi perfil» (2026-09-25). */
.sesion-perfil{display:flex;align-items:center;gap:8px;min-width:0;flex:1 1 auto;
  color:inherit;text-decoration:none;border-radius:var(--r);padding:2px 4px;margin:-2px -4px}
.sesion-perfil:hover{background:var(--riel-activo)}
.sesion-perfil:focus-visible{outline:2px solid var(--oro);outline-offset:2px}

/* Estadística oficial (hoja del colegio 2009–2026) */
.eu-avisos{margin:0;padding-left:1.2em;display:grid;gap:4px;font-size:var(--t-sm);color:var(--tinta-2)}
.tabla td.eu-difiere{color:var(--alerta);font-weight:600;box-shadow:inset 3px 0 0 var(--alerta)}

/* ── «Mis clases de hoy» (Inicio del docente, 2026-09-29) ─────────────────────────
   Una fila por clase, en orden de hora. El estado va con color Y texto (DESIGN.md:
   nunca color solo). La clase que está en su hora es la única que se destaca: es lo
   único que el docente tiene que hacer AHORA. */
.mis-clases-franja{margin-bottom:var(--e5)}
.mc-clases{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--e2)}
.mc-clase{background:var(--panel);border:1px solid var(--linea);border-radius:var(--r-lg);
  padding:var(--e3) var(--e4)}
.mc-clase[data-estado="pasar_lista"]{border-color:var(--ok);box-shadow:0 0 0 1px var(--ok) inset}
.mc-cab{display:flex;align-items:baseline;gap:var(--e3);flex-wrap:wrap}
.mc-hora{font-variant-numeric:tabular-nums;font-weight:600;color:var(--tinta);min-width:9ch}
.mc-que{flex:1;min-width:0;color:var(--tinta)}
.mc-estado{font-size:var(--t-2xs);font-weight:700;padding:2px 8px;border-radius:var(--r-full);
  white-space:nowrap;color:var(--nada);background:var(--nada-f)}
.mc-clase[data-estado="pasar_lista"] .mc-estado{color:var(--ok);background:var(--ok-f)}
.mc-clase[data-estado="registrada"] .mc-estado{color:var(--ok);background:var(--ok-f)}
.mc-clase[data-estado="sin_lista"] .mc-estado{color:var(--riesgo);background:var(--riesgo-f)}
.mc-clase[data-estado="sin_malla"] .mc-estado{color:var(--alerta);background:var(--alerta-f)}
.mc-detalle{margin:var(--e1) 0 0;color:var(--tinta-2);font-size:var(--t-sm)}
.mc-zona{margin-top:var(--e3);display:flex;flex-direction:column;gap:var(--e3)}
.mc-lista{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;
  border-top:1px solid var(--linea)}
.mc-alumno{display:flex;align-items:center;gap:var(--e3);flex-wrap:wrap;
  padding:var(--e2) 0;border-bottom:1px solid var(--linea)}
.mc-nombre{flex:1;min-width:14ch;color:var(--tinta)}
.mc-marcas{display:flex;gap:4px}
.mc-marca{min-height:44px;min-width:44px;padding:0 var(--e3);border-radius:var(--r-md);
  border:1px solid var(--linea);background:var(--panel);color:var(--tinta-2);cursor:pointer;
  transition:background-color .15s ease,color .15s ease,border-color .15s ease}
.mc-marca[aria-pressed="true"][data-v="presente"]{background:var(--ok-f);color:var(--ok);border-color:var(--ok)}
.mc-marca[aria-pressed="true"][data-v="atraso"]{background:var(--alerta-f);color:var(--alerta);border-color:var(--alerta)}
.mc-marca[aria-pressed="true"][data-v="ausente"]{background:var(--riesgo-f);color:var(--riesgo);border-color:var(--riesgo)}
@media (prefers-reduced-motion:reduce){.mc-marca{transition:none}}

/* ── Rediseño 2026-09-29 · submódulos clicables en el riel ──────────────────────
   Un grupo con varias pantallas lleva debajo cada pantalla como ENLACE propio. Se
   marca el activo con fondo y peso (DESIGN §6: nunca franja lateral). El punto es
   el mismo vocabulario de las pantallas del ERP, para que las dos repisas se lean
   igual. Objetivo táctil ≥ 32 px (§9). */
.riel-it-grupo{display:flex;flex-direction:column}
.riel-it-fila{display:flex;align-items:center;gap:2px}
.riel-it-fila > .riel-it{flex:1;min-width:0}
.riel-it-abrir{
  flex:none;display:grid;place-items:center;width:28px;min-height:32px;padding:0;
  border:0;border-radius:var(--r);background:none;color:var(--riel-txt-2);cursor:pointer;
}
.riel-it-abrir:hover{background:var(--riel-activo);color:var(--tinta)}
.riel-it-abrir:focus-visible{outline:2px solid var(--app, var(--navy-logo));outline-offset:1px}
.riel-it-abrir svg{width:10px;height:10px;transition:transform var(--rapido) ease-out}
.riel-it-abrir[aria-expanded="true"] svg{transform:rotate(-180deg)}
.riel-sub-lista{display:flex;flex-direction:column;gap:1px;margin:1px 0 4px 10px;
  padding-left:8px;border-left:1px solid var(--riel-linea)}
.riel-sub-lista[hidden]{display:none}
.riel-sub-it{
  display:flex;align-items:center;gap:8px;min-height:30px;padding:6px 10px;
  border-radius:var(--r);color:var(--riel-txt);text-decoration:none;
  font-size:var(--t-xs);line-height:1.3;
}
.riel-sub-it .pt{width:5px;height:5px;border-radius:50%;flex:none;background:var(--riel-txt-2);opacity:.6}
.riel-sub-it .nm{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.riel-sub-it:hover{background:var(--riel-activo);color:var(--tinta)}
.riel-sub-it[aria-current="page"]{background:var(--seleccion);color:var(--navy-logo);font-weight:650}
.riel-sub-it[aria-current="page"] .pt{background:currentColor;opacity:1}
@media (prefers-reduced-motion: reduce){.riel-it-abrir svg{transition:none}}

/* ── Matrículas históricas en tramos de 5 años ──────────────────────────────── */
.historico-tramos{margin:var(--e3) var(--e4) var(--e3)}
.historico-tramo{min-width:0}

/* ── ERP · limpieza de la Facturación (SRI) (captura de Hidalgo, 2026-09-29) ─────
   Los formularios salían como una fila corrida de etiquetas y cajas pegadas (el
   `.campos` era un flex sin rejilla): cada campo quedaba a un ancho distinto y la
   etiqueta competía con la caja de al lado. Ahora: rejilla de columnas iguales,
   etiqueta ENCIMA de su caja (se lee de arriba abajo, sin buscar a qué caja
   pertenece), y la barra de filtros con aire propio dentro del panel. */
.sri-form{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));
  gap:var(--e3) var(--e4);align-items:end}
.sri-form .campo,.sri-form .campo.fila{display:flex;flex-direction:column;align-items:stretch;gap:5px}
.sri-form .campo > span{font-size:var(--t-2xs);font-weight:600;color:var(--tinta-2);
  text-transform:none;letter-spacing:0;line-height:1.3}
.sri-form .input,.sri-form .select{width:100%;min-width:0}
.sri-form-sub{margin:var(--e5) 0 var(--e2);font-size:var(--t-sm);font-weight:650;color:var(--tinta)}
.sri-filtros{display:grid;grid-template-columns:repeat(auto-fill,minmax(170px,1fr));
  gap:var(--e3);align-items:end;padding:var(--e3) var(--e4);
  border-bottom:1px solid var(--linea);margin:0}
.sri-filtros .campo,.sri-filtros .campo.fila{display:flex;flex-direction:column;align-items:stretch;gap:5px}
.sri-filtros .campo > span{font-size:var(--t-2xs);font-weight:600;color:var(--tinta-2);text-transform:none;letter-spacing:0}
.sri-filtros .input,.sri-filtros .select{width:100%;min-width:0}
/* Un vacío dentro de un panel NO es otro panel: sin caja, compacto. Los dos
   «Nada que aprobar» / «Sin comprobantes» medían ~190 px de aire cada uno. */
.sri-zona .vacio,.sri-vacio-compacto .vacio{padding:var(--e5) var(--e4)}
.sri-zona .vacio .fig-puente,.sri-vacio-compacto .vacio .fig-puente{width:52px;height:24px}
@media (max-width: 640px){
  .sri-form,.sri-filtros{grid-template-columns:1fr}
}
/* La primera celda de la CABECERA es fija y llevaba el fondo del panel: sobre la fila
   navy se veía como un hueco blanco con la palabra «Curso» invisible. Hereda el fondo
   de su fila. Con 5 columnas por tramo la tabla ya no necesita ocupar max-content. */
.tabla-historico-curso thead th:first-child{background:inherit;color:inherit}
.historico-tramo .tabla-historico-curso{width:100%}
