/*!
 * portal.css — Lenguaje visual del portal de clientes (namespace .my-*).
 *
 * ══ LA IDEA ════════════════════════════════════════════════════════════════
 * Abaku es un ábaco. El isotipo son literalmente CUENTAS SOBRE VARILLAS: tres
 * rieles horizontales y cinco cuentas, una de ellas cian. Ese objeto ya dice
 * todo lo que este portal hace — contar alumnos, contar dinero, contar días —
 * y estaba sin usar.
 *
 * Así que el ábaco es el sistema:
 *   · la VARILLA (línea + cuenta) estructura el menú, los separadores y el hero;
 *   · la CUENTA es el indicador de estado universal — llena y cian = al día,
 *     hueca = pendiente, rosa = vencido. Un badge de texto dice lo mismo pero
 *     no se reconoce a dos metros;
 *   · el NÚMERO manda. Un portal de cobro tiene una sola pregunta ("¿debo
 *     algo?") y merece tipografía de titular, no una tarjeta de métrica.
 *
 * ══ REGLAS QUE SE RESPETAN ═════════════════════════════════════════════════
 * (assets/css/CSS_CONVENTIONS.md del producto)
 *   · Cero hardcodes cromáticos: el color sale de tokens --abk-*, y los valores
 *     los pone themes/portal-brand.css. Aquí se decide DÓNDE va el color.
 *   · Todo con prefijo .my-. Nada de estilizar body/h1..h6/input/button sueltos.
 *   · Ninguna fuente menor a 12px.
 *   · Dark mode por token: casi no hay overrides al final del archivo.
 *
 * --my-font la inyecta el PHP según el idioma: chino e hindi necesitan su pila
 * o el navegador pinta cajas.
 */

/* ═══════════════════════════════════════════════════════════════════════════
   1 · MEDIDAS PROPIAS DEL PORTAL
   ═════════════════════════════════════════════════════════════════════════ */
:root {
  --my-font: var(--abk-font-family);
  --my-sidebar-w: 268px;
  --my-header-h: 68px;

  /* Escala de titular. Sólo el veredicto de la portada usa --my-display; los
     importes de una LISTA usan --my-cifra, que es bastante más chica: cuando
     cada fila grita, ninguna se oye, y una lista de cargos con tipografía de
     portada se lee como si todo fuera urgente.

     El importe llegó a 62px. A ese tamaño una deuda no se informa: se reclama.
     Quien entra ya sabe que debe — el número tiene que ser lo primero que se
     lee, no un reproche a pantalla completa. 38px sigue ganando a todo lo demás
     de la página por goleada y se lee de un vistazo desde lejos. */
  --my-display:    clamp(26px, 1.6vw + 16px, 38px);
  --my-display-sm: clamp(22px, 1.2vw + 14px, 30px);
  --my-cifra:      clamp(19px, .9vw + 14px, 24px);
  --my-eyebrow:    12px;

  /* La varilla del ábaco. */
  --my-bead: 11px;
  --my-rail-w: 2px;

  /* EL FILO. Una línea de luz de 1px en el canto superior de cada superficie.
     Es el detalle que separa "un rectángulo con color de fondo" de "un objeto
     con material": en el mundo real la luz llega de arriba y el canto superior
     de cualquier cosa la recoge. Cuesta una sombra interior y se nota en toda
     la pantalla, sobre todo en oscuro. */
  --my-filo: inset 0 1px 0 color-mix(in oklch, var(--abk-grad-ink) 7%, transparent);
}

.my-body {
  margin: 0;
  font-family: var(--my-font);
  font-size: var(--abk-font-size-base);
  line-height: var(--abk-line-height, 1.55);
  color: var(--abk-text);
  background: var(--abk-bg-shell);
  -webkit-font-smoothing: antialiased;
}

/* Etiqueta de sección: minúscula, espaciada, discreta. Es el contrapeso del
   número gigante — sin algo pequeño al lado, lo grande no se lee como grande. */
.my-eyebrow {
  margin: 0;
  font-size: var(--my-eyebrow);
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--abk-text-muted);
}

/* ═══════════════════════════════════════════════════════════════════════════
   2 · LA VARILLA Y LA CUENTA — el motivo del ábaco
   ═════════════════════════════════════════════════════════════════════════ */

/* Separador con cuenta al centro. Sustituye a los <hr> y a los bordes tristes. */
.my-varilla {
  --my-varilla-color: var(--abk-border-section);
  position: relative;
  height: var(--my-rail-w);
  border: 0;
  margin: var(--abk-space-5) 0;
  background: var(--my-varilla-color);
  border-radius: var(--abk-radius-pill);
}
.my-varilla::after {
  content: "";
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  width: var(--my-bead);
  height: var(--my-bead);
  translate: -50% -50%;
  border-radius: 50%;
  background: var(--abk-accent);
  box-shadow: 0 0 0 4px var(--abk-bg-shell), 0 0 14px var(--abk-accent-glow);
}

/* Cuenta suelta: el indicador de estado del portal. */
.my-cuenta-bead {
  flex: none;
  display: inline-block;
  width: var(--my-bead);
  height: var(--my-bead);
  border-radius: 50%;
  background: var(--abk-text-muted);
}
.my-cuenta-bead--ok {
  background: var(--abk-accent);
  box-shadow: 0 0 12px var(--abk-accent-glow);
}
.my-cuenta-bead--warn { background: var(--abk-warn); box-shadow: 0 0 12px var(--abk-warn-glow); }
.my-cuenta-bead--err  { background: var(--abk-err);  box-shadow: 0 0 12px var(--abk-err-glow); }
.my-cuenta-bead--off  { background: transparent; box-shadow: inset 0 0 0 2px var(--abk-border-strong); }

/* ═══════════════════════════════════════════════════════════════════════════
   3 · PÁGINAS DE ACCESO — pantalla partida: marca a un lado, formulario al otro
   ═════════════════════════════════════════════════════════════════════════ */

.my-body--auth {
  min-height: 100vh;
  display: grid;
  grid-template-columns: 1.05fr 1fr;
  background: var(--abk-bg-shell);
}

/* Panel de marca: el gradiente del isotipo, la luz viva encima y la marca al
   centro. Es lo primero que ve quien llega desde un correo de cobro y necesita
   reconocer de quién es esta pantalla antes de escribir su contraseña. */
.my-brandpane {
  position: relative;
  display: grid;
  place-items: center;
  padding: var(--abk-space-6);
  overflow: hidden;
  background: var(--abk-grad-hero);
  color: var(--abk-grad-ink);
  isolation: isolate;
}

/* Malla sutil: le da textura al gradiente para que no se vea como un degradado
   de plantilla. Va en ::before para no ensuciar el markup. */
.my-brandpane::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  /* La malla se deriva de la tinta del panel, no de un blanco fijo: así sigue
     al tema cuando el degradado baja a índigo en modo oscuro. */
  background-image:
    radial-gradient(circle at 1px 1px,
      color-mix(in oklch, var(--abk-grad-ink) 22%, transparent) 1px, transparent 0);
  background-size: 26px 26px;
  opacity: .5;
  mask-image: radial-gradient(120% 90% at 20% 0%, #000 30%, transparent 75%);
}

/* Viñeta. Nació como un velo al pie para salvar el texto de abajo; con la marca
   al centro hace algo mejor: oscurece los bordes y deja el medio como la zona
   más limpia del panel. El logo queda donde la luz es más pareja, y no depende
   de por dónde ande una mancha en ese momento — un fondo que cambia no puede
   dejar la legibilidad al azar. */
.my-brandpane::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(78% 64% at 50% 45%, transparent 42%, rgba(0,0,0,.24) 100%);
}

/* ── EL FONDO VIVO ───────────────────────────────────────────────────────
   Tres capas a velocidades muy distintas. Esa diferencia es lo que se lee como
   profundidad; si todo va al mismo ritmo, el conjunto se aplana.

   ── Lo que falló en el primer intento ─────────────────────────────────────
   Sólo había manchas, y muy lentas: 16% de recorrido en 37 segundos son unos
   0.15 píxeles por fotograma. Eso NO es movimiento lento, es una imagen fija —
   nadie mira una pantalla de acceso 37 segundos para comprobarlo. Dos cosas lo
   arreglan: acortar los ciclos, y sobre todo meter una capa CON BORDE. El ojo
   detecta movimiento persiguiendo bordes; un degradado difuso que se desplaza
   no da nada que perseguir por muy rápido que vaya.

   Deliberadamente NO responde al puntero: un fondo que persigue el ratón es un
   truco que se nota a los dos segundos y estorba mientras se escribe una
   contraseña. Esto se mueve porque el tiempo pasa, no porque tú estés ahí. */
.my-aurora {
  position: absolute;
  inset: 0;
  /* Debajo de la malla de puntos (que está en ::before, z-index -1): la textura
     tiene que quedar ENCIMA de la luz o el degradado se ve plano otra vez. */
  z-index: -2;
  overflow: hidden;
  pointer-events: none;
}

/* ── Capa 1 · las manchas: la luz de marca ─────────────────────────────────
   Cuatro focos, y cada uno hace tres cosas a la vez: se desplaza, cambia de
   tamaño y sube y baja de intensidad. Ese tercer eje es el importante — si sólo
   se mueven, el degradado es siempre el mismo dibujo cambiado de sitio; al
   respirar cada uno por su cuenta, la mezcla de colores es distinta en cada
   momento. Encima, la capa entera va derivando de tinte muy despacio. */
.my-aurora__luz {
  position: absolute;
  inset: 0;
  /* La deriva de tinte. ±14° de matiz sobre 46s: nunca se pilla en el acto,
     pero el fondo de dentro de un minuto no es el de ahora. La saturación
     acompaña para que el momento más frío no se vea también más apagado. */
  animation: my-aurora-tinte 46s ease-in-out infinite alternate;
}
@keyframes my-aurora-tinte {
  from { filter: hue-rotate(-14deg) saturate(.95); }
  to   { filter: hue-rotate(14deg)  saturate(1.18); }
}

.my-aurora__mancha {
  position: absolute;
  border-radius: 50%;
  /* 52px y no 70: con más desenfoque la mancha pierde todo canto y su
     desplazamiento deja de percibirse. */
  filter: blur(52px);
  will-change: transform, opacity;
}
/* Violeta claro, arriba a la izquierda. */
.my-aurora__mancha--1 {
  width: 58%; aspect-ratio: 1;
  inset-block-start: -16%;
  inset-inline-start: -14%;
  background: radial-gradient(circle, var(--abk-primary-3), transparent 66%);
  animation: my-aurora-1 19s ease-in-out infinite alternate;
}
/* Cian: el punto del isotipo, abajo a la derecha. */
.my-aurora__mancha--2 {
  width: 52%; aspect-ratio: 1;
  inset-block-end: -20%;
  inset-inline-end: -16%;
  background: radial-gradient(circle, var(--abk-accent), transparent 64%);
  animation: my-aurora-2 27s ease-in-out infinite alternate;
}
/* Índigo al centro: el que da profundidad y ata a los demás. */
.my-aurora__mancha--3 {
  width: 48%; aspect-ratio: 1;
  inset-block-start: 30%;
  inset-inline-start: 22%;
  background: radial-gradient(circle, var(--abk-primary-deep), transparent 68%);
  animation: my-aurora-3 23s ease-in-out infinite alternate;
}
/* El cuarto entra desde arriba a la derecha, que era la zona que se quedaba
   siempre igual. Violeta medio: hace de bisagra entre el claro y el índigo. */
.my-aurora__mancha--4 {
  width: 44%; aspect-ratio: 1;
  inset-block-start: -10%;
  inset-inline-end: -12%;
  background: radial-gradient(circle, var(--abk-primary), transparent 66%);
  animation: my-aurora-4 31s ease-in-out infinite alternate;
}

/* Ciclos 19/27/23/31 — cuatro números sin divisores comunes. La composición
   completa no se repite hasta pasadas horas, así que no hay bucle que
   memorizar por muchas veces que se entre al portal. */
@keyframes my-aurora-1 {
  from { transform: translate3d(-6%, -4%, 0) scale(.92);  opacity: .44; }
  to   { transform: translate3d(26%, 20%, 0)  scale(1.3); opacity: .72; }
}
@keyframes my-aurora-2 {
  from { transform: translate3d(6%, 6%, 0)     scale(1.16); opacity: .6; }
  to   { transform: translate3d(-28%, -22%, 0) scale(.84);  opacity: .34; }
}
@keyframes my-aurora-3 {
  from { transform: translate3d(8%, -6%, 0)   scale(.86);  opacity: .38; }
  to   { transform: translate3d(-22%, 24%, 0) scale(1.34); opacity: .66; }
}
@keyframes my-aurora-4 {
  from { transform: translate3d(10%, 8%, 0)   scale(1.22); opacity: .56; }
  to   { transform: translate3d(-18%, -4%, 0) scale(.9);   opacity: .3; }
}

/* ── Capa 2 · las motas: cuentas de polvo ──────────────────────────────────
   El guiño del ábaco. Las mismas cuentas de las varillas, sueltas y subiendo.
   Son diminutas y con brillo propio: cada una es un punto nítido, y un punto
   nítido que se desplaza se percibe aunque tarde veinte segundos en cruzar.
   Carril, tamaño, deriva y reloj los sortea el PHP en cada visita. */
.my-motas { position: absolute; inset: 0; overflow: hidden; }

.my-mota {
  position: absolute;
  inset-block-end: -12px;
  inset-inline-start: var(--mx, 50%);
  width: var(--ms, 3px);
  height: var(--ms, 3px);
  border-radius: 50%;
  background: var(--abk-grad-ink);
  opacity: 0;
  will-change: transform, opacity;
  animation: my-mota-sube var(--mt, 24s) linear var(--md, 0s) infinite;
}
/* Una de cada cinco va en cian, como la cuenta del isotipo. Lleva halo: es la
   que se ve de reojo y la que hace que el conjunto se lea como marca. */
.my-mota--acento {
  background: var(--abk-accent);
  box-shadow: 0 0 8px var(--abk-accent), 0 0 16px var(--abk-accent-glow);
}
@keyframes my-mota-sube {
  0%        { transform: translate3d(0, 0, 0);                       opacity: 0; }
  10%, 82%  { opacity: var(--mo, .4); }
  /* La deriva lateral llega entera al final: sube en curva, no en línea recta. */
  100%      { transform: translate3d(var(--mdx, 0px), -100vh, 0);    opacity: 0; }
}

/* Quien pidió menos movimiento se queda con la misma composición, quieta. */
@media (prefers-reduced-motion: reduce) {
  .my-aurora__luz,
  .my-aurora__mancha,
  .my-marca__leyenda,
  .my-mota { animation: none; }
  /* Sin animación, las manchas se quedarían en la opacidad 0 de su keyframe.
     Se les devuelve un valor de reposo o el panel sale casi liso. */
  .my-aurora__mancha { opacity: .45; }
  .my-mota { opacity: var(--mo, .4); }
  /* El eslogan se queda donde la luz aún no ha entrado: limpio, sin el cian a
     medio camino congelado en una palabra al azar. */
  .my-marca__leyenda { background-position: 140% 0; }
}

/* ── LA MARCA, AL CENTRO ──────────────────────────────────────────────────
   Isotipo, nombre y una línea. Nada más. Con tan pocos elementos, lo único que
   sostiene la composición es el aire y la alineación: cualquier margen flojo se
   ve al instante porque no hay contenido que lo tape. */
.my-marca {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  margin: auto;                  /* centrado real en el panel, no a ojo */
  padding: var(--abk-space-6) 0;
}

.my-marca__logo {
  width: clamp(76px, 6vw + 42px, 108px);
  aspect-ratio: 1;
  border-radius: 26%;
  padding: 14px;
  /* Derivado de la tinta del panel, no un blanco fijo: en modo oscuro el
     recuadro tiene que atenuarse con el resto. */
  background: color-mix(in oklch, var(--abk-grad-ink) 10%, transparent);
  /* Vidrio: filo de luz arriba, borde de un pelo, y sombra propia para que la
     pastilla se despegue del fondo en vez de estar impresa sobre él. */
  border: 1px solid color-mix(in oklch, var(--abk-grad-ink) 16%, transparent);
  box-shadow: inset 0 1px 0 color-mix(in oklch, var(--abk-grad-ink) 28%, transparent),
              0 12px 34px rgba(0,0,0,.32);
  color: var(--abk-grad-ink);
}

/* ── EL ISOTIPO ───────────────────────────────────────────────────────────
   Quieto, y por una razón: se intentó animarlo — las cuentas del propio logo
   deslizándose por sus varillas, que sobre el papel era el mejor sitio para el
   gesto del ábaco. En pantalla no: al moverse las cuentas, el isotipo deja de
   ser el isotipo. Un logo es una forma que se reconoce de un vistazo; si sus
   piezas cambian de sitio, cada instante es un dibujo distinto y ninguno es la
   marca. El fondo puede permitirse estar vivo porque no tiene que ser
   reconocible. El logo no.

   Sigue en línea y no como <img> por dos cosas que no dependen de animarlo: los
   colores salen de los tokens (--abk-accent para la cuenta cian, currentColor
   para el resto, así sigue al tema) y se ahorra una petición — son 400 bytes. */
.my-iso { display: block; width: 100%; height: 100%; }

.my-iso__cuenta { fill: currentColor; }

/* La cuenta cian del isotipo. Con su halo, que es lo único que la separa del
   resto y lo que hace que el logo tenga un punto de color propio. */
.my-iso__cuenta--acento {
  fill: var(--abk-accent);
  filter: drop-shadow(0 0 3px var(--abk-accent));
}

.my-marca__nombre {
  margin: var(--abk-space-5) 0 0;
  font-size: clamp(30px, 2.4vw + 16px, 42px);
  font-weight: 800;
  /* Tracking abierto, no cerrado. Un nombre corto y solo en medio de la pantalla
     se lee como marca cuando respira; apretado se lee como un titular más. */
  letter-spacing: .01em;
  line-height: 1;
}

/* La leyenda. No es una frase, es una etiqueta: dice qué es esto. En versalitas
   y muy espaciada, que es como se comporta una leyenda debajo de un logo — a
   ese tamaño el tracking abierto es lo que la separa de "texto pequeño". */
.my-marca__leyenda {
  margin: var(--abk-space-3) 0 0;
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .28em;
  /* El tracking añade el hueco DESPUÉS de la última letra también, y descentra
     la línea a ojo. Se compensa con el mismo valor de sangría. */
  text-indent: .28em;
  line-height: 1.4;

  /* La leyenda respira luz. Una veta cian la recorre cada 9 segundos, muy
     abierta y sin llegar a apagar el blanco: no parpadea, sólo cambia de
     temperatura al pasar. Es la misma luz del fondo aplicada a la tipografía —
     la pantalla entera se comporta como una superficie sobre la que pasa luz. */
  background: linear-gradient(100deg,
    var(--abk-grad-ink) 0%,
    var(--abk-grad-ink) 40%,
    color-mix(in oklch, var(--abk-accent) 70%, var(--abk-grad-ink)) 50%,
    var(--abk-grad-ink) 60%,
    var(--abk-grad-ink) 100%);
  background-size: 280% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: my-leyenda-luz 9s ease-in-out infinite;
}
@keyframes my-leyenda-luz {
  0%, 100% { background-position: 140% 0; }
  50%      { background-position: -40% 0; }
}
/* Sin background-clip: text el eslogan se quedaría invisible. Mismo salvavidas
   que ya lleva .my-brand en el shell. */
@supports not (background-clip: text) {
  .my-marca__leyenda {
    -webkit-text-fill-color: currentColor;
    background: none;
    animation: none;
  }
}


/* Panel del formulario. */
.my-authpane {
  position: relative;
  display: grid;
  place-items: center;
  padding: var(--abk-space-6) var(--abk-space-5);
}
/* ── LA JUNTA, RETROILUMINADA ─────────────────────────────────────────────
   Un borde recto de arriba abajo se ve como un corte. Esto es otra cosa: una
   tira de led cian metida DETRÁS del canto del panel del formulario, que se
   desborda sobre el panel de marca.

   Son dos piezas, y hacen falta las dos. El led sin derrame es una raya de
   color; el derrame sin led es una mancha que no se sabe de dónde sale. Juntos
   el ojo reconstruye la escena: hay una fuente estrecha detrás de ese canto, y
   lo que se ve iluminado es la pared de al lado.

   Todo en propiedades lógicas: en árabe (dir="rtl") los paneles se
   intercambian, el led sigue pegado al canto del formulario y la luz sigue
   cayendo sobre la marca, sin una sola regla aparte. */

/* 1 · El led. Nace y muere en los extremos — una tira tiene largo, y si llega
   a tocar el borde de la pantalla se lee como marco y no como fuente. */
.my-authpane::before {
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  width: 1px;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(to bottom,
    transparent,
    color-mix(in oklch, var(--abk-accent) 30%, transparent) 22%,
    color-mix(in oklch, var(--abk-accent) 62%, transparent) 50%,
    color-mix(in oklch, var(--abk-accent) 30%, transparent) 78%,
    transparent);
  /* El halo inmediato: lo que en una foto sería el sobreexpuesto del propio
     led, antes de que la luz empiece a caer. */
  box-shadow: 0 0 12px color-mix(in oklch, var(--abk-accent) 26%, transparent);
}

/* 2 · El derrame sobre el panel de marca. Se sale del panel del formulario por
   su canto interior y cae encima del vecino.

   La caída NO es lineal: la luz real pierde intensidad rápido al principio y
   luego arrastra una cola larga. De ahí las tres paradas — 8% de opacidad casi
   pegado al canto, y ya en el 22% de la distancia va por la mitad. Con un
   degradado lineal el derrame se ve como una banda pintada. */
.my-authpane::after {
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline-start: -110px;
  width: 110px;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(to var(--my-derrame, left),
    color-mix(in oklch, var(--abk-accent) 8%, transparent) 0%,
    color-mix(in oklch, var(--abk-accent) 4%, transparent) 22%,
    transparent 72%);
  /* Screen en vez de opacidad plana: así se SUMA a lo que hay debajo, que es lo
     que hace la luz. Con alfa normal, el cian ensucia el violeta del fondo en
     vez de encenderlo. */
  mix-blend-mode: screen;
  /* Se apaga arriba y abajo igual que el led: la luz no puede llegar a donde no
     llega la tira. */
  mask-image: linear-gradient(to bottom, transparent, #000 20%, #000 80%, transparent);
}
/* El degradado del derrame apunta hacia el panel de marca, que en RTL cambia de
   lado. Es lo único que las propiedades lógicas no cubren solas. */
html[dir="rtl"] .my-authpane::after { --my-derrame: right; }
.my-auth { width: 100%; max-width: 400px; }

.my-auth__head { margin-bottom: var(--abk-space-5); }
.my-auth__title {
  margin: var(--abk-space-2) 0 var(--abk-space-2);
  font-size: var(--my-display-sm);
  font-weight: 800;
  letter-spacing: -.03em;
  line-height: 1.1;
  color: var(--abk-text-heading);
}
.my-auth__sub {
  margin: 0;
  color: var(--abk-text-muted);
  line-height: 1.5;
}

.my-auth__form {
  display: flex;
  flex-direction: column;
  gap: var(--abk-space-3);
  margin: 0 0 var(--abk-space-4);
}
.my-auth__submit, .my-auth__magic { width: 100%; }

.my-auth__sep {
  display: flex;
  align-items: center;
  gap: var(--abk-space-3);
  margin-block: var(--abk-space-4);
  color: var(--abk-text-muted);
  font-size: var(--abk-font-size-sm);
}
.my-auth__sep::before,
.my-auth__sep::after {
  content: "";
  flex: 1;
  height: var(--my-rail-w);
  border-radius: var(--abk-radius-pill);
  background: var(--abk-border-soft);
}

.my-auth__olvide { margin: 0 0 var(--abk-space-4); font-size: var(--abk-font-size-sm); }

.my-auth__foot {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--abk-space-3);
  flex-wrap: wrap;
  padding-top: var(--abk-space-4);
  border-top: 1px solid var(--abk-border-soft);
}

.my-auth__quien {
  margin: 0 0 var(--abk-space-4);
  padding: var(--abk-space-2) var(--abk-space-3);
  border-radius: var(--abk-radius-sm);
  background: var(--abk-surface-2);
  font-family: var(--abk-font-mono);
  font-size: var(--abk-font-size-sm);
  color: var(--abk-text-soft);
  overflow-wrap: anywhere;
}

@media (max-width: 900px) {
  .my-body--auth { grid-template-columns: 1fr; }
  /* En móvil el panel de marca se reduce a una cabecera: el formulario es lo
     que la persona vino a usar. Queda logo y nombre en una fila — lo justo para
     reconocer la pantalla — y el fondo animado sigue debajo. */
  .my-brandpane { padding: var(--abk-space-5); }
  .my-marca {
    flex-direction: row;
    align-items: center;
    gap: var(--abk-space-3);
    padding: 0;
  }
  .my-marca__logo { width: 52px; padding: 7px; border-radius: 15px; }
  .my-marca__nombre { margin: 0; font-size: 26px; }
  .my-marca__leyenda { display: none; }
  /* El led vertical y su derrame no tienen sentido cuando los paneles quedan
     uno encima del otro: la junta pasa a ser horizontal. */
  .my-authpane::before,
  .my-authpane::after { display: none; }
  .my-authpane { padding: var(--abk-space-5) var(--abk-space-4) var(--abk-space-6); }
}

/* ═══════════════════════════════════════════════════════════════════════════
   4 · AVISOS
   ═════════════════════════════════════════════════════════════════════════ */

.my-alert {
  display: flex;
  align-items: flex-start;
  gap: var(--abk-space-3);
  margin: 0 0 var(--abk-space-4);
  padding: var(--abk-space-3) var(--abk-space-4);
  border-radius: var(--abk-radius);
  font-size: var(--abk-font-size-base);
  line-height: 1.5;
  border: 1px solid transparent;
  border-inline-start-width: 3px;
}
.my-alert::before {
  content: "";
  flex: none;
  width: 8px; height: 8px;
  margin-block-start: .5em;
  border-radius: 50%;
  background: currentColor;
}
.my-alert--err  { color: var(--abk-err-strong);  background: var(--abk-err-bg);  border-color: var(--abk-err); }
.my-alert--ok   { color: var(--abk-ok-strong);   background: var(--abk-ok-bg);   border-color: var(--abk-ok); }
.my-alert--warn { color: var(--abk-warn-strong); background: var(--abk-warn-bg); border-color: var(--abk-warn); }
.my-alert--info { color: var(--abk-info-strong); background: var(--abk-info-bg); border-color: var(--abk-info); }

/* ═══════════════════════════════════════════════════════════════════════════
   5 · SELECTOR DE IDIOMA
   ═════════════════════════════════════════════════════════════════════════ */

/* Selector de idioma compacto. Con cuatro idiomas —y con chino e hindi, que
   ocupan anchos muy distintos— una fila de píldoras se come el header. */
.my-langsel {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0 var(--abk-space-2) 0 var(--abk-space-3);
  border: 1px solid var(--abk-border-section);
  border-radius: var(--abk-radius-pill);
  background: var(--abk-surface);
  transition: border-color .15s;
}
.my-langsel:hover,
.my-langsel:focus-within { border-color: var(--abk-accent); }

.my-langsel__ico { display: grid; place-items: center; color: var(--abk-text-muted); }

.my-langsel__sel {
  border: 0;
  background: transparent;
  color: var(--abk-text);
  font: inherit;
  font-size: var(--abk-font-size-sm);
  font-weight: 600;
  padding: 7px 2px;
  cursor: pointer;
  /* El select nativo no se estiliza bien entre navegadores; se le quita la
     flecha del sistema y el contenedor pone el marco. */
  appearance: none;
  -webkit-appearance: none;
}
.my-langsel__sel:focus { outline: none; }
.my-langsel__sel option { color: var(--abk-text); background: var(--abk-surface); }

.my-lang { display: flex; flex-wrap: wrap; gap: 4px; }
.my-lang__btn {
  padding: 4px 10px;
  border-radius: var(--abk-radius-pill);
  font-size: var(--abk-font-size-sm);
  font-weight: 600;
  color: var(--abk-text-muted);
  text-decoration: none;
  transition: background var(--abk-transition-fast, .15s), color var(--abk-transition-fast, .15s);
}
.my-lang__btn:hover { background: var(--abk-surface-2); color: var(--abk-text); }
.my-lang__btn.is-active {
  background: var(--abk-accent);
  color: var(--abk-primary-ink);
  box-shadow: 0 0 14px var(--abk-accent-glow);
}

/* ═══════════════════════════════════════════════════════════════════════════
   6 · SHELL — la varilla vertical con las cuentas del menú
   ═════════════════════════════════════════════════════════════════════════ */

.my-shell {
  display: grid;
  grid-template-columns: var(--my-sidebar-w) 1fr;
  min-height: 100vh;
}

.my-sidebar {
  display: flex;
  flex-direction: column;
  gap: var(--abk-space-5);
  padding: var(--abk-space-5) var(--abk-space-4);
  background: var(--abk-surface);
  border-inline-end: 1px solid var(--abk-border-soft);
}

.my-brand {
  display: flex;
  align-items: center;
  gap: var(--abk-space-3);
  font-size: 22px;
  font-weight: 800;
  letter-spacing: -.02em;
  color: var(--abk-primary-2);
  background: var(--abk-grad-text);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}
@supports not (background-clip: text) {
  .my-brand { -webkit-text-fill-color: currentColor; background: none; }
}
.my-brand__logo {
  flex: none;
  width: 40px;
  height: 40px;
  border-radius: 11px;
  box-shadow: var(--abk-shadow-1);
}

/* Ficha de la cuenta: el nombre de la escuela es lo que confirma "estoy donde
   creo". Va con su cuenta de estado al lado. */
.my-ficha {
  padding: var(--abk-space-3);
  border-radius: var(--abk-radius);
  background: var(--abk-bg-tint-primary);
  border: 1px solid var(--abk-border-soft);
}
.my-ficha__nombre {
  display: block;
  font-weight: 700;
  line-height: 1.3;
  color: var(--abk-text);
  overflow-wrap: anywhere;
}
.my-ficha__estado {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-block-start: 6px;
  font-size: var(--abk-font-size-sm);
  color: var(--abk-text-muted);
}

/* ── El menú es una varilla vertical con una cuenta por módulo ─────────── */
.my-nav {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding-inline-start: 22px;
}
/* La varilla: una línea que recorre todos los ítems. */
.my-nav::before {
  content: "";
  position: absolute;
  inset-block: 14px;
  inset-inline-start: 5px;
  width: var(--my-rail-w);
  border-radius: var(--abk-radius-pill);
  background: var(--abk-border-section);
}

.my-nav__item {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--abk-space-2);
  padding: 9px var(--abk-space-3);
  border-radius: var(--abk-radius-sm);
  color: var(--abk-text-soft);
  text-decoration: none;
  font-weight: 500;
  transition: background var(--abk-transition-fast, .15s),
              color var(--abk-transition-fast, .15s);
}
/* Su cuenta, ensartada en la varilla. */
.my-nav__item::before {
  content: "";
  position: absolute;
  inset-inline-start: -22px;
  inset-block-start: 50%;
  width: 12px; height: 12px;
  translate: 0 -50%;
  border-radius: 50%;
  background: var(--abk-surface);
  box-shadow: inset 0 0 0 var(--my-rail-w) var(--abk-border-strong);
  transition: background .18s var(--abk-ease, ease), box-shadow .18s;
}
.my-nav__item:hover {
  background: var(--abk-surface-2);
  color: var(--abk-text);
}
.my-nav__item:hover::before { box-shadow: inset 0 0 0 var(--my-rail-w) var(--abk-primary-3); }

.my-nav__item.is-active {
  background: var(--abk-bg-tint-primary);
  color: var(--abk-primary-2);
  font-weight: 700;
}
/* La cuenta activa se llena de cian y brilla: se ve de reojo dónde estás. */
.my-nav__item.is-active::before {
  background: var(--abk-accent);
  box-shadow: 0 0 0 var(--my-rail-w) var(--abk-accent), 0 0 14px var(--abk-accent-glow);
}

.my-nav__icon { width: 1.15em; text-align: center; opacity: .85; }

/* Vuelta a su propio ABAKU: el camino de regreso tan visible como el de ida. */
.my-volver {
  margin-block-start: auto;
  display: flex;
  align-items: center;
  gap: var(--abk-space-2);
  padding: var(--abk-space-3);
  border-radius: var(--abk-radius);
  border: 1px dashed var(--abk-border-section);
  color: var(--abk-text-muted);
  text-decoration: none;
  font-size: var(--abk-font-size-sm);
  transition: border-color .15s, color .15s;
}
.my-volver:hover { border-style: solid; border-color: var(--abk-accent); color: var(--abk-text); }

/* ── Cabecera ─────────────────────────────────────────────────────────── */
.my-main { display: flex; flex-direction: column; min-width: 0; }

.my-header {
  position: sticky;
  inset-block-start: 0;
  z-index: var(--abk-z-header, 30);
  display: flex;
  align-items: center;
  gap: var(--abk-space-3);
  min-height: var(--my-header-h);
  padding: 0 var(--abk-space-5);
  background: var(--abk-glass-bg, var(--abk-surface));
  backdrop-filter: blur(14px);
  border-block-end: 1px solid var(--abk-border-soft);
}
.my-header__title {
  margin: 0;
  font-size: var(--abk-font-size-h3);
  font-weight: 700;
  letter-spacing: -.02em;
  color: var(--abk-text-heading);
}
.my-header__actions {
  display: flex;
  align-items: center;
  gap: var(--abk-space-2);
  margin-inline-start: auto;
}

.my-iconbtn {
  display: grid;
  place-items: center;
  width: 36px; height: 36px;
  border: 1px solid var(--abk-border-section);
  border-radius: var(--abk-radius-sm);
  background: transparent;
  color: var(--abk-text-soft);
  cursor: pointer;
  font-size: var(--abk-font-size-base);
  transition: background .15s, color .15s, border-color .15s;
}
.my-iconbtn:hover {
  background: var(--abk-surface-2);
  color: var(--abk-text);
  border-color: var(--abk-border-strong);
}
.my-menu-toggle { display: none; }

.my-user { display: flex; align-items: center; gap: var(--abk-space-2); }
.my-user__chip {
  display: flex;
  align-items: center;
  gap: var(--abk-space-2);
  padding: 4px 10px 4px 4px;
  border-radius: var(--abk-radius-pill);
  text-decoration: none;
  color: inherit;
  transition: background .15s;
}
.my-user__chip:hover { background: var(--abk-surface-2); }
.my-user__avatar {
  display: grid;
  place-items: center;
  width: 32px; height: 32px;
  border-radius: 50%;
  background: var(--abk-grad-primary);
  color: var(--abk-grad-ink);
  font-weight: 800;
  font-size: var(--abk-font-size-sm);
}
.my-user__meta { display: flex; flex-direction: column; line-height: 1.25; }
.my-user__name { font-size: var(--abk-font-size-sm); font-weight: 700; }
.my-user__role { font-size: var(--abk-font-size-sm); color: var(--abk-text-muted); }

.my-content {
  flex: 1;
  width: 100%;
  max-width: 1120px;
  padding: var(--abk-space-6) var(--abk-space-5) var(--abk-space-6);
  display: flex;
  flex-direction: column;
  gap: var(--abk-space-6);
}

/* La raíz del módulo. Sin esto sus secciones salían pegadas literalmente —
   .my-content sí tenía gap, pero todas las secciones del módulo cuelgan de
   ESTE div, que era un contenedor de bloque sin separación ninguna: dos
   paneles seguidos quedaban borde contra borde y se leían como uno solo.

   El aire entre secciones es más grande que el de dentro de una sección (32
   contra 24): es lo que hace que se lean como cosas distintas sin necesidad de
   marcos ni líneas. */
.abk-mod-root {
  display: flex;
  flex-direction: column;
  gap: var(--abk-space-6);
}
/* Con gap, el margen inferior que traían avisos y notas sueltas se suma y abre
   un hueco doble justo debajo de ellos. El gap manda; el margen sobra. */
.abk-mod-root > * { margin-block-end: 0; }

/* Cinta del SSO. */
.my-ssobar {
  display: flex;
  align-items: center;
  gap: var(--abk-space-3);
  padding: var(--abk-space-3) var(--abk-space-4);
  border-radius: var(--abk-radius);
  background: var(--abk-bg-tint-primary);
  border: 1px solid var(--abk-border-soft);
  border-inline-start: 3px solid var(--abk-accent);
  font-size: var(--abk-font-size-sm);
  color: var(--abk-text-soft);
}

/* ═══════════════════════════════════════════════════════════════════════════
   7 · PANELES — la card del portal
   ─────────────────────────────────────────────────────────────────────────
   .my-panel en vez de reusar .abk-card: el portal necesita su propio marco
   (radio grande, borde suave, franja de marca opcional) y las convenciones
   prohíben re-estilizar los componentes globales.
   ═════════════════════════════════════════════════════════════════════════ */

.my-panel {
  position: relative;
  padding: var(--abk-space-5);
  border-radius: var(--abk-radius-lg);
  background: var(--abk-surface);
  border: 1px solid var(--abk-border-soft);
  box-shadow: var(--my-filo), var(--abk-shadow-1);
  overflow: hidden;
}
/* Variante con franja de marca arriba — para el panel que abre una sección. */
.my-panel--marcado::before {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  height: 3px;
  background: var(--abk-module-topbar);
}

.my-panel__head {
  display: flex;
  align-items: flex-start;
  gap: var(--abk-space-3);
  flex-wrap: wrap;
  margin-block-end: var(--abk-space-4);
}
.my-panel__title {
  margin: 0;
  font-size: var(--abk-font-size-h3);
  font-weight: 700;
  letter-spacing: -.02em;
  color: var(--abk-text-heading);
}
.my-panel__help { margin: 6px 0 0; font-size: var(--abk-font-size-sm); color: var(--abk-text-muted); }

.my-grid {
  display: grid;
  gap: var(--abk-space-4);
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}

/* ═══════════════════════════════════════════════════════════════════════════
   8 · NÚMEROS Y ESTADOS
   ═════════════════════════════════════════════════════════════════════════ */

.my-cifra {
  display: block;
  font-size: var(--my-cifra);
  font-weight: 700;
  letter-spacing: -.025em;
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
  color: var(--abk-text-heading);
}
/* Sólo para el veredicto de la portada: ahí SÍ hay una sola cifra y tiene que
   destacar sobre todo lo demás. Peso 700, no 800: a este tamaño el extranegrita
   ya no aporta jerarquía, sólo dureza. */
.my-cifra--xl {
  font-size: var(--my-display);
  font-weight: 700;
  letter-spacing: -.03em;
}

.my-stat { display: flex; flex-direction: column; gap: var(--abk-space-2); }
.my-stat__sub { font-size: var(--abk-font-size-sm); color: var(--abk-text-muted); }

/* Píldora de estado. Lleva su cuenta a la izquierda: forma + color, no sólo
   color, para quien no distingue rojo de verde. */
.my-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 11px 4px 8px;
  border-radius: var(--abk-radius-pill);
  font-size: var(--abk-font-size-sm);
  font-weight: 700;
  border: 1px solid transparent;
  white-space: nowrap;
}
.my-badge::before {
  content: "";
  width: 7px; height: 7px;
  border-radius: 50%;
  background: currentColor;
}
.my-badge--ok   { color: var(--abk-ok-strong);   background: var(--abk-ok-bg);   border-color: color-mix(in oklch, var(--abk-ok) 40%, transparent); }
.my-badge--warn { color: var(--abk-warn-strong); background: var(--abk-warn-bg); border-color: color-mix(in oklch, var(--abk-warn) 42%, transparent); }
.my-badge--err  { color: var(--abk-err-strong);  background: var(--abk-err-bg);  border-color: color-mix(in oklch, var(--abk-err) 40%, transparent); }
.my-badge--mute { color: var(--abk-text-muted);  background: var(--abk-surface-2); border-color: var(--abk-border-section); }

/* ═══════════════════════════════════════════════════════════════════════════
   9 · TABLAS
   ═════════════════════════════════════════════════════════════════════════ */

.my-tablewrap {
  overflow-x: auto;
  border-radius: var(--abk-radius);
  border: 1px solid var(--abk-border-soft);
}
.my-table { width: 100%; border-collapse: collapse; font-size: var(--abk-font-size-base); }
.my-table th, .my-table td {
  padding: var(--abk-space-3) var(--abk-space-4);
  text-align: start;
  white-space: nowrap;
}
.my-table thead th {
  font-size: var(--my-eyebrow);
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--abk-text-muted);
  background: var(--abk-surface-2);
  border-block-end: 1px solid var(--abk-border-soft);
}
.my-table tbody td { border-block-end: 1px solid var(--abk-border-soft); }
.my-table tbody tr:last-child td { border-block-end: 0; }
.my-table tbody tr { transition: background .12s; }
.my-table tbody tr:hover { background: var(--abk-surface-2); }
.my-table td.is-wrap { white-space: normal; min-width: 220px; }
.my-num { font-variant-numeric: tabular-nums; text-align: end; font-weight: 600; }

.my-empty {
  padding: var(--abk-space-6) var(--abk-space-4);
  text-align: center;
  color: var(--abk-text-muted);
}

.my-actions { display: flex; flex-wrap: wrap; gap: var(--abk-space-2); }

/* Bloque de dato copiable (clave de licencia, referencia de pago). */
.my-secret {
  display: flex;
  align-items: center;
  gap: var(--abk-space-2);
  padding: var(--abk-space-3);
  border-radius: var(--abk-radius-sm);
  background: var(--abk-surface-2);
  border: 1px dashed var(--abk-border-section);
  font-family: var(--abk-font-mono);
  font-size: var(--abk-font-size-sm);
  letter-spacing: .04em;
  overflow-wrap: anywhere;
}

.my-txt-ok  { color: var(--abk-ok-strong); }
.my-txt-err { color: var(--abk-err-strong); }

[hidden] { display: none !important; }

/* ═══════════════════════════════════════════════════════════════════════════
   11 · EL CAMBIO DE CLARO A OSCURO
   ─────────────────────────────────────────────────────────────────────────
   El tema nuevo se descubre en un círculo que crece desde el botón. La lógica
   está en assets/js/tema.js; aquí sólo se declara cómo se ve.

   Todo va acotado a .my-vt / .my-theming: son clases que el JS pone durante
   medio segundo y quita. Sin acotar, cualquier otra view transition futura del
   portal heredaría este iris sin haberlo pedido.
   ═════════════════════════════════════════════════════════════════════════ */

/* El navegador anima por defecto un fundido entre las dos fotos. Se apaga: el
   fundido por debajo del iris ensucia el borde del círculo. */
html.my-vt::view-transition-old(root),
html.my-vt::view-transition-new(root) {
  animation: none;
  mix-blend-mode: normal;
}
html.my-vt::view-transition-old(root) { z-index: 0; }  /* el tema viejo se queda quieto debajo */
html.my-vt::view-transition-new(root) {
  z-index: 1;                                          /* el nuevo se revela encima */
  animation: my-iris .58s cubic-bezier(.45, .05, .2, 1) both,
             my-vt-pop .58s ease-out both;
}

/* El círculo. --my-vt-x/y (origen) y --my-vt-r (radio hasta la esquina más
   lejana) los escribe el JS con la posición real del clic. */
@keyframes my-iris {
  from { clip-path: circle(0px at var(--my-vt-x, 50%) var(--my-vt-y, 0px)); }
  to   { clip-path: circle(var(--my-vt-r, 150vmax) at var(--my-vt-x, 50%) var(--my-vt-y, 0px)); }
}

/* Un golpe de luz y saturación que se apaga solo: hace que el tema nuevo entre
   "encendido" en vez de aparecer ya asentado. Dura lo mismo que el iris. */
@keyframes my-vt-pop {
  0%   { filter: brightness(1.18) saturate(1.35); }
  100% { filter: none; }
}

/* ── Respaldo, para navegadores sin View Transitions ──────────────────────
   Sin la API no hay dos fotos que cruzar. Se anima el color de TODO a la vez
   durante medio segundo: no es el iris, pero tampoco es el salto seco. El
   !important es necesario — hay que ganarle a las transiciones propias de cada
   componente, que duran 150ms y cortarían ésta. */
html.my-theming,
html.my-theming body,
html.my-theming *:not(.my-destello) {
  transition: background-color .5s ease,
              color .45s ease,
              border-color .5s ease,
              fill .45s ease,
              box-shadow .5s ease,
              background-image .5s ease !important;
}

/* El destello que sale del botón en ese mismo respaldo. Nace de 12px y crece
   ×70: a esa escala el degradado se estira y cubre la pantalla como una onda. */
.my-destello {
  position: fixed;
  width: 12px; height: 12px;
  margin: -6px 0 0 -6px;
  border-radius: 50%;
  pointer-events: none;
  z-index: 2147483646;
  background: radial-gradient(circle,
    color-mix(in oklch, var(--abk-accent) 55%, transparent) 0%,
    color-mix(in oklch, var(--abk-primary) 40%, transparent) 38%,
    transparent 70%);
  animation: my-destello .65s ease-out forwards;
}
@keyframes my-destello {
  from { transform: scale(1);  opacity: .9; }
  to   { transform: scale(70); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  /* El JS ni siquiera entra por aquí, pero si algo lo dispara, que no se mueva. */
  html.my-vt::view-transition-new(root) { animation: none; }
  .my-destello { display: none; }
}

/* Toggle de tema flotante en las páginas de acceso. */
.my-theme-float {
  position: fixed;
  inset-block-start: var(--abk-space-4);
  inset-inline-end: var(--abk-space-4);
  z-index: var(--abk-z-header, 30);
  background: var(--abk-surface);
  box-shadow: var(--abk-shadow-2);
}

/* ═══════════════════════════════════════════════════════════════════════════
   10 · MÓVIL
   ═════════════════════════════════════════════════════════════════════════ */

@media (max-width: 900px) {
  .my-shell { grid-template-columns: 1fr; }
  .my-sidebar {
    position: fixed;
    inset-block: 0;
    inset-inline-start: 0;
    width: var(--my-sidebar-w);
    z-index: var(--abk-z-sidebar, 40);
    translate: -105% 0;
    transition: translate .22s var(--abk-ease, cubic-bezier(.4,0,.2,1));
    box-shadow: var(--abk-shadow-3);
  }
  html[dir="rtl"] .my-sidebar { translate: 105% 0; }
  .my-shell.is-nav-open .my-sidebar { translate: 0 0; }
  .my-menu-toggle { display: grid; }
  .my-content { padding: var(--abk-space-4); gap: var(--abk-space-5); }
  .abk-mod-root { gap: var(--abk-space-5); }
  .my-user__meta { display: none; }
}
