/* ═══════════════════════════════════════════════════════════════════════════
   SENSEI — sistema de diseño
   ═══════════════════════════════════════════════════════════════════════════

   Este archivo NO contiene un solo color de marca. Todos salen de
   `marca.css`, que se genera por instalación a partir de la tabla `settings`.

   La división es deliberada:
     · `marca.css`  → primitivas. Las define el cliente. Cambian por instalación.
     · `sensei.css` → semántica. La define el producto. Viaja con el despliegue.

   Un cliente puede cambiar su rojo sin poder romper el contraste del sistema,
   porque la decisión de QUÉ neutro es el fondo y CUÁL es la tinta vive acá.

   ─── Orden del archivo ─────────────────────────────────────────────────────
     1. Tokens semánticos          6. Formularios
     2. Reinicio y base            7. Retroalimentación
     3. Tipografía                 8. Navegación
     4. Disposición                9. Utilidades
     5. Componentes               10. Accesibilidad y movimiento
   ═══════════════════════════════════════════════════════════════════════════ */

/* ───────────────────────────────────────────────────────────────────────────
   1. TOKENS SEMÁNTICOS
   ─────────────────────────────────────────────────────────────────────────── */

/*
 * ┌─ Por qué `light-dark()` y no dos bloques de tokens ──────────────────────┐
 *
 * La plataforma anterior declaraba la paleta oscura DOS veces: una bajo
 * `@media (prefers-color-scheme: dark)` y otra bajo `:root[data-theme="dark"]`,
 * con 32 declaraciones copiadas literalmente entre las dos. Cualquier ajuste
 * de color había que hacerlo dos veces, y el día que alguien hiciera una sola
 * los dos caminos al modo oscuro darían resultados distintos.
 *
 * `light-dark()` resuelve las dos ramas con UNA declaración por token. El
 * conmutador manual no reescribe colores: sólo cambia `color-scheme`, y el
 * navegador elige la rama. Un token, una línea, dos temas.
 *
 * Requiere Chrome 123+, Safari 17.5+ o Firefox 120+ (todos de 2024). Es un
 * requisito consciente, no un descuido: la alternativa costaba duplicar la
 * paleta entera para siempre.
 *
 * └──────────────────────────────────────────────────────────────────────────┘
 */
:root {
  /*
   * El punto de partida del tema lo decide la INSTALACIÓN, no esta hoja.
   * `marca.css` declara `--marca-esquema` a partir del ajuste `marca.tema`;
   * si no lo declara —o si `marca.css` todavía no se generó— se sigue la
   * preferencia del navegador, que es el comportamiento sensato por defecto.
   *
   * Ojo con el orden de carga: `sensei.css` va DESPUÉS de `marca.css`, así que
   * escribir acá un `color-scheme` fijo pisaría la decisión del cliente. Por
   * eso se lee como variable y no se declara un valor.
   */
  color-scheme: var(--marca-esquema, light dark);

  /* ── Superficies ──────────────────────────────────────────────────────── */
  --s-fondo:        light-dark(var(--marca-neutro-100), var(--marca-neutro-900));
  --s-superficie:   light-dark(var(--marca-neutro-0),   var(--marca-neutro-800));
  /*
   * Los tonos intermedios del modo oscuro se DERIVAN de la rampa de marca con
   * `color-mix`, no se escriben a mano. Si fueran literales, un cliente que
   * cambia su rampa de neutros vería el tema claro adaptarse y el oscuro
   * quedarse con los grises del cliente anterior — y nadie relacionaría el
   * síntoma con el cambio de marca.
   */
  --s-superficie-2: light-dark(var(--marca-neutro-50),  color-mix(in srgb, var(--marca-neutro-800) 55%, var(--marca-neutro-900)));
  --s-relleno:      light-dark(var(--marca-neutro-100), color-mix(in srgb, var(--marca-neutro-700) 40%, var(--marca-neutro-800)));
  --s-borde:        light-dark(var(--marca-neutro-300), color-mix(in srgb, var(--marca-neutro-600) 45%, var(--marca-neutro-800)));
  --s-borde-suave:  light-dark(var(--marca-neutro-200), color-mix(in srgb, var(--marca-neutro-700) 40%, var(--marca-neutro-800)));

  /* ── Texto ────────────────────────────────────────────────────────────── */
  --s-tinta:     light-dark(var(--marca-neutro-700), var(--marca-neutro-50));
  --s-tinta-2:   light-dark(var(--marca-neutro-600), var(--marca-neutro-300));
  --s-tenue:     light-dark(var(--marca-neutro-500), var(--marca-neutro-400));
  --s-apagado:   light-dark(var(--marca-neutro-400), var(--marca-neutro-500));

  /* ── Acción ───────────────────────────────────────────────────────────── */
  /*
   * En modo oscuro el color de marca se usa tal cual sobre superficies
   * oscuras: aclararlo lo aleja de la identidad, y el contraste lo resuelve
   * el fondo, no el acento.
   */
  --s-accento:        var(--marca-primaria);

  /*
   * El acento cuando es TINTA, no superficie.
   *
   * ⚠️ `--s-accento` es el rojo pleno de la marca, elegido para sostener texto
   * blanco encima. Como color de texto sobre la superficie oscura quedaba en
   * 2,58:1 contra los 4,5:1 exigidos — enlaces, pestaña activa, cifras
   * destacadas: media plataforma.
   *
   * Y no se puede arreglar aclarando `--s-accento` a secas, que era la
   * tentación: ese token también pinta fondos de botón, y sobre un rojo claro
   * el blanco de `--s-sobre-solido` deja de leerse. Arreglar el texto rompería
   * los botones.
   *
   * Por eso son dos tokens y la regla es de una línea: el acento SOBRE una
   * superficie usa este; el acento COMO superficie usa el otro.
   */
  --s-accento-texto:  light-dark(var(--marca-primaria), var(--marca-primaria-claro));
  --s-accento-fuerte: var(--marca-primaria-oscura);
  --s-accento-suave:  light-dark(var(--marca-primaria-suave), color-mix(in srgb, var(--marca-primaria) 18%, transparent));
  --s-sobre-accento:  var(--marca-sobre-primaria);
  /*
   * El color que se lee sobre CUALQUIER superficie sólida de estado: el rojo
   * de peligro, el verde de éxito, el fondo oscuro de un toast. Sale del mismo
   * valor que el cliente ya eligió para su color de marca, así que no hay un
   * blanco fijo peleando contra un fondo tematizado — el defecto que deja
   * texto invisible en cuanto alguien cambia la paleta.
   */
  --s-sobre-solido:   var(--marca-sobre-primaria);

  /*
   * ── Estados ─────────────────────────────────────────────────────────────
   *
   * Cada estado tiene DOS colores, uno por tema. Los de la marca están
   * elegidos para leerse sobre blanco, y sobre la superficie oscura quedaban
   * entre 2,31:1 y 3,09:1 contra los 4,5:1 que exige el mínimo — el rojo de
   * error, que es justo el que más urge leer, era el peor de los cuatro.
   *
   * ⚠️ La variante clara la calcula `MarcaService` al compilar la marca, no se
   * escribe acá. Un valor a mano en esta hoja se despegaría de la paleta el día
   * que un cliente cambie su rojo desde la consola de parametrización, y el
   * contraste volvería a fallar en silencio.
   */
  --s-ok:            light-dark(var(--marca-ok), var(--marca-ok-claro));
  --s-ok-fondo:      light-dark(var(--marca-ok-suave),      color-mix(in srgb, var(--marca-ok) 20%, transparent));
  --s-aviso:         light-dark(var(--marca-aviso), var(--marca-aviso-claro));
  --s-aviso-fondo:   light-dark(var(--marca-aviso-suave),   color-mix(in srgb, var(--marca-aviso) 20%, transparent));
  --s-peligro:       light-dark(var(--marca-peligro), var(--marca-peligro-claro));
  --s-peligro-fondo: light-dark(var(--marca-peligro-suave), color-mix(in srgb, var(--marca-peligro) 20%, transparent));
  --s-info:          light-dark(var(--marca-info), var(--marca-info-claro));
  --s-info-fondo:    light-dark(var(--marca-info-suave),    color-mix(in srgb, var(--marca-info) 20%, transparent));

  /* ── Elevación ────────────────────────────────────────────────────────── */
  --s-sombra-sm: 0 1px 2px light-dark(rgb(0 0 0 / .06), rgb(0 0 0 / .4));
  --s-sombra-md: 0 4px 12px light-dark(rgb(0 0 0 / .08), rgb(0 0 0 / .5));
  --s-sombra-lg: 0 12px 32px light-dark(rgb(0 0 0 / .12), rgb(0 0 0 / .6));
  --s-velo:      light-dark(rgb(22 24 26 / .45), rgb(0 0 0 / .65));
  --s-halo:      0 0 0 3px color-mix(in srgb, var(--marca-primaria) 28%, transparent);

  /* ── Tipografía ───────────────────────────────────────────────────────── */
  /*
   * 16px es el PISO del cuerpo de texto, no una sugerencia. Por debajo, un
   * teléfono ofrece hacer zoom y eso rompe la disposición. El prototipo usa
   * tamaños de 8,5 a 13px en varios lugares; acá se suben a la escala, porque
   * una etiqueta que no se lee bajo el sol no comunica nada.
   */
  --s-fuente-titulo: var(--marca-fuente-titulo), 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --s-fuente-cuerpo: var(--marca-fuente-cuerpo), system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;

  --s-t-xs:  0.75rem;   /* 12px — sólo etiquetas, sólo con contraste alto */
  --s-t-sm:  0.875rem;  /* 14px — metadatos, texto secundario             */
  --s-t-md:  1rem;      /* 16px — CUERPO. Nunca menos                     */
  --s-t-lg:  1.125rem;  /* 18px                                           */
  --s-t-xl:  1.375rem;  /* 22px — título de tarjeta                       */
  --s-t-2xl: 1.75rem;   /* 28px — título de sección                       */
  --s-t-3xl: 2.25rem;   /* 36px — título de pantalla                      */

  --s-peso-normal: 400;
  --s-peso-medio:  500;
  --s-peso-fuerte: 600;
  --s-peso-negro:  700;

  --s-interlinea-ajustada: 1.2;
  --s-interlinea-normal:   1.5;
  --s-interlinea-suelta:   1.7;

  /* ── Espaciado ────────────────────────────────────────────────────────── */
  --s-e-1: 0.25rem;  /*  4px */
  --s-e-2: 0.5rem;   /*  8px */
  --s-e-3: 0.75rem;  /* 12px */
  --s-e-4: 1rem;     /* 16px */
  --s-e-5: 1.5rem;   /* 24px */
  --s-e-6: 2rem;     /* 32px */
  --s-e-7: 3rem;     /* 48px */
  --s-e-8: 4rem;     /* 64px */

  /* ── Radios ───────────────────────────────────────────────────────────── */
  --s-r-sm:    6px;
  --s-r-md:    10px;
  --s-r-lg:    16px;
  --s-r-plena: 999px;

  /* ── Movimiento ───────────────────────────────────────────────────────── */
  /* Faltaban por completo en la plataforma anterior: cada transición traía su
     propia duración escrita a mano, y el conjunto se sentía descoordinado. */
  --s-mov-rapido: 120ms;
  --s-mov-normal: 200ms;
  --s-mov-lento:  320ms;
  --s-curva:      cubic-bezier(.4, 0, .2, 1);

  /* ── Medidas de la estructura ─────────────────────────────────────────── */
  /* 92px es la medida del prototipo, y no es holgura: la barra lleva el
     descriptor en dos renglones al lado de un logotipo de 55, más el bloque de
     persona con nombre y cargo apilados. Con 76 el logotipo tocaba los bordes. */
  --s-barra-alto:      92px;
  --s-lateral-ancho:   264px;
  --s-lateral-mini:    72px;
  --s-contenido-max:   1320px;
  --s-lectura-max:     68ch;

  /* ── Objetivo táctil ──────────────────────────────────────────────────── */
  /* 44px no es estético: es el mínimo que se acierta con el pulgar. Un icono
     de 16px es un icono que se falla, y quien falla dos veces deja de intentar. */
  --s-tactil: 44px;
}

/* El conmutador manual no reescribe ni un color: sólo cambia qué rama de
   `light-dark()` resuelve el navegador. */
:root[data-theme='light'] { color-scheme: light; }
:root[data-theme='dark']  { color-scheme: dark;  }

/* ───────────────────────────────────────────────────────────────────────────
   2. REINICIO Y BASE
   ─────────────────────────────────────────────────────────────────────────── */

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

/*
 * ┌─ `[hidden]` tiene que GANAR ──────────────────────────────────────────────┐
 *
 * El navegador implementa `[hidden]` como `display: none` en su hoja de
 * usuario, que pierde contra cualquier regla de autor. Este sistema declara
 * `display: flex` en tarjetas, filtros y estados vacíos, así que
 * `elemento.hidden = true` desde JavaScript no ocultaba NADA.
 *
 * Se vio primero en el filtro de la malla: el cartel de «no encontramos nada»
 * quedaba visible sobre quince resultados. El caso grave era el otro —las
 * tarjetas descartadas por el filtro seguían en pantalla—, o sea un filtro que
 * dice cuántos encontró y no esconde a los demás.
 *
 * Es el único `!important` de la hoja y se lo gana: `[hidden]` no es una
 * sugerencia de estilo, es una declaración de que el elemento no está.
 *
 * └──────────────────────────────────────────────────────────────────────────┘
 */
[hidden] { display: none !important; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  min-height: 100svh;
  background: var(--s-fondo);
  color: var(--s-tinta);
  font-family: var(--s-fuente-cuerpo);
  font-size: var(--s-t-md);
  line-height: var(--s-interlinea-normal);
  -webkit-font-smoothing: antialiased;
}

img, svg, video { max-width: 100%; height: auto; display: block; }

a {
  color: var(--s-accento-texto);
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}
a:hover { color: var(--s-accento-fuerte); }

/* El foco SIEMPRE se ve. `outline: none` sin reemplazo deja a quien navega con
   teclado sin saber dónde está, que es como usar la plataforma a ciegas. */
:focus-visible {
  outline: 2px solid var(--s-accento);
  outline-offset: 2px;
  border-radius: var(--s-r-sm);
}

hr { border: 0; border-top: 1px solid var(--s-borde); margin: var(--s-e-5) 0; }

::selection { background: var(--s-accento-suave); color: var(--s-tinta); }

/* ───────────────────────────────────────────────────────────────────────────
   3. TIPOGRAFÍA
   ─────────────────────────────────────────────────────────────────────────── */

h1, h2, h3, h4 {
  font-family: var(--s-fuente-titulo);
  line-height: var(--s-interlinea-ajustada);
  color: var(--s-tinta);
  margin: 0 0 var(--s-e-3);
  text-wrap: balance;
}

h1 { font-size: var(--s-t-3xl); font-weight: var(--s-peso-negro);  letter-spacing: -.02em; }
h2 { font-size: var(--s-t-2xl); font-weight: var(--s-peso-fuerte); letter-spacing: -.01em; }
h3 { font-size: var(--s-t-xl);  font-weight: var(--s-peso-fuerte); }
h4 { font-size: var(--s-t-lg);  font-weight: var(--s-peso-medio);  }

p { margin: 0 0 var(--s-e-4); max-width: var(--s-lectura-max); }
p:last-child { margin-bottom: 0; }

.s-guia    { font-size: var(--s-t-lg); color: var(--s-tinta-2); }
.s-meta    { font-size: var(--s-t-sm); color: var(--s-tenue); }
.s-rotulo  { font-size: var(--s-t-xs); font-weight: var(--s-peso-fuerte);
             letter-spacing: .12em; text-transform: uppercase; color: var(--s-tenue); }
.s-numero  { font-family: var(--s-fuente-titulo); font-size: var(--s-t-3xl);
             font-weight: var(--s-peso-negro); line-height: 1; letter-spacing: -.03em;
             display: flex; align-items: baseline; gap: 2px; }
/* El denominador de «4 de 10» va más chico pero alineado por la BASE, no
   centrado: centrado se lee como un subíndice y el número deja de leerse como
   una fracción. */
.s-numero__total { font-size: var(--s-t-lg); font-weight: var(--s-peso-medio);
                   color: var(--s-tenue); letter-spacing: 0; }

/* ───────────────────────────────────────────────────────────────────────────
   4. DISPOSICIÓN
   ─────────────────────────────────────────────────────────────────────────── */

/*
 * ┌─ Móvil primero, y con los cortes nombrados ──────────────────────────────┐
 *
 * La plataforma anterior era escritorio primero: 24 consultas de medio, todas
 * `max-width`, con cinco cortes sin nombre (400, 680, 900, 960, 1180) que
 * nadie podía justificar. El resultado es que cada pantalla nueva inventaba el
 * suyo.
 *
 * Acá hay CUATRO cortes, nombrados, y siempre `min-width`. La base es el
 * teléfono, que es donde de verdad se usa un LMS corporativo: de pie, entre
 * tareas, en sesiones de tres minutos.
 *
 *     sm  480px   celular grande
 *     md  768px   tableta
 *     lg 1024px   escritorio        ← acá aparece la barra lateral fija
 *     xl 1280px   escritorio amplio
 *
 * └──────────────────────────────────────────────────────────────────────────┘
 */

.s-marco {
  display: grid;
  grid-template-rows: auto 1fr auto;
  min-height: 100svh;
}
/*
 * ┌─ `min-width: 0` en TODOS los elementos de la rejilla ─────────────────────┐
 *
 * Un elemento de rejilla trae `min-width: auto`, o sea que se niega a bajar de
 * su ancho mínimo de contenido. Basta una palabra larga, un logotipo o una
 * columna que no puede partirse para que el elemento sea más ancho que su
 * carril, y entonces la PÁGINA ENTERA se desplaza de costado.
 *
 * Pasó dos veces con el mismo síntoma y dos culpables distintos: primero la
 * barra superior y después el pie, que en un teléfono de 375 px dejaba el
 * documento en 415. Y se ve horrible: la página se corre cuando uno la toca,
 * la barra pegajosa queda a medias y no hay nada que indique por qué.
 *
 * Va sobre `> *` y no elemento por elemento justamente porque el próximo
 * culpable va a ser otro.
 *
 * └──────────────────────────────────────────────────────────────────────────┘
 */
.s-marco > * { min-width: 0; }

/* ── Barra superior ─────────────────────────────────────────────────────── */
/*
 * El prototipo aprobado la trae blanca sobre un fondo casi blanco. El criterio
 * general dice que una barra blanca sobre fondo claro no separa nada, y es
 * cierto — pero el diseño es del cliente. Se resuelve dándole el borde que el
 * blanco solo no da: la jerarquía la sostiene la línea, no el color.
 */
.s-barra {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  /*
   * `min-width: 0` porque la barra es un elemento de REJILLA dentro de
   * `.s-marco`, y un elemento de rejilla trae `min-width: auto` — o sea, se
   * niega a bajar de su ancho mínimo de contenido. En un teléfono de 375 px
   * eso la dejaba en 415 y toda la página se corría de costado.
   */
  min-width: 0;
  align-items: center;
  gap: clamp(8px, 1.6vw, 22px);
  height: var(--s-barra-alto);
  padding-inline: clamp(12px, 2.2vw, 32px);
  background: var(--s-superficie);
  border-bottom: 1px solid var(--s-borde);
}

.s-barra__marca {
  display: flex;
  align-items: center;
  gap: var(--s-e-4);
  flex: none;
  min-width: 0;
  text-decoration: none;
}
.s-barra__logo { height: 38px; width: auto; }

/*
 * El descriptor va en dos renglones a la izquierda del logotipo, separado por
 * una línea vertical. `max-width` en `ch` y no en píxeles porque la unidad
 * tiene que seguir al tamaño de fuente: con `px`, quien sube el tamaño del
 * texto del navegador se queda con «UNIVERSIDA» cortado.
 */
.s-barra__descriptor {
  display: none;
  flex: none;
  font-family: var(--s-fuente-titulo);
  font-size: var(--s-t-xs);
  font-weight: var(--s-peso-medio);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--s-tenue);
  line-height: 1.45;
  max-width: 11ch;
}
.s-barra__divisor {
  display: none;
  flex: none;
  width: 1px;
  height: 44px;
  background: var(--s-borde);
}

/*
 * La navegación central. Se esconde antes que nada porque cada uno de sus
 * destinos está también en la lateral: es la única parte de la barra cuya
 * ausencia no deja a nadie sin camino.
 *
 * `flex: 0 1 auto` y no `1 1 auto`: con `1` reclamaba el espacio sobrante y
 * después no le alcanzaba para sus propios enlaces, que van con
 * `white-space: nowrap` y se desbordaban por debajo del logotipo. Pide lo que
 * mide y cede lo que le sobra; el `overflow` es la red por si aun así no entra.
 */
.s-barra__nav {
  display: none;
  flex: 0 0 auto;
  overflow: hidden;
  align-items: center;
  /*
   * `safe center` y no `center` a secas. Con `center`, un contenido más ancho
   * que su caja se desborda por LOS DOS lados y el desborde de la izquierda
   * queda fuera del área recortable: los enlaces se metían por debajo del
   * logotipo y ningún `overflow: hidden` los tapaba. `safe` dice «centra
   * mientras entre; si no entra, alinea al comienzo», que es lo que hace que
   * el recorte funcione.
   */
  justify-content: safe center;
  gap: clamp(10px, 1.4vw, 26px);
  margin-inline: auto;
}
.s-barra__nav-enlace {
  padding: var(--s-e-2) 2px;
  border-bottom: 2px solid transparent;
  color: var(--s-tinta-2);
  font-size: var(--s-t-sm);
  text-decoration: none;
  white-space: nowrap;
  transition: color var(--s-mov-rapido) var(--s-curva),
              border-color var(--s-mov-rapido) var(--s-curva);
}
.s-barra__nav-enlace:hover { color: var(--s-accento); }
.s-barra__nav-enlace[aria-current='page'] {
  color: var(--s-accento-texto);
  border-bottom-color: var(--s-accento-texto);
}

.s-barra__buscador {
  display: none;
  align-items: center;
  gap: var(--s-e-2);
  flex: 0 1 auto;
  /*
   * Un PISO, y es lo que faltaba.
   *
   * Con `min-width: 0` el buscador era el único elemento de la barra que podía
   * encogerse —los otros tres van con `flex: none`—, así que absorbía él solo
   * todo lo que sobrara. A 1440 quedaba en 82px: un recuadro con la lupa y dos
   * letras del marcador, que no parece un buscador sino un defecto.
   *
   * Y no se arregla una vez: cada icono nuevo en la barra le comía otros
   * cuarenta píxeles en silencio, sin romper nada que se pudiera ver en una
   * prueba. El piso convierte ese desbordamiento invisible en uno visible.
   */
  min-width: 200px;
  width: clamp(200px, 22vw, 300px);
  height: 40px;
  padding-inline: var(--s-e-3);
  border: 1px solid var(--s-borde);
  border-radius: var(--s-r-md);
  background: var(--s-fondo);
  /*
   * ⚠️ `--s-tinta-2` y no `--s-tenue`. Acá no se pinta una etiqueta de apoyo:
   * se pinta lo que la persona ESCRIBE y el marcador que le dice qué escribir.
   * Con `--s-tenue` sobre este fondo el par daba 4,34:1, por debajo del mínimo,
   * y en un buscador eso significa no poder releer lo que uno acaba de tipear.
   */
  color: var(--s-tinta-2);
}
.s-barra__buscador:focus-within { border-color: var(--s-accento); box-shadow: var(--s-halo); }
.s-barra__buscador input {
  flex: 1;
  min-width: 0;
  border: none;
  background: transparent;
  outline: none;
  font: inherit;
  font-size: var(--s-t-sm);
  color: var(--s-tinta);
}

.s-barra__acciones {
  display: flex;
  align-items: center;
  gap: var(--s-e-1);
  margin-inline-start: auto;
  flex: none;
}
.s-barra__solo-ancho { display: none; }

.s-icono { flex: none; width: 20px; height: 20px; }

/* ── Menús desplegables de la barra ─────────────────────────────────────── */
/*
 * `<details>` trae el estado, el teclado y el anuncio al lector de pantalla de
 * fábrica. Lo único que agrega `sensei.js` es cerrar al tocar afuera y con
 * Escape — doce líneas para los tres menús juntos.
 */
/*
 * Un menú CERRADO no se dispone. `<details>` oculta su contenido, pero el
 * panel está posicionado en absoluto y en esa situación sigue participando del
 * ancho desplazable de la página: 262 px anclados al borde derecho de una
 * pantalla de 375 son 262 px de desplazamiento horizontal por un menú que
 * nadie abrió.
 */
.s-menu { position: relative; flex: none; }
.s-menu:not([open]) .s-menu__panel { display: none; }
.s-menu__boton {
  display: flex;
  align-items: center;
  gap: var(--s-e-2);
  min-height: var(--s-tactil);
  padding: 0 var(--s-e-2);
  border-radius: var(--s-r-sm);
  color: var(--s-tinta-2);
  cursor: pointer;
  list-style: none;
  user-select: none;
}
/* El triángulo por defecto de `<summary>` en WebKit no se quita con
   `list-style` a secas. */
.s-menu__boton::-webkit-details-marker { display: none; }
.s-menu__boton:hover { background: var(--s-relleno); }
.s-menu__etiqueta { font-size: var(--s-t-xs); font-variant-numeric: tabular-nums; }
.s-menu__flecha { font-size: .625rem; color: var(--s-tenue); }
.s-menu--cuenta .s-menu__boton { padding-inline-start: var(--s-e-3); }

.s-menu__boton--icono { position: relative; }
.s-menu__burbuja {
  position: absolute;
  top: 4px;
  inset-inline-end: 0;
  min-width: 17px;
  height: 17px;
  padding-inline: 4px;
  border-radius: var(--s-r-sm);
  background: var(--s-accento);
  color: var(--s-sobre-accento);
  font-size: .625rem;
  font-weight: var(--s-peso-fuerte);
  line-height: 17px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.s-menu__persona { display: none; flex-direction: column; gap: 2px; min-width: 0; }
.s-menu__persona-nombre {
  font-size: var(--s-t-xs);
  font-weight: var(--s-peso-medio);
  color: var(--s-tinta);
  white-space: nowrap;
}
.s-menu__persona-cargo {
  font-size: .6875rem;
  color: var(--s-tenue);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 22ch;
}

.s-menu__panel {
  position: absolute;
  top: calc(100% + 6px);
  inset-inline-end: 0;
  z-index: 60;
  width: 262px;
  max-width: calc(100vw - 24px);
  padding: var(--s-e-2);
  border: 1px solid var(--s-borde);
  border-radius: var(--s-r-md);
  background: var(--s-superficie);
  box-shadow: var(--s-sombra-lg);
}
.s-menu__panel--angosto { width: 190px; }
.s-menu__panel--ancho   { width: 352px; padding: 0; overflow: hidden; }

.s-menu__titulo {
  margin: 0;
  padding: var(--s-e-2) var(--s-e-3) var(--s-e-1);
  font-size: .6875rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--s-tenue);
}
.s-menu__opcion {
  display: flex;
  align-items: center;
  gap: var(--s-e-3);
  min-height: var(--s-tactil);
  padding: var(--s-e-2) var(--s-e-3);
  border-radius: var(--s-r-sm);
  color: var(--s-tinta);
  font-size: var(--s-t-sm);
  text-decoration: none;
}
.s-menu__opcion:hover { background: var(--s-relleno); color: var(--s-tinta); }
.s-menu__opcion .s-icono { width: 17px; height: 17px; color: var(--s-tenue); }
.s-menu__opcion[aria-current='true'] { background: var(--s-accento-suave); color: var(--s-accento-fuerte); }
.s-menu__opcion--salir {
  margin-top: var(--s-e-1);
  border-top: 1px solid var(--s-borde-suave);
  border-radius: 0 0 var(--s-r-sm) var(--s-r-sm);
  color: var(--s-accento-texto);
}
.s-menu__opcion--salir .s-icono { color: currentColor; }

.s-menu__persona-caja {
  display: flex;
  align-items: center;
  gap: var(--s-e-3);
  padding: var(--s-e-3);
  margin-bottom: var(--s-e-1);
  border-bottom: 1px solid var(--s-borde-suave);
}
.s-menu__persona-caja .s-menu__persona { display: flex; }

/* ── Panel de notificaciones ────────────────────────────────────────────── */
.s-menu__cabecera {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-e-3);
  padding: var(--s-e-3) var(--s-e-4);
  border-bottom: 1px solid var(--s-borde-suave);
}
.s-menu__cabecera-titulo { font-size: var(--s-t-sm); font-weight: var(--s-peso-fuerte); }
.s-menu__lista { max-height: 330px; overflow-y: auto; overscroll-behavior: contain; }
.s-menu__vacio { margin: 0; padding: var(--s-e-5) var(--s-e-4); font-size: var(--s-t-sm); color: var(--s-tenue); }
.s-menu__pie {
  display: block;
  padding: var(--s-e-3);
  border-top: 1px solid var(--s-borde-suave);
  color: var(--s-accento-texto);
  font-size: var(--s-t-sm);
  text-align: center;
  text-decoration: none;
}
.s-menu__pie:hover { background: var(--s-relleno); }

.s-notif {
  display: flex;
  align-items: flex-start;
  gap: var(--s-e-3);
  width: 100%;
  padding: var(--s-e-3) var(--s-e-4);
  border: none;
  border-bottom: 1px solid var(--s-borde-suave);
  background: none;
  font: inherit;
  text-align: start;
  cursor: pointer;
}
.s-notif:hover { background: var(--s-relleno); }
.s-notif__cuerpo { display: flex; flex-direction: column; gap: var(--s-e-1); flex: 1; min-width: 0; }
.s-notif__titulo { font-size: var(--s-t-sm); font-weight: var(--s-peso-medio); color: var(--s-tinta); }
.s-notif__texto  { font-size: var(--s-t-xs); color: var(--s-tinta-2); text-wrap: pretty; }
.s-notif__fecha  { font-size: .6875rem; color: var(--s-tenue); }
/* El punto no es la única señal de «sin leer»: la fila tiene además el fondo
   tenue. Sólo con el punto, quien no distingue el color de marca no ve nada. */
[data-propia='nueva'] .s-notif { background: var(--s-accento-suave); }
.s-notif__punto {
  flex: none;
  width: 8px;
  height: 8px;
  margin-top: 6px;
  border-radius: 50%;
  background: var(--s-accento);
}

.s-enlace-boton {
  border: none;
  background: none;
  padding: 0;
  font: inherit;
  font-size: var(--s-t-xs);
  color: var(--s-accento-texto);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* ── Retratos ───────────────────────────────────────────────────────────── */
/*
 * El lado llega por variable desde el ayudante `avatar()`, no por una clase
 * por tamaño. Con clases habría `--sm`, `--md`, `--lg` y el día que una
 * pantalla necesite 52 px habría que inventar `--between`.
 */
.s-retrato {
  display: grid;
  place-items: center;
  flex: none;
  width: var(--s-retrato-lado, 40px);
  height: var(--s-retrato-lado, 40px);
  border-radius: 50%;
  overflow: hidden;
  background: var(--s-relleno);
  color: var(--s-tinta-2);
  font-size: calc(var(--s-retrato-lado, 40px) * .36);
  font-weight: var(--s-peso-fuerte);
  line-height: 1;
  text-transform: uppercase;
}
.s-retrato picture, .s-retrato img { width: 100%; height: 100%; }
.s-retrato img { object-fit: cover; }
.s-retrato--acento { box-shadow: 0 0 0 2px var(--s-accento); }

/* ── Barra lateral ──────────────────────────────────────────────────────── */
/*
 * En teléfono es un cajón que entra desde el costado; en escritorio es una
 * columna fija. Es el MISMO marcado: sólo cambia la posición. Dos marcados
 * distintos serían dos navegaciones que hay que acordarse de mantener iguales.
 */
.s-lateral {
  position: fixed;
  inset-block: 0;
  inset-inline-start: 0;
  z-index: 60;
  display: flex;
  flex-direction: column;
  gap: 5px;
  width: min(var(--s-lateral-ancho), 84vw);
  /*
   * En el cajón, `inset-block: 0` ya lo estira de arriba abajo. El alto va
   * declarado igual porque `.s-nav--scroll` necesita un contenedor con alto
   * RESUELTO para saber cuánto puede crecer antes de desplazarse; con el alto
   * implícito del `inset`, Safari se lo pasa como `auto` y la lista vuelve a
   * empujar.
   */
  height: 100%;
  padding: var(--s-e-4) var(--s-e-3);
  background: var(--s-superficie);
  border-inline-end: 1px solid var(--s-borde);
  /*
   * `hidden` y no `auto`: acá adentro el que se desplaza es `.s-nav--scroll`.
   * Con los dos en `auto` aparecen dos barras de desplazamiento anidadas y el
   * gesto cae en una o en otra según dónde esté el puntero.
   */
  overflow: hidden;
  transform: translateX(-100%);
  transition: transform var(--s-mov-normal) var(--s-curva);
}
.s-lateral[data-abierto='true'] { transform: translateX(0); }

.s-lateral__velo {
  position: fixed;
  inset: 0;
  z-index: 50;
  background: var(--s-velo);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--s-mov-normal) var(--s-curva);
}
.s-lateral__velo[data-abierto='true'] { opacity: 1; pointer-events: auto; }

/* El plegado sólo tiene sentido donde la lateral es columna. En el cajón, el
   botón que lo cierra ya es el velo y la tecla Escape. */
.s-lateral__plegar { display: none; }

.s-nav { display: flex; flex-direction: column; gap: 2px; list-style: none; margin: 0; padding: 0; }

/*
 * ┌─ Sólo la LISTA se desplaza, no la columna entera ──────────────────────────┐
 *
 * La lateral mide `100svh` menos la barra y lleva catorce destinos. En un
 * portátil no entran, y como el desplazamiento era de la columna completa, al
 * bajar para llegar al último destino se iba «Ocultar menú» por arriba y
 * «Cerrar sesión» quedaba fuera por abajo. Se veía como un menú cortado, y era
 * exactamente eso.
 *
 * Ahora los dos extremos quedan quietos y el medio corre entre ellos.
 *
 * ⚠️ `min-height: 0` no es opcional y es la mitad del arreglo. Un hijo de
 * flexbox trae `min-height: auto`, o sea que se NIEGA a achicarse por debajo
 * de su contenido: sin esta línea la lista empuja igual y `overflow-y: auto`
 * no llega a activarse nunca. Es el defecto de flexbox que hace que la gente
 * concluya que «overflow no funciona».
 *
 * `overscroll-behavior` para que al llegar al final de la lista el gesto no se
 * contagie a la página de atrás.
 *
 * └───────────────────────────────────────────────────────────────────────────┘
 */
.s-nav--scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.s-nav__enlace {
  display: flex;
  align-items: center;
  gap: var(--s-e-3);
  min-height: var(--s-tactil);
  padding: var(--s-e-2) var(--s-e-3);
  border-radius: var(--s-r-md);
  color: var(--s-tinta-2);
  font-size: var(--s-t-sm);
  font-weight: var(--s-peso-medio);
  text-decoration: none;
  white-space: nowrap;
  transition: background var(--s-mov-rapido) var(--s-curva);
}
.s-nav__enlace:hover { background: var(--s-relleno); color: var(--s-tinta); }

/*
 * El destino activo se marca de DOS formas: relleno sólido y peso de la letra.
 * Sólo con color falla para quien no lo distingue, y es la señal que dice
 * «estás acá» — la más consultada de toda la navegación.
 */
.s-nav__enlace[aria-current='page'] {
  background: var(--s-accento);
  color: var(--s-sobre-accento);
  font-weight: var(--s-peso-fuerte);
}
.s-nav__enlace--salir {
  margin-top: auto;
  background: var(--s-fondo);
  color: var(--s-accento-texto);
}
.s-nav__enlace--salir:hover { background: var(--s-accento-suave); color: var(--s-accento-fuerte); }

/* ── Contenido ──────────────────────────────────────────────────────────── */
.s-principal { min-width: 0; display: flex; flex-direction: column; }
.s-principal__cuerpo { flex: 1; padding: var(--s-e-4); }
.s-contenedor { width: 100%; max-width: var(--s-contenido-max); margin-inline: auto; }

/* ── Banda de encabezado ────────────────────────────────────────────────── */
/*
 * Va a SANGRE dentro del área principal y con fondo propio. Es lo que separa
 * «dónde estoy» de «qué hay acá», y con el mismo fondo que el contenido esa
 * separación desaparece justo en la pantalla donde más se necesita: la que se
 * abre desde un enlace y no desde la navegación.
 */
.s-banda {
  background: var(--s-relleno);
  border-bottom: 1px solid var(--s-borde);
  padding: var(--s-e-4) var(--s-e-4) var(--s-e-5);
}
.s-banda__fila {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s-e-4);
  margin-top: var(--s-e-2);
}
.s-banda__titulo {
  margin: 0;
  font-family: var(--s-fuente-titulo);
  font-size: var(--s-t-2xl);
  font-weight: var(--s-peso-fuerte);
  letter-spacing: -.4px;
  line-height: var(--s-interlinea-ajustada);
}
.s-banda__bajada { margin: var(--s-e-2) 0 0; font-size: var(--s-t-sm); color: var(--s-tinta-2); }

.s-migas { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-e-2);
           font-size: var(--s-t-xs); color: var(--s-tenue); }
.s-migas a { color: var(--s-tinta-2); text-decoration: none; }
.s-migas a:hover { color: var(--s-accento); text-decoration: underline; }
.s-migas__sep { color: var(--s-apagado); }

/* ── Pie ────────────────────────────────────────────────────────────────── */
/*
 * Oscuro en los dos temas, como lo trae el prototipo. El color sale del
 * extremo oscuro de la rampa de neutros del cliente, no de un `#000` literal:
 * quien cambie su rampa a una cálida verá el pie acompañarla en vez de un
 * rectángulo azulado peleando con el resto.
 */
.s-pie {
  --s-pie-fondo: var(--marca-neutro-800);
  --s-pie-tinta: var(--marca-neutro-0);

  padding: var(--s-e-6) var(--s-e-4) 0;
  background: var(--s-pie-fondo);
  color: color-mix(in srgb, var(--s-pie-tinta) 62%, transparent);
  font-size: var(--s-t-xs);
}
.s-pie__cuerpo {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(210px, 100%), 1fr));
  gap: var(--s-e-6);
  width: 100%;
  max-width: var(--s-contenido-max);
  margin-inline: auto;
  padding-bottom: var(--s-e-5);
  border-bottom: 1px solid color-mix(in srgb, var(--s-pie-tinta) 12%, transparent);
}
.s-pie__marca { display: flex; flex-direction: column; gap: var(--s-e-4); }
.s-pie__logo  { height: 48px; width: auto; align-self: flex-start; }
.s-pie__descripcion { margin: 0; max-width: 34ch; line-height: var(--s-interlinea-suelta); text-wrap: pretty; }

.s-pie__columna { display: flex; flex-direction: column; gap: var(--s-e-3); }
.s-pie__columna-titulo {
  margin: 0;
  font-size: .6875rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--s-accento-texto);
}
.s-pie__columna a { color: inherit; text-decoration: none; }
.s-pie__columna a:hover { color: var(--s-pie-tinta); text-decoration: underline; }

.s-pie__legal {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-e-4);
  width: 100%;
  max-width: var(--s-contenido-max);
  margin-inline: auto;
  padding: var(--s-e-4) 0 var(--s-e-5);
  color: color-mix(in srgb, var(--s-pie-tinta) 45%, transparent);
}
.s-pie__legal a { color: inherit; }
.s-pie__version { font-variant-numeric: tabular-nums; display: flex; gap: var(--s-e-2); }

/* ── Rejillas ───────────────────────────────────────────────────────────── */
/*
 * `minmax` con repetición automática en vez de un número fijo de columnas por
 * corte: la rejilla se acomoda sola y no hay que declarar el mismo bloque
 * cuatro veces.
 *
 * ┌─ `auto-fill` y no `auto-fit`, y la diferencia se ve con UN elemento ──────┐
 *
 * Los dos se comportan igual mientras haya elementos suficientes para llenar
 * la fila. Cuando sobran columnas, `auto-fit` las COLAPSA y reparte el ancho
 * entre los elementos que hay: un único curso en progreso pasaba a medir 1.286
 * píxeles, con su foto de 180 px de alto estirada de lado a lado. `auto-fill`
 * las conserva vacías y ese curso queda del ancho de una tarjeta, que es lo
 * que el prototipo muestra y lo único que se ve bien.
 *
 * └──────────────────────────────────────────────────────────────────────────┘
 */
.s-rejilla       { display: grid; gap: var(--s-e-4); }
.s-rejilla--auto { grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr)); }
.s-rejilla--ancha{ grid-template-columns: repeat(auto-fill, minmax(min(320px, 100%), 1fr)); }
.s-rejilla--kpi  { grid-template-columns: repeat(auto-fill, minmax(min(200px, 100%), 1fr)); }

.s-pila { display: flex; flex-direction: column; gap: var(--s-e-4); }
.s-fila { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-e-3); }

/*
 * ┌─ Teléfono angosto: la barra tiene que ENTRAR ─────────────────────────────┐
 *
 * A 375 px medía 415: cuarenta píxeles de desborde horizontal, que en un
 * teléfono no se ven como un error sino como una página que se corre de
 * costado cuando uno la toca.
 *
 * La cuenta era: 24 de relleno + 44 del botón de menú + 144 del logotipo + 144
 * de las acciones + 24 de separaciones = 380. Con el logotipo a 30 px y las
 * separaciones al mínimo entra con margen, y el logotipo sigue siendo legible
 * porque la marca es una palabra corta.
 *
 * No se resuelve con `overflow-x: hidden` en el `body`: eso tapa el síntoma y
 * de paso rompe cualquier elemento pegajoso de la página.
 *
 * └──────────────────────────────────────────────────────────────────────────┘
 */
@media (max-width: 479px) {
  .s-barra       { gap: var(--s-e-1); padding-inline: var(--s-e-2); }
  .s-barra__logo { height: 30px; }
  .s-barra__marca { min-width: 0; overflow: hidden; }
  .s-menu--cuenta .s-menu__boton { padding-inline: var(--s-e-1); }
  .s-menu__flecha { display: none; }
}

@media (min-width: 480px) {
  .s-principal__cuerpo { padding: var(--s-e-5); }
  .s-banda             { padding-inline: var(--s-e-5); }
  .s-barra__logo       { height: 44px; }
}

@media (min-width: 768px) {
  .s-barra             { padding-inline: var(--s-e-5); }
  .s-barra__buscador   { display: flex; }
  .s-barra__descriptor { display: block; }
  .s-barra__divisor    { display: block; }
  .s-barra__logo       { height: 50px; }
}

@media (min-width: 900px) {
  .s-menu__persona { display: flex; }
  .s-menu--cuenta .s-menu__boton {
    margin-inline-start: var(--s-e-1);
    border-inline-start: 1px solid var(--s-borde);
  }
}

@media (min-width: 1024px) {
  /* Acá la lateral deja de ser cajón y pasa a ser columna. */
  .s-marco {
    grid-template-columns: var(--s-lateral-ancho) 1fr;
    grid-template-areas:
      'barra   barra'
      'lateral principal'
      'pie     pie';
    grid-template-rows: auto 1fr auto;
  }
  .s-barra     { grid-area: barra; }
  .s-lateral   { grid-area: lateral; position: sticky; top: var(--s-barra-alto);
                 height: calc(100svh - var(--s-barra-alto)); transform: none; width: auto;
                 inset: auto; z-index: 1; align-self: flex-start; }
  .s-principal { grid-area: principal; }
  .s-principal__cuerpo { padding: var(--s-e-5) var(--s-e-6) var(--s-e-7); }
  .s-banda     { padding: var(--s-e-5) var(--s-e-6); }
  .s-pie       { grid-area: pie; padding-inline: var(--s-e-6); }
  .s-lateral__velo { display: none; }

  /*
   * ┌─ El botón de menú de la barra desaparece en escritorio ─────────────────┐
   *
   * A partir de acá la lateral deja de ser un cajón y pasa a ser una columna
   * fija con su propio «Ocultar menú». Dos controles para lo mismo, uno al
   * lado del otro, obligan a decidir cuál hace qué — y hacen cosas distintas:
   * el de la barra ABRE el cajón, el de la lateral la PLIEGA a iconos.
   *
   * `.s-barra .s-barra__menu` y no `.s-barra__menu` a secas, y ese es el
   * arreglo de verdad: la regla ya existía y no servía. `.s-boton-icono`
   * declara `display: inline-flex` doscientas líneas más abajo, con la MISMA
   * especificidad, así que ganaba por ser la última. El botón se veía en
   * escritorio desde siempre, y nadie lo noto porque parecía a propósito.
   *
   * └────────────────────────────────────────────────────────────────────────┘
   */
  .s-barra .s-barra__menu { display: none; }
  .s-lateral__plegar {
    display: flex;
    align-items: center;
    gap: var(--s-e-3);
    min-height: var(--s-tactil);
    padding: var(--s-e-2) var(--s-e-3);
    border: none;
    border-radius: var(--s-r-md);
    background: none;
    color: var(--s-tenue);
    font: inherit;
    font-size: var(--s-t-sm);
    text-align: start;
    cursor: pointer;
  }
  .s-lateral__plegar:hover { background: var(--s-relleno); color: var(--s-tinta); }
  .s-barra__logo { height: 55px; }
}

/*
 * Los dos cortes de la barra, y por qué están donde están.
 *
 * A 1280 px la suma medida era: 64 de relleno + 327 de marca + 340 de
 * navegación + 160 de buscador + 412 de acciones = 1303. Trece píxeles de
 * más, y el síntoma no era una barra apretada sino los enlaces del centro
 * saliéndose por debajo del logotipo. Los números salen de medir, no de
 * elegir un número redondo.
 */
@media (min-width: 1120px) {
  .s-barra__solo-ancho { display: flex; }
}

/*
 * La navegación central aparece recién acá, y no a 1400.
 *
 * A 1400 entraba, pero entraba a costa del buscador: cuatro destinos ocupan
 * unos 400px y la barra ya lleva el logotipo con su descriptor, cinco iconos y
 * el bloque de persona con nombre y cargo. Algo tenía que ceder, y cedía el
 * único que podía.
 *
 * Se sacrifica la navegación central porque es la que DUPLICA: sus cuatro
 * destinos están todos en la lateral, que está siempre. El buscador no está en
 * ningún otro lado.
 */
@media (min-width: 1560px) {
  .s-barra__nav { display: flex; }
}

/*
 * ── Lateral plegada ────────────────────────────────────────────────────────
 *
 * El estado vive en `localStorage` y se estampa en `<html>` ANTES del primer
 * pintado. Guardado en el servidor sería una preferencia de la PERSONA, y esto
 * es una preferencia del DISPOSITIVO: quien pliega el menú en el monitor de la
 * oficina no quiere encontrárselo plegado en la tablet de la sucursal.
 *
 * Las etiquetas se ocultan con `clip-path` y ancho cero en vez de
 * `display:none` para que la transición tenga algo que animar. Con
 * `display:none` el menú salta de 264 a 72 px de golpe.
 */
@media (min-width: 1024px) {
  :root[data-lateral='plegada'] .s-marco { grid-template-columns: var(--s-lateral-mini) 1fr; }
  :root[data-lateral='plegada'] .s-lateral__texto {
    width: 0;
    opacity: 0;
    overflow: hidden;
    clip-path: inset(50%);
  }
  :root[data-lateral='plegada'] .s-nav__enlace,
  :root[data-lateral='plegada'] .s-lateral__plegar { justify-content: center; gap: 0; }
  .s-lateral__texto {
    transition: opacity var(--s-mov-rapido) var(--s-curva), width var(--s-mov-normal) var(--s-curva);
  }
  .s-marco { transition: grid-template-columns var(--s-mov-normal) var(--s-curva); }
}

/* ───────────────────────────────────────────────────────────────────────────
   5. COMPONENTES
   ─────────────────────────────────────────────────────────────────────────── */

/* ── Botón ──────────────────────────────────────────────────────────────── */
.s-boton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-e-2);
  min-height: var(--s-tactil);
  padding: var(--s-e-2) var(--s-e-5);
  border: 1px solid transparent;
  border-radius: var(--s-r-md);
  background: var(--s-relleno);
  color: var(--s-tinta);
  font-family: inherit;
  font-size: var(--s-t-md);
  font-weight: var(--s-peso-fuerte);
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--s-mov-rapido) var(--s-curva),
              border-color var(--s-mov-rapido) var(--s-curva),
              transform var(--s-mov-rapido) var(--s-curva);
}
.s-boton:active { transform: translateY(1px); }
.s-boton[disabled], .s-boton[aria-disabled='true'] {
  opacity: .55; cursor: not-allowed; transform: none;
}

.s-boton--primario  { background: var(--s-accento); color: var(--s-sobre-accento); }
.s-boton--primario:hover:not([disabled]) { background: var(--s-accento-fuerte); color: var(--s-sobre-accento); }

.s-boton--contorno  { background: transparent; border-color: var(--s-borde); color: var(--s-tinta); }
.s-boton--contorno:hover:not([disabled]) { border-color: var(--s-accento); color: var(--s-accento); }

.s-boton--fantasma  { background: transparent; color: var(--s-tinta-2); }
.s-boton--fantasma:hover:not([disabled]) { background: var(--s-relleno); color: var(--s-tinta); }

.s-boton--peligro   { background: var(--s-peligro); color: var(--s-sobre-solido); }

.s-boton--bloque    { width: 100%; }
.s-boton--sm        { min-height: 36px; padding: var(--s-e-1) var(--s-e-3); font-size: var(--s-t-sm); }

/*
 * Botón que es sólo un icono. Lleva `aria-label` SIEMPRE — un botón sin texto
 * accesible es un botón que un lector de pantalla anuncia como «botón», y con
 * seis de esos en la barra superior la navegación se vuelve adivinanza.
 */
.s-boton-icono {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--s-tactil);
  height: var(--s-tactil);
  border: 0;
  border-radius: var(--s-r-md);
  background: transparent;
  color: var(--s-tinta-2);
  cursor: pointer;
  position: relative;
  transition: background var(--s-mov-rapido) var(--s-curva);
}
.s-boton-icono:hover { background: var(--s-relleno); color: var(--s-tinta); }

/* El contador de avisos SÓLO existe si hay avisos. Un `0` permanente entrena
   a la persona a ignorar la campana, que es lo contrario de lo que se busca. */
.s-boton-icono__contador {
  position: absolute;
  inset-block-start: 6px;
  inset-inline-end: 6px;
  min-width: 18px;
  height: 18px;
  padding-inline: 5px;
  border-radius: var(--s-r-plena);
  background: var(--s-peligro);
  color: var(--s-sobre-solido);
  font-size: 11px;
  font-weight: var(--s-peso-negro);
  line-height: 18px;
  text-align: center;
}

/* ── Tarjeta ────────────────────────────────────────────────────────────── */
.s-tarjeta {
  display: flex;
  flex-direction: column;
  background: var(--s-superficie);
  border: 1px solid var(--s-borde);
  border-radius: var(--s-r-lg);
  padding: var(--s-e-5);
}
.s-tarjeta--enlace { text-decoration: none; color: inherit;
  transition: border-color var(--s-mov-normal) var(--s-curva),
              box-shadow var(--s-mov-normal) var(--s-curva),
              transform var(--s-mov-normal) var(--s-curva); }
.s-tarjeta--enlace:hover { border-color: var(--s-accento); box-shadow: var(--s-sombra-md); transform: translateY(-2px); }
.s-tarjeta--plana  { border-radius: var(--s-r-md); padding: var(--s-e-4); }
.s-tarjeta__medio  { margin: calc(var(--s-e-5) * -1) calc(var(--s-e-5) * -1) var(--s-e-4);
                     border-radius: var(--s-r-lg) var(--s-r-lg) 0 0; overflow: hidden; aspect-ratio: 16 / 9; }
.s-tarjeta__medio img { width: 100%; height: 100%; object-fit: cover; }
.s-tarjeta__pie    { margin-top: auto; padding-top: var(--s-e-4); }
/*
 * Tarjeta cuyo contenido son FILAS de borde a borde: la lista de
 * notificaciones, y cualquier lista futura con el mismo aspecto. Sin relleno
 * propio, porque el relleno lo pone cada fila — si lo pusiera la tarjeta, el
 * resaltado al pasar por encima dejaría un marco blanco alrededor.
 */
.s-tarjeta--lista { padding: 0; overflow: hidden; }
.s-tarjeta--lista > *:last-child .s-notif { border-bottom: none; }

/* ── Tarjeta de curso ───────────────────────────────────────────────────── */
/*
 * Es su propio componente y no una variante de `.s-tarjeta` porque la anatomía
 * es distinta: la foto va a sangre contra los bordes y el relleno lo pone el
 * cuerpo, no la tarjeta. Con `.s-tarjeta--*` habría que anular el relleno de la
 * tarjeta con márgenes negativos, que es exactamente lo que hacía
 * `.s-tarjeta__medio` y lo que rompe en cuanto el radio cambia.
 */
.s-curso {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--s-superficie);
  border: 1px solid var(--s-borde-suave);
  border-radius: var(--s-r-lg);
  color: inherit;
  text-decoration: none;
  transition: border-color var(--s-mov-normal) var(--s-curva),
              box-shadow var(--s-mov-normal) var(--s-curva),
              transform var(--s-mov-normal) var(--s-curva);
}
/*
 * El resaltado lo dispara `:has()` sobre el enlace del título y no `a.s-curso`:
 * la tarjeta dejó de ser un `<a>` cuando entró el corazón —un botón dentro de
 * un enlace es marcado inválido— y ahora es un `<article>` con enlace
 * extendido.
 *
 * `:has()` pide Chrome 105+, Safari 15.4+ y Firefox 121+. Es un requisito
 * consciente, del mismo orden que `light-dark()`, que esta hoja ya exige.
 */
.s-curso { position: relative; }
.s-curso:has(.s-curso__enlace:hover),
.s-curso:has(.s-curso__enlace:focus-visible) {
  border-color: var(--s-accento-texto);
  box-shadow: var(--s-sombra-md);
  transform: translateY(-2px);
}

/*
 * El enlace extendido: el título es el enlace real —lo que un lector de
 * pantalla anuncia— y su pseudoelemento se estira sobre toda la tarjeta para
 * que el área de clic no se achique.
 *
 * El corazón lleva `z-index: 2` y queda por ENCIMA de esta capa. Sin eso, el
 * enlace se traga el toque y marcar un favorito navegaría al curso.
 */
.s-curso__enlace--extendido::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
}
/* El foco se dibuja sobre la tarjeta entera, no sobre las palabras del título:
   es lo que la persona está a punto de activar. */
.s-curso:has(.s-curso__enlace:focus-visible) { outline: 2px solid var(--s-accento); outline-offset: 2px; }
.s-curso__enlace:focus-visible { outline: none; }

.s-curso__medio {
  position: relative;
  flex: none;
  height: 180px;
  overflow: hidden;
  background: var(--s-relleno);
}
.s-curso__medio picture, .s-curso__medio img { width: 100%; height: 100%; }
/* El encuadre lo calcula la vista a partir del slug del curso. El respaldo no
   es adorno: si un día la vista deja de pasarlo, la foto se centra en vez de
   irse a la esquina superior izquierda. */
.s-curso__medio img { object-fit: cover; object-position: var(--s-encuadre, center); }

/* Sin foto: la letra del curso sobre el color de marca. Una foto de archivo
   repetida quince veces no distingue nada; una letra sí. */
.s-curso__letra {
  display: grid;
  place-items: center;
  height: 100%;
  background: var(--s-accento-suave);
  color: var(--s-accento-texto);
  font-family: var(--s-fuente-titulo);
  font-size: 3.5rem;
  font-weight: var(--s-peso-negro);
  line-height: 1;
}

.s-curso__estado {
  position: absolute;
  top: var(--s-e-3);
  inset-inline-start: var(--s-e-3);
  display: inline-flex;
  align-items: center;
  gap: var(--s-e-2);
  padding: var(--s-e-1) var(--s-e-3);
  border-radius: var(--s-r-plena);
  background: var(--s-superficie);
  color: var(--s-tinta);
  font-size: var(--s-t-xs);
  font-weight: var(--s-peso-medio);
  box-shadow: var(--s-sombra-md);
}
.s-curso__estado--accento { color: var(--s-accento-fuerte); }
.s-curso__estado--ok      { color: var(--s-ok); }
.s-curso__estado--aviso   { color: var(--s-aviso); }
.s-curso__estado--peligro { color: var(--s-peligro); }
.s-curso__estado--info    { color: var(--s-info); }

.s-curso__cuerpo {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: var(--s-e-3);
  padding: var(--s-e-5);
}
.s-curso__eje      { margin: 0; font-size: var(--s-t-xs); color: var(--s-tenue); }
.s-curso__titulo   { margin: 0; font-size: var(--s-t-lg); font-weight: var(--s-peso-negro);
                     line-height: 1.3; text-wrap: pretty; }
.s-curso__bajada   { margin: 0; font-size: var(--s-t-sm); color: var(--s-tenue);
                     line-height: 1.55; text-wrap: pretty; }
.s-curso__duracion { margin: 0; display: flex; align-items: center; gap: var(--s-e-2);
                     font-size: var(--s-t-sm); color: var(--s-tenue); }
.s-curso__duracion .s-icono { width: 16px; height: 16px; }
.s-curso__pie      { margin-top: auto; display: flex; flex-direction: column; gap: var(--s-e-3); }
.s-curso__accion   { width: 100%; }

/* «Recomendados»: la misma tarjeta con menos peso. Más baja, sin estado y sin
   botón, porque no se está retomando nada — se está mirando qué hay. */
.s-curso--compacta .s-curso__medio  { height: 150px; }
.s-curso--compacta .s-curso__cuerpo { padding: var(--s-e-4); gap: var(--s-e-2); }
.s-curso--compacta .s-curso__titulo { font-size: var(--s-t-md); font-weight: var(--s-peso-fuerte); }

/* ── Insignia de estado ─────────────────────────────────────────────────── */
/*
 * El color NUNCA es el único portador. Cada insignia lleva color, símbolo y
 * palabra: quien no distingue el verde del ámbar lee «Certificado» igual.
 */
.s-insignia {
  display: inline-flex;
  align-items: center;
  gap: var(--s-e-1);
  padding: 3px var(--s-e-3);
  border-radius: var(--s-r-plena);
  background: var(--s-relleno);
  color: var(--s-tinta-2);
  font-size: var(--s-t-xs);
  font-weight: var(--s-peso-fuerte);
  line-height: 1.5;
  white-space: nowrap;
}
.s-insignia--ok      { background: var(--s-ok-fondo);      color: var(--s-ok); }
.s-insignia--aviso   { background: var(--s-aviso-fondo);   color: var(--s-aviso); }
.s-insignia--peligro { background: var(--s-peligro-fondo); color: var(--s-peligro); }
.s-insignia--info    { background: var(--s-info-fondo);    color: var(--s-info); }
.s-insignia--accento { background: var(--s-accento-suave); color: var(--s-accento-fuerte); }

/* ── Barra de progreso ──────────────────────────────────────────────────── */
/*
 * El ancho llega como custom property desde el servidor: es el único caso de
 * estilo en línea que se acepta, porque un porcentaje calculado no puede ser
 * una clase. Ver `style="--pct: 42%"` en las vistas.
 */
.s-progreso {
  height: 8px;
  border-radius: var(--s-r-plena);
  background: var(--s-relleno);
  overflow: hidden;
}
.s-progreso__valor {
  display: block;
  height: 100%;
  width: var(--pct, 0%);
  border-radius: inherit;
  background: var(--s-accento);
  transition: width var(--s-mov-lento) var(--s-curva);
}
.s-progreso--fino { height: 5px; }
.s-progreso--ok      .s-progreso__valor { background: var(--s-ok); }
.s-progreso--aviso   .s-progreso__valor { background: var(--s-aviso); }
.s-progreso--peligro .s-progreso__valor { background: var(--s-peligro); }

/*
 * `3 de 8 lecciones` es mejor que `37%`: un porcentaje es una abstracción,
 * «faltan 5» es una decisión. Cuando el porcentaje va igual, va acompañado.
 */
.s-progreso-eti { display: flex; justify-content: space-between; gap: var(--s-e-3);
                  font-size: var(--s-t-sm); color: var(--s-tenue); margin-bottom: var(--s-e-2); }

/* ── Avatar ─────────────────────────────────────────────────────────────── */
.s-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 40px;
  height: 40px;
  border-radius: var(--s-r-plena);
  background: var(--s-accento-suave);
  color: var(--s-accento-fuerte);
  font-family: var(--s-fuente-titulo);
  font-size: var(--s-t-sm);
  font-weight: var(--s-peso-negro);
  overflow: hidden;
  text-transform: uppercase;
}
.s-avatar img { width: 100%; height: 100%; object-fit: cover; }
.s-avatar--sm { width: 32px; height: 32px; font-size: var(--s-t-xs); }
.s-avatar--lg { width: 64px; height: 64px; font-size: var(--s-t-xl); }

/* ── Tabla ──────────────────────────────────────────────────────────────── */
/*
 * En teléfono la tabla no se encoge: se desplaza dentro de su propio marco.
 * Una tabla que hace desplazar la página entera hacia el costado rompe la
 * lectura de todo lo demás.
 */
.s-tabla-marco { overflow-x: auto; border: 1px solid var(--s-borde);
                 border-radius: var(--s-r-lg); background: var(--s-superficie); }
.s-tabla { width: 100%; border-collapse: collapse; font-size: var(--s-t-sm); }
.s-tabla th, .s-tabla td { padding: var(--s-e-3) var(--s-e-4); text-align: start; }
.s-tabla thead th {
  position: sticky; top: 0;
  background: var(--s-superficie-2);
  border-bottom: 1px solid var(--s-borde);
  font-size: var(--s-t-xs);
  font-weight: var(--s-peso-fuerte);
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--s-tenue);
  white-space: nowrap;
}
.s-tabla tbody tr + tr td { border-top: 1px solid var(--s-borde-suave); }
.s-tabla tbody tr:hover td { background: var(--s-superficie-2); }
.s-tabla__num { font-variant-numeric: tabular-nums; text-align: end; }
/* La fila propia se resalta: en un ranking, encontrarse es lo primero que
   hace todo el mundo. */
.s-tabla tr[data-propia='true'] td { background: var(--s-accento-suave); font-weight: var(--s-peso-fuerte); }

/* ───────────────────────────────────────────────────────────────────────────
   6. FORMULARIOS
   ─────────────────────────────────────────────────────────────────────────── */

.s-campo { display: flex; flex-direction: column; gap: var(--s-e-2); margin-bottom: var(--s-e-4); }

/* La etiqueta SIEMPRE visible, nunca sólo un `placeholder`. El marcador de
   posición desaparece al escribir, y quien vuelve al formulario a corregir ya
   no sabe qué pedía ese campo. */
.s-campo__eti { font-size: var(--s-t-sm); font-weight: var(--s-peso-medio); color: var(--s-tinta); }
.s-campo__ayuda { font-size: var(--s-t-sm); color: var(--s-tenue); }

.s-entrada, .s-area, .s-selector {
  width: 100%;
  min-height: var(--s-tactil);
  padding: var(--s-e-2) var(--s-e-4);
  border: 1px solid var(--s-borde);
  border-radius: var(--s-r-md);
  background: var(--s-superficie);
  color: var(--s-tinta);
  font-family: inherit;
  /* 16px exactos: por debajo, iOS hace zoom al enfocar y descuadra la pantalla. */
  font-size: var(--s-t-md);
  transition: border-color var(--s-mov-rapido) var(--s-curva), box-shadow var(--s-mov-rapido) var(--s-curva);
}
.s-entrada::placeholder, .s-area::placeholder { color: var(--s-apagado); }
.s-entrada:focus, .s-area:focus, .s-selector:focus {
  outline: none; border-color: var(--s-accento); box-shadow: var(--s-halo);
}
.s-area { min-height: 120px; resize: vertical; line-height: var(--s-interlinea-normal); }

.s-entrada[aria-invalid='true'] { border-color: var(--s-peligro); }
.s-campo__error {
  display: flex; align-items: center; gap: var(--s-e-2);
  font-size: var(--s-t-sm); font-weight: var(--s-peso-medio); color: var(--s-peligro);
}

.s-marca-check { display: flex; align-items: flex-start; gap: var(--s-e-3); cursor: pointer;
                 min-height: var(--s-tactil); padding-block: var(--s-e-2); }
.s-marca-check input { width: 20px; height: 20px; margin-top: 2px; flex: none; accent-color: var(--s-accento); }

/* ───────────────────────────────────────────────────────────────────────────
   7. RETROALIMENTACIÓN
   ─────────────────────────────────────────────────────────────────────────── */

/* ── Aviso en flujo ─────────────────────────────────────────────────────── */
.s-aviso {
  display: flex; align-items: flex-start; gap: var(--s-e-3);
  padding: var(--s-e-4);
  border: 1px solid transparent;
  border-radius: var(--s-r-md);
  background: var(--s-relleno);
  font-size: var(--s-t-md);
  margin-bottom: var(--s-e-4);
}
.s-aviso--ok      { background: var(--s-ok-fondo);      color: var(--s-ok);      border-color: color-mix(in srgb, var(--s-ok) 25%, transparent); }
.s-aviso--atencion{ background: var(--s-aviso-fondo);   color: var(--s-aviso);   border-color: color-mix(in srgb, var(--s-aviso) 25%, transparent); }
.s-aviso--error   { background: var(--s-peligro-fondo); color: var(--s-peligro); border-color: color-mix(in srgb, var(--s-peligro) 25%, transparent); }
.s-aviso--info    { background: var(--s-info-fondo);    color: var(--s-info);    border-color: color-mix(in srgb, var(--s-info) 25%, transparent); }
.s-aviso__icono   { flex: none; width: 20px; height: 20px; margin-top: 2px; }

/* ── Toast ──────────────────────────────────────────────────────────────── */
/* No existía en la plataforma anterior. Hace falta para confirmar acciones que
   no cambian de pantalla —guardar un ajuste, copiar un enlace— sin empujar el
   contenido hacia abajo. */
.s-toasts {
  position: fixed;
  inset-block-end: var(--s-e-4);
  inset-inline: var(--s-e-4);
  z-index: 90;
  display: flex;
  flex-direction: column;
  gap: var(--s-e-2);
  pointer-events: none;
}
.s-toast {
  display: flex; align-items: center; gap: var(--s-e-3);
  padding: var(--s-e-3) var(--s-e-4);
  border-radius: var(--s-r-md);
  background: var(--marca-neutro-800);
  color: var(--marca-neutro-0);
  box-shadow: var(--s-sombra-lg);
  font-size: var(--s-t-sm);
  pointer-events: auto;
  animation: s-toast-entra var(--s-mov-normal) var(--s-curva);
}
.s-toast--ok    { border-inline-start: 3px solid var(--s-ok); }
.s-toast--error { border-inline-start: 3px solid var(--s-peligro); }
@keyframes s-toast-entra { from { opacity: 0; transform: translateY(12px); } }

@media (min-width: 480px) {
  .s-toasts { inset-inline: auto var(--s-e-5); max-width: 380px; }
}

/* ── Diálogo ────────────────────────────────────────────────────────────── */
/*
 * `<dialog>` nativo, no un `div` con JavaScript. El navegador ya da el fondo
 * inerte, el cierre con Escape, el atrapado del foco y su restauración al
 * cerrar. La plataforma anterior reimplementaba las cuatro cosas a mano.
 */
.s-dialogo {
  width: min(560px, calc(100vw - var(--s-e-6)));
  max-height: min(80svh, 720px);
  padding: 0;
  border: 1px solid var(--s-borde);
  border-radius: var(--s-r-lg);
  background: var(--s-superficie);
  color: var(--s-tinta);
  box-shadow: var(--s-sombra-lg);
}
.s-dialogo::backdrop { background: var(--s-velo); }
.s-dialogo__cabeza { display: flex; align-items: center; justify-content: space-between;
                     gap: var(--s-e-4); padding: var(--s-e-5); border-bottom: 1px solid var(--s-borde); }
.s-dialogo__cuerpo { padding: var(--s-e-5); overflow-y: auto; }
.s-dialogo__pie    { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--s-e-3);
                     padding: var(--s-e-5); border-top: 1px solid var(--s-borde); }

/* ── Esqueleto de carga ─────────────────────────────────────────────────── */
/*
 * Esqueleto, no rueda giratoria: comunica QUÉ va a aparecer y evita que la
 * página salte cuando llega. Sólo entre 300 ms y 2 s — por debajo de 300 ms un
 * indicador que aparece y desaparece se percibe como un parpadeo.
 */
.s-esqueleto {
  border-radius: var(--s-r-sm);
  background: linear-gradient(90deg, var(--s-relleno) 25%, var(--s-borde-suave) 37%, var(--s-relleno) 63%);
  background-size: 400% 100%;
  animation: s-esqueleto 1.4s ease infinite;
}
.s-esqueleto--texto   { height: 1em; margin-bottom: var(--s-e-2); }
.s-esqueleto--titulo  { height: 1.6em; width: 55%; margin-bottom: var(--s-e-3); }
.s-esqueleto--tarjeta { height: 180px; border-radius: var(--s-r-lg); }
@keyframes s-esqueleto { from { background-position: 100% 50%; } to { background-position: 0 50%; } }

/* ── Estado vacío ───────────────────────────────────────────────────────── */
/*
 * Tres partes: qué debería haber acá, por qué no hay, y qué hacer.
 * «No hay resultados» no es ninguna de las tres.
 */
.s-vacio { display: flex; flex-direction: column; align-items: center; text-align: center;
           gap: var(--s-e-3); padding: var(--s-e-7) var(--s-e-4); color: var(--s-tenue); }
.s-vacio__icono { width: 48px; height: 48px; color: var(--s-apagado); }
.s-vacio__titulo { font-family: var(--s-fuente-titulo); font-size: var(--s-t-xl);
                   font-weight: var(--s-peso-fuerte); color: var(--s-tinta); margin: 0; }
.s-vacio__texto  { margin: 0; max-width: 46ch; text-wrap: pretty; }

/* ───────────────────────────────────────────────────────────────────────────
   8. NAVEGACIÓN DENTRO DE LA PÁGINA
   ─────────────────────────────────────────────────────────────────────────── */

/* ── Pestañas ───────────────────────────────────────────────────────────── */
/* Con el patrón ARIA completo. La plataforma anterior las dibujaba como
   enlaces sueltos: se veían como pestañas y no se comportaban como tales. */
.s-pestanas { display: flex; gap: var(--s-e-1); border-bottom: 1px solid var(--s-borde);
              overflow-x: auto; scrollbar-width: none; }
.s-pestanas::-webkit-scrollbar { display: none; }
.s-pestana {
  flex: none;
  min-height: var(--s-tactil);
  padding: var(--s-e-2) var(--s-e-4);
  border: 0;
  border-bottom: 2px solid transparent;
  background: transparent;
  color: var(--s-tenue);
  font-family: inherit;
  font-size: var(--s-t-md);
  font-weight: var(--s-peso-medio);
  cursor: pointer;
  white-space: nowrap;
  transition: color var(--s-mov-rapido) var(--s-curva), border-color var(--s-mov-rapido) var(--s-curva);
}
.s-pestana:hover { color: var(--s-tinta); }
.s-pestana[aria-selected='true'] { color: var(--s-accento); border-bottom-color: var(--s-accento);
                                   font-weight: var(--s-peso-fuerte); }
.s-panel { padding-top: var(--s-e-5); }

/* ── Acordeón ───────────────────────────────────────────────────────────── */
/* Con `<details>`: plegado accesible sin una línea de JavaScript. */
.s-acordeon { border: 1px solid var(--s-borde); border-radius: var(--s-r-md);
              background: var(--s-superficie); overflow: hidden; }
.s-acordeon + .s-acordeon { margin-top: var(--s-e-2); }
.s-acordeon > summary {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-e-3);
  min-height: var(--s-tactil);
  padding: var(--s-e-3) var(--s-e-4);
  font-weight: var(--s-peso-fuerte);
  cursor: pointer;
  list-style: none;
}
.s-acordeon > summary::-webkit-details-marker { display: none; }
.s-acordeon > summary::after {
  content: '';
  flex: none; width: 9px; height: 9px;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(45deg) translateY(-2px);
  transition: transform var(--s-mov-normal) var(--s-curva);
}
.s-acordeon[open] > summary::after { transform: rotate(-135deg) translateY(-2px); }
.s-acordeon__cuerpo { padding: 0 var(--s-e-4) var(--s-e-4); }

/* ── Paginación ─────────────────────────────────────────────────────────── */
.s-paginado { display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
              gap: var(--s-e-1); margin-top: var(--s-e-5); }
.s-paginado a, .s-paginado span {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: var(--s-tactil); min-height: var(--s-tactil);
  padding-inline: var(--s-e-2);
  border-radius: var(--s-r-md);
  color: var(--s-tinta-2);
  font-size: var(--s-t-sm);
  text-decoration: none;
}
.s-paginado a:hover { background: var(--s-relleno); }
.s-paginado [aria-current='page'] { background: var(--s-accento); color: var(--s-sobre-accento);
                                    font-weight: var(--s-peso-fuerte); }

/* ───────────────────────────────────────────────────────────────────────────
   9. UTILIDADES
   ─────────────────────────────────────────────────────────────────────────── */

/* Escala de 8 pasos, sin valores intermedios. La disciplina es el punto: con
   valores libres cada pantalla queda con su propio ritmo y el conjunto se ve
   improvisado. */
.s-mt-1 { margin-top: var(--s-e-1); } .s-mb-1 { margin-bottom: var(--s-e-1); }
.s-mt-2 { margin-top: var(--s-e-2); } .s-mb-2 { margin-bottom: var(--s-e-2); }
.s-mt-3 { margin-top: var(--s-e-3); } .s-mb-3 { margin-bottom: var(--s-e-3); }
.s-mt-4 { margin-top: var(--s-e-4); } .s-mb-4 { margin-bottom: var(--s-e-4); }
.s-mt-5 { margin-top: var(--s-e-5); } .s-mb-5 { margin-bottom: var(--s-e-5); }
.s-mt-6 { margin-top: var(--s-e-6); } .s-mb-6 { margin-bottom: var(--s-e-6); }
.s-mt-7 { margin-top: var(--s-e-7); } .s-mb-7 { margin-bottom: var(--s-e-7); }
.s-mt-0 { margin-top: 0; }            .s-mb-0 { margin-bottom: 0; }

.s-gap-2 { gap: var(--s-e-2); } .s-gap-3 { gap: var(--s-e-3); }
.s-gap-4 { gap: var(--s-e-4); } .s-gap-5 { gap: var(--s-e-5); }

.s-empuje    { margin-inline-start: auto; }
/* Una lista que se dibuja como rejilla o como fila sigue siendo una lista para
   el lector de pantalla: se le saca la viñeta, no la semántica. */
.s-lista-limpia { list-style: none; margin: 0; padding: 0; }
/* Existe porque `justify-content` NO puede ir en línea: la única excepción del
   contrato es un valor calculado en el servidor, y separar dos extremos de una
   fila es una decisión de disposición, no un dato. */
.s-entre     { justify-content: space-between; }
.s-centro    { text-align: center; }
.s-derecha   { text-align: end; }
.s-nowrap    { white-space: nowrap; }
.s-tabular   { font-variant-numeric: tabular-nums; }
.s-truncar   { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ───────────────────────────────────────────────────────────────────────────
   10. ACCESIBILIDAD Y MOVIMIENTO
   ─────────────────────────────────────────────────────────────────────────── */

/* Sólo para lectores de pantalla. */
.s-solo-lector {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Primer elemento enfocable de la página. Invisible hasta que recibe el foco:
   quien navega con teclado no debería recorrer treinta enlaces de navegación
   en cada pantalla para llegar al contenido. */
.s-saltar {
  position: absolute;
  inset-block-start: -100px;
  inset-inline-start: var(--s-e-4);
  z-index: 100;
  padding: var(--s-e-3) var(--s-e-4);
  border-radius: 0 0 var(--s-r-md) var(--s-r-md);
  background: var(--s-accento);
  color: var(--s-sobre-accento);
  font-weight: var(--s-peso-fuerte);
  text-decoration: none;
  transition: inset-block-start var(--s-mov-normal) var(--s-curva);
}
.s-saltar:focus { inset-block-start: 0; color: var(--s-sobre-accento); }

/*
 * No es una preferencia estética: para personas con trastornos vestibulares el
 * movimiento produce náusea real. Se apaga TODO, incluido el desplazamiento
 * suave del `html`.
 */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* Quien pide más contraste normalmente lo pide porque lo necesita. */
@media (prefers-contrast: more) {
  :root { --s-borde: light-dark(var(--marca-neutro-500), var(--marca-neutro-300)); }
  .s-meta, .s-rotulo { color: var(--s-tinta-2); }
}

/* Al imprimir sobra todo lo que no es contenido. Importa para el diploma y
   para los reportes de equipo, que la gente sí imprime. */
@media print {
  .s-barra, .s-lateral, .s-pie, .s-toasts, .s-saltar { display: none !important; }
  .s-marco { display: block; }
  .s-principal { padding: 0; }
  /* En papel no hay tema: fondo blanco fijo, borde gris fijo. Es el unico
     lugar donde un valor literal es lo correcto, porque el medio no cambia. */
  .s-tarjeta { border-color: #999999; break-inside: avoid; }
}

/* ───────────────────────────────────────────────────────────────────────────
   11. PORTAL SIN SESIÓN
   ─────────────────────────────────────────────────────────────────────────── */

/*
 * Dos columnas en escritorio, una en teléfono — y en teléfono la imagen NO se
 * muestra. No es una decisión estética: la foto de una sucursal pesa cientos
 * de kilobytes y quien entra desde el celular en un pasillo la paga de su plan
 * de datos para ver algo que no le sirve para entrar.
 */
/*
 * El marco envuelve las dos columnas Y el pie. Sin él, `min-height: 100svh` en
 * la rejilla dejaba el pie SIEMPRE por debajo del pliegue: había que desplazar
 * para ver el aviso legal en una pantalla que no tiene nada más que desplazar.
 */
.s-portal-marco { display: flex; flex-direction: column; min-height: 100svh; }
.s-portal { display: grid; flex: 1 1 auto; min-height: 0; }

/*
 * ┌─ La foto se posiciona en absoluto, y no es capricho ──────────────────────┐
 *
 * Con `height: 100%` dentro de una celda de rejilla, la altura de la imagen
 * depende de la fila y la altura de la fila depende de la imagen. El navegador
 * rompe el ciclo usando el alto INTRÍNSECO —1.200 px—, y el portal crecía
 * hasta empujar el pie legal por debajo del pliegue en una pantalla que no
 * tiene nada más que desplazar.
 *
 * Sacándola del flujo, la celda mide lo que mide el formulario y la foto llena
 * lo que haya. El `min-height` es el piso para que en teléfono, donde la
 * columna de la foto va arriba, no quede una franja de dos centímetros.
 *
 * └──────────────────────────────────────────────────────────────────────────┘
 */
.s-portal__imagen { display: none; position: relative; overflow: hidden; min-height: 320px; }
.s-portal__imagen picture { position: absolute; inset: 0; }
.s-portal__imagen img { width: 100%; height: 100%; object-fit: cover; }

.s-portal__panel {
  display: flex; align-items: center; justify-content: center;
  /*
   * El relleno inferior es mayor que el superior a proposito: con el centrado
   * exacto, el bloque de marca queda mas abajo de donde el ojo lo busca. Es
   * centrado optico, no matematico.
   */
  padding: var(--s-e-5) var(--s-e-4) var(--s-e-8);
  background: var(--s-superficie);
}
.s-portal__caja { width: 100%; max-width: 460px; }

/*
 * ┌─ El logotipo y el descriptor mandan, no el formulario ─────────────────────┐
 *
 * La primera versión los dibujaba del tamaño de un rótulo cualquiera, y el
 * campo de correo terminaba pesando más en la pantalla que la marca. Está al
 * revés: quien abre esta dirección tiene que saber DÓNDE ESTÁ antes de saber
 * qué escribir. El formulario es obvio; la institución no.
 *
 * «Universidad Corporativa» no es una decoración: es el posicionamiento del
 * producto. Que se lea del tamaño de una nota al pie lo convierte en otra
 * cosa.
 *
 * └───────────────────────────────────────────────────────────────────────────┘
 */
.s-portal__marca {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-e-3);
  margin-bottom: var(--s-e-6);
}

.s-portal__descriptor {
  font-family: var(--s-fuente-titulo);
  font-size: 1rem;
  font-weight: var(--s-peso-fuerte);
  /* El interletrado abre la palabra y la vuelve un rótulo institucional. Es lo
     que la distingue de una línea de texto más. */
  letter-spacing: .34em;
  /* Compensa el interletrado, que agrega espacio sólo a la derecha y descentra
     la línea entera si no se corrige. */
  text-indent: .34em;
  text-transform: uppercase;
  color: var(--s-tenue);
  line-height: 1.3;
  margin: 0;
}

.s-portal__logo { height: 68px; width: auto; margin-inline: auto; }

@media (min-width: 480px) {
  .s-portal__descriptor { font-size: 1.125rem; }
  .s-portal__logo       { height: 80px; }
}

.s-portal__idiomas {
  display: flex; justify-content: center; flex-wrap: wrap;
  gap: var(--s-e-3); margin-top: var(--s-e-6);
  font-size: var(--s-t-sm);
}
.s-portal__idiomas a { color: var(--s-tenue); text-decoration: none; }
.s-portal__idiomas a:hover { color: var(--s-accento); text-decoration: underline; }
.s-portal__idiomas [aria-current='true'] { color: var(--s-tinta); font-weight: var(--s-peso-fuerte); }

/*
 * La segunda columna aparece SÓLO si hay imagen. `:has()` lo resuelve sin que
 * la vista tenga que pasar una clase extra — y sin él, el verificador público
 * de certificados quedaba con media pantalla en gris vacío.
 */
@media (min-width: 900px) {
  .s-portal:has(.s-portal__imagen) { grid-template-columns: 1fr 1fr; }
  .s-portal__imagen { display: block; }
  /* Mismo centrado optico que en movil: el relleno inferior manda. */
  .s-portal__panel { padding: var(--s-e-6) var(--s-e-7) calc(var(--s-e-8) * 1.4); }
}

/* ───────────────────────────────────────────────────────────────────────────
   12. BLOQUES DE LA PANTALLA DE INICIO
   ─────────────────────────────────────────────────────────────────────────── */

/* El espacio entre bloques es mayor que el de una rejilla cualquiera: son
   secciones distintas de la pantalla, no ítems de una lista. */
.s-inicio { display: flex; flex-direction: column; gap: var(--s-e-6); }

.s-inicio__cabecera {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s-e-4);
}
.s-inicio__rotulo { color: var(--s-accento); }
.s-inicio__titulo {
  margin: 0;
  font-family: var(--s-fuente-titulo);
  /* Grande de verdad. Es la unica linea de la pantalla que se dirige a la
     persona por su nombre, y su tamano es lo que separa «entre a una
     herramienta» de «entre a MI universidad corporativa». */
  font-size: var(--s-t-3xl);
  font-weight: var(--s-peso-negro);
  line-height: var(--s-interlinea-ajustada);
  letter-spacing: -1px;
  text-wrap: balance;
}
/* La bajada nombra el SIGUIENTE paso concreto, asi que se lee y no se saltea:
   ancho de lectura y no de pantalla. */
.s-inicio__bajada { margin: var(--s-e-2) 0 0; max-width: 62ch; color: var(--s-tinta-2); text-wrap: pretty; }

/* Las dos mitades de abajo. La de la derecha no baja de 300px: el asistente
   necesita espacio para decir que sabe hacer, y comprimido queda como un
   boton con adornos. */
.s-inicio__dupla {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(300px, 1fr);
  gap: var(--s-e-4);
  align-items: stretch;
}
@media (max-width: 900px) { .s-inicio__dupla { grid-template-columns: 1fr; } }

.s-pastilla {
  display: inline-flex;
  align-items: center;
  gap: var(--s-e-2);
  padding: var(--s-e-2) var(--s-e-4);
  border-radius: var(--s-r-plena);
  background: var(--s-relleno);
  color: var(--s-tinta-2);
  font-size: var(--s-t-xs);
}
.s-pastilla__punto { width: 8px; height: 8px; border-radius: 50%; background: var(--s-accento); }
/* Un logro se celebra: fondo de exito y no el relleno neutro. Se muestra solo
   cuando hay algo que celebrar — una pastilla que dice «0 certificadas» es un
   reproche, no un logro. */
.s-pastilla--logro {
  background: var(--s-ok-fondo);
  color: var(--s-ok);
  font-weight: var(--s-peso-fuerte);
}

/* ── Banda de bienvenida ────────────────────────────────────────────────── */
/*
 * Fondo oscuro con foto al costado, como el prototipo. El oscuro sale del
 * extremo de la rampa de neutros del cliente y no de un literal, por lo mismo
 * que el pie: quien cambie su rampa verá la banda acompañarla.
 *
 * `auto-fit` con `minmax(330px, 1fr)`: en pantalla ancha son dos columnas y en
 * teléfono se apilan solas, sin un corte declarado. La foto queda arriba en
 * ese caso, que es lo correcto — el titular es lo que hay que leer, y leer
 * después de mirar es el orden natural.
 */
.s-bienvenida {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(330px, 100%), 1fr));
  align-items: stretch;
  overflow: hidden;
  border-radius: var(--s-r-lg);
  background: var(--marca-neutro-700);
  color: var(--marca-neutro-0);
}
.s-bienvenida__texto {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: var(--s-e-4);
  padding: var(--s-e-5) clamp(1.5rem, 4vw, 3.25rem);
}
.s-bienvenida__titulo {
  margin: 0;
  font-family: var(--s-fuente-titulo);
  font-size: clamp(1.375rem, 2.2vw, 1.875rem);
  font-weight: var(--s-peso-negro);
  line-height: 1.25;
  letter-spacing: -.5px;
  color: inherit;
  text-wrap: pretty;
}
.s-bienvenida picture { display: block; min-height: 170px; max-height: 240px; }
.s-bienvenida__foto   { width: 100%; height: 100%; object-fit: cover; object-position: center 45%; }

/* ── Panel de indicadores ───────────────────────────────────────────────── */
/*
 * Los indicadores van sobre un panel de relleno y no sueltos sobre el fondo.
 * Es lo que los agrupa como UNA lectura —«cómo voy»— en vez de cuatro tarjetas
 * que compiten con las de curso que vienen abajo.
 */
/*
 * Tres columnas fijas en escritorio y no `auto-fill`.
 *
 * Con `auto-fill` y tres tarjetas, un monitor ancho metía las tres en una fila
 * y dejaba un hueco del tamaño de una cuarta: la fila quedaba visiblemente
 * incompleta, como si faltara algo. Repartir el ancho entre las que hay es lo
 * que hace que se lea como un conjunto cerrado.
 *
 * El panel ya no lleva fondo propio. Era una caja gris alrededor de tres cajas
 * blancas: dos niveles de recuadro para agrupar algo que la fila ya agrupa.
 */
.s-panel-kpi {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr));
  gap: var(--s-e-3);
}
/*
 * ┌─ El panel se achicó, y no por gusto ──────────────────────────────────────┐
 *
 * Cada tarjeta medía 24px de relleno, un icono de 44px con su propio recuadro y
 * un número de 36px. Cuatro de esas ocupaban media pantalla de portátil, y
 * debajo todavía venía lo que la persona de verdad entró a buscar: el curso a
 * medias.
 *
 * Un indicador no es el contenido de esta pantalla, es el margen del contenido.
 * Con el icono al lado del rótulo en vez de encima, y el número en 28, las tres
 * tarjetas entran en la mitad de alto y la reja de cursos sube sobre el
 * pliegue.
 *
 * └──────────────────────────────────────────────────────────────────────────┘
 */
.s-kpi {
  display: flex;
  flex-direction: column;
  gap: var(--s-e-2);
  padding: var(--s-e-4);
  border: 1px solid var(--s-borde-suave);
  border-radius: var(--s-r-md);
  background: var(--s-superficie);
}
/* El icono acompaña al rótulo en el mismo renglón: apilado se llevaba 44px de
   alto para decir lo que el rótulo ya dice con palabras. */
.s-kpi__cabeza { display: flex; align-items: center; gap: var(--s-e-2); }
.s-kpi__icono {
  display: grid;
  place-items: center;
  flex: none;
  width: 28px;
  height: 28px;
  border-radius: var(--s-r-sm);
  background: var(--s-relleno);
  color: var(--s-tinta-2);
}
.s-kpi__icono .s-icono { width: 16px; height: 16px; }

/*
 * ┌─ UNO relleno, y solo uno ──────────────────────────────────────────────────┐
 *
 * Es el numero que resume a los otros tres: cuanto de la ruta lleva. En un
 * panel donde las cuatro tarjetas pesan igual no hay donde posar el ojo
 * primero, y el ojo entonces las lee las cuatro o ninguna.
 *
 * Un solo elemento destacado crea jerarquia. Dos la deshacen, y cuatro es el
 * panel que habia antes.
 *
 * └───────────────────────────────────────────────────────────────────────────┘
 */
.s-kpi--destacado {
  border-color: transparent;
  background: linear-gradient(135deg, var(--s-accento), var(--s-accento-fuerte));
  color: var(--s-sobre-accento);
}
.s-kpi--destacado .s-kpi__titulo,
.s-kpi--destacado .s-kpi__nota { color: inherit; opacity: .84; }
.s-kpi--destacado .s-kpi__numero { font-size: 2.5rem; }

/* El denominador va mas chico: «2/8» dice una cosa y el 8 es el contexto, no
   el dato. Al mismo tamano compiten y el numero deja de leerse de un vistazo. */
.s-kpi__de { font-size: .6em; font-weight: var(--s-peso-medio); color: var(--s-tenue); }
.s-kpi__titulo { margin: 0; font-size: var(--s-t-sm); font-weight: var(--s-peso-medio); color: var(--s-tenue); text-wrap: pretty; }
.s-kpi__nota   { margin: 0; font-size: var(--s-t-xs); color: var(--s-tenue); }
.s-kpi__fila   { margin-top: auto; display: flex; flex-wrap: wrap; align-items: baseline;
                 justify-content: space-between; gap: var(--s-e-2); }
.s-kpi__numero {
  font-family: var(--s-fuente-titulo);
  font-size: 1.75rem;
  font-weight: var(--s-peso-negro);
  line-height: 1.1;
  letter-spacing: -1px;
  font-variant-numeric: tabular-nums;
}
.s-kpi__numero--pie { margin-top: auto; }
.s-kpi__chip {
  padding: var(--s-e-1) var(--s-e-3);
  border-radius: var(--s-r-sm);
  background: var(--s-relleno);
  color: var(--s-tinta-2);
  font-size: var(--s-t-xs);
}
.s-kpi__accion { margin-top: auto; width: 100%; }

/* ── Cabecera de sección ────────────────────────────────────────────────── */
.s-seccion__cabecera {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-e-3);
  margin-bottom: var(--s-e-4);
}
.s-seccion__titulo {
  margin: 0;
  font-family: var(--s-fuente-titulo);
  font-size: var(--s-t-xl);
  font-weight: var(--s-peso-negro);
  letter-spacing: -.4px;
}

/*
 * «Continuar donde quedaste» ocupa el lugar de la banda de bienvenida cuando
 * hay algo que continuar. No conviven: la pantalla tiene un solo primer golpe
 * de vista, y repartirlo entre dos bloques es perderlo en los dos.
 */
/*
 * ┌─ «Continua donde quedaste» dejo de ser una banda roja a sangre ───────────┐
 *
 * Ocupaba el ancho entero pintada con el color de marca, y con eso se comia el
 * primer golpe de vista de la pantalla entera para decir UN dato. Peor: al
 * lado, el panel de indicadores quedaba lavado, y abajo el mismo curso volvia
 * a aparecer en la reja.
 *
 * Ahora es una tarjeta normal que comparte fila con el asistente. El color de
 * marca se reserva para la tarjeta del indicador destacado y para los botones
 * — que es donde señala una accion en vez de decorar un fondo.
 *
 * └──────────────────────────────────────────────────────────────────────────┘
 */
.s-retomar { display: flex; flex-direction: column; gap: var(--s-e-4); }
.s-retomar__fila {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-e-4);
}
/* La inicial del curso sobre un cuadro de marca. Reemplaza a la portada: en un
   solo elemento ya nombrado con todas las letras, la fotografia no agrega
   reconocimiento y si agrega un pedido de red en lo primero que se ve. */
.s-retomar__marca {
  display: grid;
  place-items: center;
  flex: none;
  width: 76px;
  height: 76px;
  border-radius: var(--s-r-md);
  background: linear-gradient(135deg, var(--s-accento), var(--s-accento-fuerte));
  color: var(--s-sobre-accento);
  font-family: var(--s-fuente-titulo);
  font-size: var(--s-t-2xl);
  font-weight: var(--s-peso-negro);
  line-height: 1;
}
.s-retomar__texto { flex: 1; min-width: 200px; display: flex; flex-direction: column; gap: var(--s-e-2); }
.s-retomar__eje   { margin: 0; font-size: var(--s-t-xs); color: var(--s-tenue); }
.s-retomar__titulo, .s-hero__titulo {
  margin: 0;
  font-family: var(--s-fuente-titulo);
  font-size: var(--s-t-xl);
  font-weight: var(--s-peso-negro);
  line-height: var(--s-interlinea-ajustada);
  text-wrap: balance;
}
.s-retomar__meta  { margin: 0; font-size: var(--s-t-sm); color: var(--s-tenue); }
.s-hero__texto    { margin: 0; max-width: 52ch; color: var(--s-tinta-2); }

/* ── La tarjeta del asistente ───────────────────────────────────────────── */
/*
 * Esta SI va oscura, y es la unica de la fila.
 *
 * El asistente es lo distinto de esta plataforma, no una seccion mas. Una
 * tarjeta oscura entre claras se lee como «esto es otra cosa» sin necesidad de
 * un rotulo que lo diga.
 */
.s-asistente-cta {
  display: flex;
  flex-direction: column;
  gap: var(--s-e-3);
  padding: var(--s-e-5);
  border-radius: var(--s-r-lg);
  background: var(--marca-neutro-800);
  color: var(--marca-neutro-50);
}
.s-asistente-cta__quien  { display: flex; align-items: center; gap: var(--s-e-3); }
.s-asistente-cta__nombre { margin: 0; font-size: var(--s-t-lg); font-weight: var(--s-peso-negro); }
.s-asistente-cta__rol    { margin: 0; font-size: var(--s-t-xs); opacity: .72; }
.s-asistente-cta__texto  { margin: 0; font-size: var(--s-t-sm); line-height: var(--s-interlinea-normal); opacity: .88; }
.s-asistente-cta__accion { margin-top: auto; width: 100%; }

/* ── La franja de la proxima sesion ─────────────────────────────────────── */
.s-franja {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-e-4);
  padding: var(--s-e-4) var(--s-e-5);
  border: 1px solid var(--s-borde-suave);
  border-inline-start: 3px solid var(--s-accento);
  border-radius: var(--s-r-md);
  background: var(--s-superficie);
}
.s-franja__icono {
  display: grid;
  place-items: center;
  flex: none;
  width: 36px;
  height: 36px;
  border-radius: var(--s-r-sm);
  background: var(--s-accento-suave);
  color: var(--s-accento-texto);
}
.s-franja__texto  { flex: 1; min-width: 220px; }
.s-franja__titulo { margin: 0; font-weight: var(--s-peso-fuerte); text-wrap: pretty; }
.s-franja__meta   { margin: 0; font-size: var(--s-t-sm); color: var(--s-tenue); }

/* Botón de contorno sobre fondo claro: el «Ver todos» de cada sección. No es
   `--fantasma` porque ése no lleva borde, y acá el borde es lo que lo separa
   de un enlace suelto en una fila que ya tiene un título grande al lado. */
.s-boton--borde {
  background: transparent;
  border-color: var(--s-accento-texto);
  color: var(--s-accento-texto);
  font-weight: var(--s-peso-medio);
}
.s-boton--borde:hover:not([disabled]) { background: var(--s-accento-suave); color: var(--s-accento-fuerte); }

/* Botón sobre superficie de marca. Ambos valores salen del MISMO par de
   tokens: un blanco fijo contra un fondo tematizado da 1,15:1 de contraste en
   cuanto alguien cambia la paleta, y el texto desaparece. */
.s-boton--claro {
  align-self: flex-start;
  background: var(--s-sobre-accento);
  color: var(--s-accento-fuerte);
}
.s-boton--claro:hover { background: var(--s-sobre-accento); color: var(--s-accento-fuerte); opacity: .9; }

/* Lo bloqueado se muestra, apagado, con lo que falta. Ocultarlo elimina la
   razón para seguir avanzando; mostrarlo habilitado y rebotar enseña a
   desconfiar del resto de la interfaz. */
.s-tarjeta--apagada { opacity: .62; background: var(--s-superficie-2); }

@media (min-width: 768px) {
  /*
   * `.s-retomar` ya NO pasa a fila acá.
   *
   * Esta regla es de cuando era una banda a sangre: rotulo y titulo a la
   * izquierda, boton a la derecha. Desde que es una tarjeta que comparte fila
   * con el asistente, la fila la arma `.s-retomar__fila` — y esta regla la
   * pisaba, dejando el rotulo AL COSTADO del cuadro en vez de arriba.
   *
   * Es el defecto clasico de reestilizar sin buscar las otras reglas del mismo
   * selector: la nueva estaba bien escrita y perdia en la cascada.
   */
  .s-hero { padding: var(--s-e-7) var(--s-e-6); }
  .s-hero__titulo { font-size: var(--s-t-3xl); max-width: 22ch; }
}

/* ───────────────────────────────────────────────────────────────────────────
   13. RUTA, CURSO Y LECCIÓN
   ─────────────────────────────────────────────────────────────────────────── */

/* ── Marca de un paso de la ruta ────────────────────────────────────────── */
/*
 * Por qué existe: los pasos de la ruta metodológica se agrupan por una LETRA,
 * y la letra sola no comunica. En el método que trae el cliente hay dos fases
 * que empiezan con la misma inicial, así que quien ve una «T» no sabe cuál de
 * las dos está mirando. El nombre completo va SIEMPRE debajo, y por eso los
 * dos viven en un mismo bloque: separados, cualquier ajuste de disposición
 * puede dejar la letra sin su nombre y nadie lo nota hasta que pasa.
 *
 * Ningún componente existente sirve. `.s-numero` es una cifra con su
 * denominador al lado, alineado por la base; acá el nombre va debajo y no es
 * un denominador de nada.
 */
.s-paso__marca  { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.s-paso__letra  { font-family: var(--s-fuente-titulo); font-size: var(--s-t-2xl);
                  font-weight: var(--s-peso-negro); line-height: 1; letter-spacing: -.02em;
                  color: var(--s-accento-texto); }
/* El nombre va en tamaño de rótulo y no de cuerpo: acompaña a la letra, no
   compite con el título de la tarjeta, que es el que se lee primero. */
.s-paso__nombre { font-size: var(--s-t-sm); font-weight: var(--s-peso-fuerte); color: var(--s-tinta); }
/* Un paso bloqueado apaga la letra: el acento es la señal de «esto está vivo»
   y dejarlo encendido sobre una tarjeta apagada es una promesa falsa. */
.s-tarjeta--apagada .s-paso__letra { color: var(--s-tenue); }

/* ── Lista de lecciones ─────────────────────────────────────────────────── */
/*
 * Por qué existe: el índice de un curso es una lista de destinos con cuatro
 * datos por fila —tipo, título, duración y estado— y ninguno se puede caer en
 * el teléfono.
 *
 * `.s-tabla` no sirve: en pantalla angosta desplaza la fila dentro de su
 * marco, y un índice de navegación no se explora de costado. Una `.s-tarjeta`
 * por lección tampoco: ocho lecciones son ocho cajas con su borde y su sombra,
 * y el conjunto deja de leerse como una secuencia ordenada.
 */
.s-lecciones { display: flex; flex-direction: column; list-style: none; margin: 0; padding: 0; }
.s-leccion {
  display: flex;
  align-items: center;
  gap: var(--s-e-3);
  /* Objetivo táctil completo: una fila de índice se toca con el pulgar, de
     pie y en movimiento. */
  min-height: var(--s-tactil);
  padding: var(--s-e-3) var(--s-e-2);
  color: inherit;
  text-decoration: none;
  transition: background var(--s-mov-rapido) var(--s-curva);
}
.s-leccion + .s-leccion { border-top: 1px solid var(--s-borde-suave); }
.s-leccion:hover        { background: var(--s-relleno); color: inherit; }
.s-leccion__icono  { flex: none; width: 24px; text-align: center;
                     font-size: var(--s-t-lg); line-height: 1.4; }
.s-leccion__cuerpo { flex: 1; min-width: 0; }
.s-leccion__titulo { display: block; font-weight: var(--s-peso-medio); color: var(--s-tinta); }

/* ── Cápsula ────────────────────────────────────────────────────────────── */
/*
 * Por qué existe: la cápsula ES el contenido de la lección y llega dentro de
 * un `<iframe>`, que no tiene tamaño propio. Sin una caja con proporción
 * declarada la página se dibuja con la cápsula en cero de alto y salta cuando
 * el paquete carga: exactamente el salto que los `width`/`height` evitan en
 * las imágenes.
 *
 * `.s-tarjeta__medio` da la proporción, pero vive de los márgenes negativos de
 * una tarjeta —depende del relleno de su contenedor— y no admite un control
 * apoyado encima.
 */
.s-capsula {
  position: relative;
  aspect-ratio: 16 / 9;
  border: 1px solid var(--s-borde);
  border-radius: var(--s-r-lg);
  background: var(--s-superficie-2);
  overflow: hidden;
}
.s-capsula iframe { display: block; width: 100%; height: 100%; border: 0; }
/*
 * El control de pantalla completa se apoya SOBRE la cápsula. No es un lujo: en
 * el teléfono una cápsula 16:9 en vertical ocupa un quinto de la pantalla y es
 * inusable, así que agrandarla es la única forma de estudiar desde el celular.
 */
.s-capsula__pantalla {
  position: absolute;
  inset-block-start: var(--s-e-2);
  inset-inline-end: var(--s-e-2);
  background: var(--s-superficie);
  box-shadow: var(--s-sombra-sm);
}

/* ── Rótulo largo dentro de un botón ────────────────────────────────────── */
/*
 * «Anterior» y «Siguiente» llevan el título completo de la lección, y un
 * título largo estira el botón hasta romper la fila de navegación. Se recorta
 * con puntos suspensivos y NO en el servidor: el texto entero se queda en el
 * marcado, así que quien usa un lector de pantalla oye el nombre completo de
 * la lección a la que va.
 */
.s-boton .s-truncar { max-width: 18ch; }

/* ───────────────────────────────────────────────────────────────────────────
   13. BLOQUES DEL HUB
   ─────────────────────────────────────────────────────────────────────────── */

/* ── Buscador de pantalla ───────────────────────────────────────────────── */
/*
 * Existe porque en la biblioteca el buscador NO es un accesorio del
 * encabezado: es la pantalla. Quien entra ahí ya sabe qué necesita, y la lupa
 * de la barra superior vive plegada en celular — había que descubrir que ese
 * botón existía para poder buscar.
 *
 * No alcanzaba con `.s-campo`: ése apila etiqueta y campo en columna, para un
 * formulario. Esto es una línea sola — campo y botón — que ocupa el ancho y se
 * ve al entrar, sin desplazamiento.
 *
 * `flex-end` y no `center`: la etiqueta va arriba del campo, así que centrar
 * el botón contra el bloque entero lo dejaría flotando por encima del campo.
 */
.s-buscador {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--s-e-3);
}
/* La base de 260px es la que decide cuándo el botón baja de renglón. */
.s-buscador__campo { flex: 1 1 260px; min-width: 0; }

/* ── Estado activo de un botón que conmuta ──────────────────────────────── */
/*
 * No es un componente nuevo: es el estado que le faltaba al botón que ya
 * existe. Un botón con `aria-pressed` decía su estado al lector de pantalla y
 * no lo decía a la vista, así que dos botones que conmutan entre sí se veían
 * exactamente iguales estuviera puesto el que estuviera.
 */
.s-boton[aria-pressed='true'] {
  background: var(--s-accento-suave);
  border-color: var(--s-accento-texto);
  color: var(--s-accento-fuerte);
}

/* ── Colección: el mismo marcado, en tarjetas o en lista ────────────────── */
/*
 * La lista entra bastantes más documentos por pantalla, que es lo que hace
 * falta cuando ya sabes qué buscas. Las tarjetas sirven para recorrer y
 * descubrir. Ninguna de las dos es «la buena»: por eso se elige y se recuerda.
 *
 * Las DOS formas comparten exactamente las mismas tarjetas: lo único que
 * cambia es la clase del contenedor. Dibujar la lista aparte duplicaría el
 * peso de la página y —peor— dejaría dos lugares donde arreglar cada cosa.
 *
 * `is-cajas` e `is-lista` son las clases que ya conmuta `vista-banco.js`: el
 * contrato de ese archivo se respeta tal cual estaba.
 */
.s-coleccion { display: grid; gap: var(--s-e-4); }
.s-coleccion.is-cajas { grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr)); }
.s-coleccion.is-lista { grid-template-columns: 1fr; gap: var(--s-e-2); }

/*
 * La fila sólo se arma cuando hay ancho para ella. En celular una «lista» de
 * tarjetas horizontales no entra: queda una columna angosta de texto partido.
 */
@media (min-width: 768px) {
  .s-coleccion.is-lista .s-tarjeta {
    flex-direction: row;
    align-items: center;
    gap: var(--s-e-4);
    padding: var(--s-e-4) var(--s-e-5);
  }
  /* En fila, `margin-top: auto` ya no empuja nada: el eje cambió. */
  .s-coleccion.is-lista .s-tarjeta__pie {
    margin-top: 0;
    margin-inline-start: auto;
    padding-top: 0;
    text-align: end;
  }
}

/* ── Resumen recortado ──────────────────────────────────────────────────── */
/*
 * El recorte se hace acá y no en PHP a propósito: cortar la cadena en el
 * servidor decide por adelantado cuántas líneas entran, y eso depende del
 * ancho de la tarjeta, que sólo conoce el navegador. Además el texto completo
 * sigue en el marcado, así que el buscador del navegador lo encuentra.
 */
.s-resumen {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  overflow: hidden;
}
.s-coleccion.is-lista .s-resumen { -webkit-line-clamp: 1; line-clamp: 1; }

/* ───────────────────────────────────────────────────────────────────────────
   14. LOGROS, PREMIOS Y CUENTA
   ─────────────────────────────────────────────────────────────────────────── */

/* ── Rejilla de tablero ─────────────────────────────────────────────────── */
/*
 * Por qué existe: `--auto`, `--ancha` y `--kpi` reparten por `auto-fit`, o sea
 * que la cantidad de columnas la decide el ancho disponible. Eso es correcto
 * para una lista de tarjetas iguales y es INCORRECTO para un tablero, donde
 * cada columna es un bloque distinto y su cantidad es una decisión. En el
 * contenedor de 1320px un `auto-fit` de 320px convierte dos bloques en cuatro
 * columnas flacas, y el tablero vuelve a ser la tira vertical que se quería
 * evitar.
 *
 * `--panel` fija DOS columnas desde 900px, la primera más ancha: lo que se lee
 * primero va al principio y lo que acompaña, al lado. Debajo de 900px se apila,
 * que es lo único legible en un teléfono.
 */
.s-rejilla--panel { grid-template-columns: 1fr; }

@media (min-width: 900px) {
  .s-rejilla--panel { grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); }
}

/* ── Bloque sobre superficie de marca ───────────────────────────────────── */
/*
 * Por qué existe: el degradado de marca ya vivía en `.s-retomar` y `.s-hero`,
 * pero los dos están atados por nombre y por forma a la pantalla de inicio.
 * Logros lo necesita para el nivel y para la puerta al canje, y Premios para el
 * saldo. Copiar el degradado en cada uno lo deja fuera del alcance de cualquier
 * cambio de paleta — que es exactamente el problema que tenía la plataforma
 * anterior con sus colores escritos a mano, uno por pantalla.
 *
 * Esto es el fondo de marca y nada más: la forma la pone la rejilla que lo
 * contiene.
 */
.s-marca-bloque {
  display: flex;
  flex-direction: column;
  gap: var(--s-e-3);
  padding: var(--s-e-5);
  border-radius: var(--s-r-lg);
  background: linear-gradient(135deg, var(--s-accento), var(--s-accento-fuerte));
  color: var(--s-sobre-accento);
}
/* Los tres van con el color explícito y no heredado: `h2` y `p` traen el suyo
   de la hoja base, y sobre el degradado eso los deja ilegibles. */
.s-marca-bloque__titulo { margin: 0; color: var(--s-sobre-accento);
                          font-size: var(--s-t-xl); text-wrap: balance; }
.s-marca-bloque__rotulo { color: var(--s-sobre-accento); opacity: .82; }
.s-marca-bloque__nota   { margin: 0; color: var(--s-sobre-accento); opacity: .92; max-width: 46ch; }
.s-marca-bloque__numero { font-family: var(--s-fuente-titulo); font-size: var(--s-t-3xl);
                          font-weight: var(--s-peso-negro); line-height: 1.1;
                          font-variant-numeric: tabular-nums; }

/* La barra de progreso dentro del bloque: el carril de `--s-relleno` es un gris
   claro y sobre el degradado desaparece. Se dibuja con el MISMO token del texto
   a baja opacidad, así que sigue saliendo del par de la marca y no de un valor
   suelto que se rompa al cambiar la paleta. */
.s-marca-bloque .s-progreso        { background: color-mix(in srgb, var(--s-sobre-accento) 28%, transparent); }
.s-marca-bloque .s-progreso__valor { background: var(--s-sobre-accento); }

.s-marca-bloque--enlace {
  text-decoration: none;
  color: var(--s-sobre-accento);
  transition: box-shadow var(--s-mov-normal) var(--s-curva),
              transform var(--s-mov-normal) var(--s-curva);
}
.s-marca-bloque--enlace:hover { color: var(--s-sobre-accento); box-shadow: var(--s-sombra-md);
                                transform: translateY(-2px); }

/*
 * El saldo queda a la vista mientras se recorre el catálogo. Decidir si un
 * premio conviene exige compararlo con lo que hay, y un saldo que se pierde al
 * primer desplazamiento obliga a subir y bajar por cada tarjeta.
 *
 * Se pega DEBAJO de la barra superior, que también es pegajosa y va primero:
 * de ahí el desplazamiento por su alto y el `z-index` menor que el de ella.
 */
.s-marca-bloque--fijo {
  position: sticky;
  inset-block-start: var(--s-barra-alto);
  z-index: 20;
}

/* ── Insignias ──────────────────────────────────────────────────────────── */
/*
 * Por qué existe: una insignia es más chica que una tarjeta de curso y se lee
 * de un vistazo — figura, nombre, estado. `--auto` arranca en 260px y la
 * dejaría gigante, con tres por fila en una pantalla entera; acá entran de
 * cuatro a seis y se siguen leyendo.
 *
 * La bloqueada NO se oculta: se apaga con `.s-tarjeta--apagada` y dice qué
 * falta para ganarla. Las insignias pendientes son el mecanismo motivador de
 * toda la gamificación; esconder lo que todavía no se tiene es esconder la
 * razón para seguir.
 */
/*
 * Va sobre un `<ul>`: nueve insignias son una LISTA, y decirlo hace que un
 * lector de pantalla anuncie cuántas hay antes de recorrerlas. Nueve `<h3>`
 * habrían dicho lo mismo a la vista y habrían llenado el esquema del documento
 * de títulos que no titulan ninguna sección.
 */
/*
 * Con el ancho completo, los medallones respiran: 200px de mínimo y `auto-fill`
 * para que seis entren en una fila y media en escritorio en vez de en tres
 * pantallas. En la columna angosta que tenían antes, 170 era lo máximo que
 * cabía — el tamaño lo decidía el hueco, no la pieza.
 */
.s-medallones {
  display: grid;
  gap: var(--s-e-4);
  /*
   * 160 y no 200.
   *
   * Con 200, seis insignias entraban de a cinco en un escritorio de 1440 y la
   * sexta quedaba sola en su renglón — que se lee como que algo falta. Con 160
   * caben las seis, y el disco mide 96 más su etiqueta, así que la celda sigue
   * holgada.
   *
   * No se persigue un número exacto de columnas: es una reja fluida y el
   * cliente que cargue nueve insignias las verá repartidas de otra forma. Lo
   * que se corrige es que la medida la decidiera el hueco de una columna
   * angosta que ya no existe.
   */
  grid-template-columns: repeat(auto-fill, minmax(min(160px, 100%), 1fr));
  list-style: none;
  margin: 0;
  padding: 0;
}
.s-medallon { align-items: center; text-align: center; gap: var(--s-e-2); }
.s-medallon__nombre { margin: 0; font-weight: var(--s-peso-fuerte); text-wrap: balance; }
/*
 * ┌─ El medallón es un DISCO, no un cuadrado con un dibujo ───────────────────┐
 *
 * Así lo trae el prototipo y el motivo es de lectura, no de estética: la
 * grilla se recorre de un vistazo buscando cuántos discos están llenos. Un
 * icono suelto sobre el fondo de la tarjeta obliga a leer la etiqueta de cada
 * uno para saber si está ganado.
 *
 * Ganado: relleno de marca con el dibujo en negativo. Pendiente: el mismo
 * disco vacío con borde. El color NO es la única señal —abajo va la palabra
 * «Ganada» o «Pendiente» con su símbolo—, pero es la que hace que la grilla se
 * entienda sin leerla.
 *
 * └──────────────────────────────────────────────────────────────────────────┘
 */
.s-medallon__figura {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 96px;
  height: 96px;
  border: 2px solid var(--s-borde);
  border-radius: 50%;
  background: var(--s-superficie);
  color: var(--s-apagado);
  font-size: var(--s-t-2xl);
  line-height: 1;
}
.s-medallon__figura img { padding: var(--s-e-3); }
/*
 * El disco lleno se declara sobre el medallón GANADO, que es el que no tiene
 * `--apagada`. Al revés —apagar el ganado— habría que acordarse de agregar una
 * clase en la vista cada vez, y el defecto de olvidarla es una insignia que
 * miente diciendo que la tienes.
 */
.s-medallon:not(.s-tarjeta--apagada) .s-medallon__figura {
  border-color: var(--s-accento-texto);
  background: linear-gradient(140deg, var(--s-accento), var(--s-accento-fuerte));
  color: var(--s-sobre-accento);
  /* El halo es lo que hace que una insignia ganada se vea GANADA y no sólo
     rellena. Es el mismo color de marca, difuminado: no agrega un color nuevo
     a la paleta, agrega presencia. */
  box-shadow: 0 0 0 6px color-mix(in srgb, var(--s-accento) 12%, transparent),
              var(--s-sombra-md);
}

/*
 * ┌─ El destello de la insignia ganada ────────────────────────────────────────┐
 *
 * Una franja de luz que cruza el disco en diagonal, una vez al entrar y después
 * cada tanto. Es lo que separa «tengo esta insignia» de «conseguí esta
 * insignia» — la misma información con temperatura distinta.
 *
 * Va con `overflow: hidden` en la figura y un pseudoelemento: sin envoltorio
 * extra en el marcado, y sin una sola línea de JavaScript.
 *
 * DURA Y PARA. Ocho segundos entre destellos y no un brillo continuo: en una
 * pantalla que alguien abre a diario, un movimiento permanente deja de
 * celebrar y empieza a distraer — y no se puede ignorar sin dejar de mirar la
 * zona entera.
 *
 * └───────────────────────────────────────────────────────────────────────────┘
 */
.s-medallon:not(.s-tarjeta--apagada) .s-medallon__figura {
  position: relative;
  overflow: hidden;
}
.s-medallon:not(.s-tarjeta--apagada) .s-medallon__figura::after {
  content: '';
  position: absolute;
  inset-block: -20%;
  inset-inline-start: -60%;
  width: 40%;
  background: linear-gradient(100deg, transparent, rgb(255 255 255 / .45), transparent);
  transform: skewX(-18deg);
  animation: s-destello 8s ease-in-out calc(var(--orden, 0) * 260ms) infinite;
}
@keyframes s-destello {
  0%, 82%  { inset-inline-start: -60%; }
  92%, 100% { inset-inline-start: 130%; }
}

/* La entrada escalonada de cada medallón: aparecen uno detrás de otro, no los
   seis de golpe. El retardo lo pone la vista con `--orden`. */
.s-medallon { animation: s-medallon-entra .45s cubic-bezier(.2, .8, .3, 1) both; animation-delay: calc(var(--orden, 0) * 80ms); }
@keyframes s-medallon-entra { from { opacity: 0; transform: translateY(12px) scale(.94); } to { opacity: 1; transform: none; } }

@media (prefers-reduced-motion: reduce) {
  .s-medallon, .s-medallon__figura::after { animation: none !important; }
  .s-medallon { opacity: 1; transform: none; }
  /* El destello es puro adorno: sin animación no tiene nada que aportar. */
  .s-medallon:not(.s-tarjeta--apagada) .s-medallon__figura::after { display: none; }
}
/* `contain` y no `cover`: la insignia es un recorte con fondo transparente y
   recortarla por el lado largo le come el borde del dibujo. */
.s-medallon__figura img { width: 100%; height: 100%; object-fit: contain; }

/* ── Banda de una tarjeta sin foto ──────────────────────────────────────── */
/*
 * Por qué existe: en el catálogo de premios hay tarjetas con foto y tarjetas
 * sin ella. Sin esta banda, la que no tiene foto nace más baja y la grilla
 * queda dentada. El emoji ocupa el lugar de la FOTO, no el del texto, así que
 * la tarjeta conserva su forma y la fila se lee pareja.
 */
.s-tarjeta__medio--sin-foto {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--s-relleno);
  font-size: var(--s-t-3xl);
}

/* ── Lista de datos que la persona no edita ─────────────────────────────── */
/*
 * Por qué existe: cargo, unidad, código y estado los define la organización.
 * Dibujarlos como campos deshabilitados invita a intentarlo igual, y cada
 * intento que no cambia nada termina en un ticket de soporte. `<dl>` es la
 * etiqueta correcta —rótulo y valor, no pregunta y respuesta— pero el navegador
 * le sangra el `<dd>` 40px por omisión y lo deja desalineado de su rótulo.
 *
 * Cada par viaja envuelto en un `<div>`, que es lo que permite que la rejilla
 * mueva rótulo y valor juntos en vez de repartirlos por columnas distintas.
 */
.s-datos {
  display: grid;
  gap: var(--s-e-4);
  margin: 0;
  grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr));
}
.s-datos > div        { min-width: 0; }
.s-datos dt, .s-datos dd { margin: 0; }
.s-datos dd           { color: var(--s-tinta); font-weight: var(--s-peso-medio); }

/* ── Casilla que la plataforma no puede cumplir ─────────────────────────── */
/*
 * Por qué existe: una preferencia que no se puede honrar —avisos por mensajería
 * sin teléfono cargado— se rechaza y se dice por qué, en el mismo lugar donde
 * se elige. El `disabled` es de verdad y no una clase que la pinte gris: un
 * control que parece apagado pero responde al teclado es el botón muerto de
 * siempre, y el motivo va escrito adentro de la etiqueta para que también lo
 * lea quien no ve la pantalla.
 */
.s-marca-check:has(input:disabled) { color: var(--s-apagado); cursor: not-allowed; }

/* ── Grupo de campos ────────────────────────────────────────────────────── */
/*
 * Por qué existe: un grupo de casillas necesita `<fieldset>` con su `<legend>`.
 * Sin él, quien navega con lector de pantalla escucha cinco casillas sueltas y
 * no sabe cuáles responden a «por dónde» y cuáles a «de qué» — un `<div>` con
 * un texto en negrita se ve igual y no significa nada.
 *
 * El precio de usar la etiqueta correcta es que el navegador le pone borde,
 * relleno y sangrías propios, que no salen de ningún token y no se parecen a
 * nada del resto de la plataforma. Esto los saca, y nada más: el agrupamiento
 * queda en la semántica, que es donde sirve.
 */
.s-grupo-campos { border: 0; padding: 0; margin-inline: 0; min-width: 0; }

/* ───────────────────────────────────────────────────────────────────────────
   14. PORTADAS DE DOCUMENTO
   ─────────────────────────────────────────────────────────────────────────── */

/*
 * Una grilla de iconos idénticos con etiquetas distintas obliga a leer cada
 * tarjeta. Una imagen distinta se reconoce de un vistazo, y eso es lo que hace
 * navegable una grilla.
 *
 * Los tres valores calculados —imagen, encuadre, ángulo, mancha— llegan como
 * custom property desde el servidor, que es la única excepción de estilo en
 * línea que el proyecto acepta: un hash del slug no puede ser una clase.
 */
.s-portada {
  position: relative;
  display: grid;
  place-items: center;
  background: var(--s-relleno);
  overflow: hidden;
}

.s-portada--foto {
  background-image: var(--img);
  background-size: cover;
  /* El encuadre sale de un hash del slug: dos documentos con la misma foto de
     archivo muestran pedazos distintos y no se leen como duplicados. */
  background-position: var(--encuadre, center);
}

.s-portada--generada {
  background-image:
    radial-gradient(circle at var(--mancha, 50%) 30%,
      color-mix(in srgb, var(--s-portada-tono) 55%, transparent), transparent 60%),
    linear-gradient(var(--angulo, 45deg),
      color-mix(in srgb, var(--s-portada-tono) 30%, var(--s-superficie-2)),
      var(--s-superficie-2));
}

.s-portada__emoji { font-size: 2.4rem; line-height: 1; filter: saturate(.9); }

/* El tono lo nombra la vista con una clase; el color lo elige la hoja. La
   vista nombra el TIPO de documento, nunca lo pinta. */
.s-portada             { --s-portada-tono: var(--s-tenue); }
.s-portada.is-pdf      { --s-portada-tono: var(--s-accento); }
.s-portada.is-video    { --s-portada-tono: var(--s-info); }
.s-portada.is-onepager { --s-portada-tono: var(--s-ok); }
.s-portada.is-vr360    { --s-portada-tono: var(--s-aviso); }
.s-portada.is-tool     { --s-portada-tono: var(--s-aviso); }
.s-portada.is-link     { --s-portada-tono: var(--s-tenue); }

/* En vista de lista la portada es una miniatura al costado, no una banda. */
.s-coleccion.is-lista .s-portada {
  aspect-ratio: 1;
  width: 72px;
  border-radius: var(--s-r-md);
  margin: 0;
}

/* ───────────────────────────────────────────────────────────────────────────
   15. LA PUERTA AL CANJE
   ─────────────────────────────────────────────────────────────────────────── */

/*
 * ┌─ Por qué este bloque respira ─────────────────────────────────────────────┐
 *
 * «Canjear tus puntos» es la acción que CIERRA el ciclo de la gamificación.
 * Sin ella, sumar puntos es sumar un número que no sirve para nada, y el
 * sistema entero queda sin propósito visible.
 *
 * El cliente pidió explícitamente que se note. La animación es de tres
 * segundos y de dos por ciento de escala: suficiente para que el ojo la
 * registre al entrar a la pantalla, insuficiente para molestar a quien pasa
 * diez minutos ahí. Una animación llamativa en una pantalla que se visita a
 * diario deja de ser un realce y pasa a ser una molestia.
 *
 * └──────────────────────────────────────────────────────────────────────────┘
 */
.s-marca-bloque--enlace {
  animation: s-respira 3.2s var(--s-curva) infinite;
}

@keyframes s-respira {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.02); }
}

/*
 * Se apaga POR NOMBRE, además de la regla general de más arriba.
 *
 * La regla general usa `!important` sobre `animation-duration`, que alcanza.
 * Ésta es explícita para que quede escrito que esta animación en particular se
 * consideró: es la única que se agregó a propósito, y la única que alguien
 * podría reponer sin acordarse de que hay gente para la que el movimiento
 * produce náusea real, no incomodidad estética.
 */
@media (prefers-reduced-motion: reduce) {
  .s-marca-bloque--enlace { animation: none; }
}

/* ───────────────────────────────────────────────────────────────────────────
   16. ESCALA DE SEVERIDAD
   ─────────────────────────────────────────────────────────────────────────── */

/*
 * ┌─ Por qué existe ──────────────────────────────────────────────────────────┐
 *
 * El panel de equipo tiene DOS listas ordenadas por gravedad —quién necesita
 * acompañamiento y qué pregunta falla el equipo— y las dos resolvían su color
 * dentro de la vista. Un color decidido en PHP no lo alcanza el tema oscuro, no
 * lo audita el verificador de contraste, y mover un umbral obliga a editar una
 * plantilla.
 *
 * Acá la vista sólo NOMBRA el tramo (`is-alto`, `is-medio`, `is-bajo`) y la
 * hoja elige el par de tokens. Es el mismo par que ya usan
 * `.s-insignia--peligro`, `--aviso` e `--info`, así que el contraste está
 * resuelto de antemano y no nace una segunda escala de color que se
 * desincronice de la primera.
 *
 * El color NUNCA viaja solo: todo elemento con estas clases lleva además el
 * símbolo y la palabra del tramo («⚠ Prioridad alta»), que es lo que lee quien
 * no distingue el rojo del ámbar y lo que escucha quien no ve la pantalla.
 *
 * └──────────────────────────────────────────────────────────────────────────┘
 */
.s-severidad          { --s-sev-tinta: var(--s-tenue);   --s-sev-fondo: var(--s-relleno); }
.s-severidad.is-alto  { --s-sev-tinta: var(--s-peligro); --s-sev-fondo: var(--s-peligro-fondo); }
.s-severidad.is-medio { --s-sev-tinta: var(--s-aviso);   --s-sev-fondo: var(--s-aviso-fondo); }
.s-severidad.is-bajo  { --s-sev-tinta: var(--s-info);    --s-sev-fondo: var(--s-info-fondo); }

/* La franja repite el tramo en la FORMA, no sólo en el color: al recorrer una
   lista larga de un vistazo, el borde se ve antes que la insignia. */
.s-severidad--franja { border-inline-start: 4px solid var(--s-sev-tinta); }

/* Va junto a `.s-insignia`, que pone la forma; esto pone sólo el par de color
   del tramo. No es un modificador más de `.s-insignia` porque el tramo lo
   elige la fila entera: así un único `is-alto` pinta la insignia y la barra
   sin repetir la decisión en dos atributos que se pueden contradecir. */
.s-severidad__insignia { background: var(--s-sev-fondo); color: var(--s-sev-tinta); }

/* La barra de una brecha toma el color del tramo por la misma vía. */
.s-severidad .s-progreso__valor { background: var(--s-sev-tinta); }

/* ───────────────────────────────────────────────────────────────────────────
   13. FILTROS DE CATÁLOGO
   ─────────────────────────────────────────────────────────────────────────── */

/*
 * La barra de búsqueda y filtros de la malla. Nace OCULTA en el marcado y la
 * muestra `sensei.js`: sin JavaScript las tarjetas están todas visibles y un
 * filtro que no filtra sería un control muerto en pantalla.
 */
.s-filtros {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-e-3);
  margin-bottom: var(--s-e-5);
}

.s-filtros__campo {
  display: flex;
  align-items: center;
  gap: var(--s-e-2);
  flex: 1 1 260px;
  min-width: 0;
  max-width: 420px;
  height: var(--s-tactil);
  padding-inline: var(--s-e-4);
  border: 1px solid var(--s-borde);
  border-radius: var(--s-r-md);
  background: var(--s-superficie);
  color: var(--s-tenue);
}
.s-filtros__campo:focus-within { border-color: var(--s-accento); box-shadow: var(--s-halo); }
.s-filtros__campo input {
  flex: 1;
  min-width: 0;
  border: none;
  background: transparent;
  outline: none;
  font: inherit;
  font-size: var(--s-t-sm);
  color: var(--s-tinta);
}

.s-filtros__grupo  { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-e-2); }
.s-filtros__rotulo { font-size: var(--s-t-xs); color: var(--s-apagado); }
/* `margin-inline-start: auto` y no un `flex: 1` intermedio: el conteo se va al
   extremo sin necesitar un elemento vacío de por medio. */
.s-filtros__cuenta { margin: 0 0 0 auto; font-size: var(--s-t-xs); color: var(--s-tenue); }

/*
 * El chip es un botón de filtro. Es su propio componente y no `.s-boton` con
 * modificador porque la altura mínima táctil de 44 px, correcta para una
 * acción, deja una fila de seis filtros ocupando más que las tarjetas que
 * filtra. Acá el objetivo es 36 px con relleno horizontal generoso: se acierta
 * con el pulgar por el ancho, no por el alto.
 */
.s-chip {
  min-height: 36px;
  padding: 0 var(--s-e-4);
  border: 1px solid var(--s-borde);
  border-radius: var(--s-r-plena);
  background: var(--s-superficie);
  color: var(--s-tinta-2);
  font: inherit;
  font-size: var(--s-t-xs);
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--s-mov-rapido) var(--s-curva),
              border-color var(--s-mov-rapido) var(--s-curva);
}
.s-chip:hover { background: var(--s-relleno); color: var(--s-tinta); }
/* El filtro puesto se marca con relleno Y borde, no sólo con color de texto:
   sobre una fila de seis, el matiz de la letra solo no se distingue. */
.s-chip[aria-pressed='true'] {
  background: var(--s-accento-suave);
  border-color: var(--s-accento-texto);
  color: var(--s-accento-fuerte);
  font-weight: var(--s-peso-fuerte);
}

/* ── Pie de la pantalla sin sesión ──────────────────────────────────────── */
/*
 * A todo el ancho y por debajo de las dos columnas del portal, como el
 * prototipo. Oscuro en los dos temas: el color sale del extremo de la rampa de
 * neutros del cliente y no de un `#000` literal, por lo mismo que el pie de la
 * plataforma con sesión.
 */
.s-portal__pie {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-e-5);
  padding: var(--s-e-4) clamp(1.25rem, 4vw, 2.5rem);
  background: var(--marca-neutro-900);
  color: color-mix(in srgb, var(--marca-neutro-0) 78%, transparent);
  font-size: var(--s-t-xs);
}
.s-portal__legal {
  flex: 1 1 280px;
  margin: 0;
  line-height: var(--s-interlinea-suelta);
  text-wrap: pretty;
}
/* El enlace hereda el color del pie en vez del acento de marca: sobre un fondo
   casi negro, el rojo de Banco Aliado da 2,4:1 y no se lee. El subrayado es lo
   que lo señala como enlace, no el color. */
.s-portal__legal a { color: var(--marca-neutro-0); text-decoration: underline; }
/*
 * `margin-inline-start: auto` y no `flex-grow` en el texto. El párrafo hereda
 * el ancho de lectura de 68 caracteres de la hoja base, y eso está BIEN: una
 * línea de aviso legal estirada a 1.200 px se lee peor, no mejor. Lo que hay
 * que mover es el logotipo, no ensanchar el texto para empujarlo.
 */
.s-portal__pie-logo { flex: none; height: 36px; width: auto; margin-inline-start: auto; }

/* ───────────────────────────────────────────────────────────────────────────
   14. CALENDARIO
   ─────────────────────────────────────────────────────────────────────────── */

.s-cal__cabecera {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-e-3);
  margin-bottom: var(--s-e-4);
}
.s-cal__mes    { display: flex; align-items: center; gap: var(--s-e-3); }
.s-cal__titulo { margin: 0; font-size: var(--s-t-lg); font-weight: var(--s-peso-fuerte); }

/* En teléfono la rejilla no se apila: un calendario apilado deja de ser un
   calendario. Se desplaza en horizontal dentro de su propio marco, que es lo
   que hace que el resto de la página NO se desplace de costado. */
.s-cal__marco { overflow-x: auto; overscroll-behavior-x: contain; }

.s-cal {
  width: 100%;
  min-width: 640px;
  border-collapse: collapse;
  table-layout: fixed;
}
.s-cal th {
  padding-bottom: var(--s-e-2);
  font-size: var(--s-t-xs);
  font-weight: var(--s-peso-medio);
  color: var(--s-tenue);
  text-align: start;
}
.s-cal__dia {
  height: 104px;
  vertical-align: top;
  padding: var(--s-e-2);
  border: 1px solid var(--s-borde-suave);
  background: var(--s-superficie);
}
/* Los días del mes vecino se apagan en vez de vaciarse: la rejilla necesita
   siete columnas siempre, y una casilla en blanco se lee como un error. */
.s-cal__dia[data-propia='vecino'] { background: var(--s-fondo); }
.s-cal__dia[data-propia='vecino'] .s-cal__numero { color: var(--s-apagado); }
.s-cal__dia[data-hoy='si'] { box-shadow: inset 0 0 0 2px var(--s-accento); }
.s-cal__numero {
  display: block;
  margin-bottom: var(--s-e-1);
  font-size: var(--s-t-xs);
  color: var(--s-tinta-2);
  font-variant-numeric: tabular-nums;
}

.s-cal__evento {
  display: flex;
  align-items: center;
  gap: var(--s-e-1);
  margin-bottom: 3px;
  padding: 3px 5px;
  border-radius: var(--s-r-sm);
  font-size: .6875rem;
  line-height: 1.25;
  text-decoration: none;
}
.s-cal__evento .s-icono { width: 11px; height: 11px; }
.s-cal__evento-texto { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Los cuatro tonos salen de los tokens de estado, no de colores propios: el
   día que el cliente cambie su paleta, el calendario la acompaña. */
.s-cal__evento--info    { background: var(--s-info-fondo);    color: var(--s-info); }
.s-cal__evento--aviso   { background: var(--s-aviso-fondo);   color: var(--s-aviso); }
.s-cal__evento--accento { background: var(--s-accento-suave); color: var(--s-accento-fuerte); }
.s-cal__evento--neutro  { background: var(--s-relleno);       color: var(--s-tinta-2); }

.s-cal__ficha {
  display: grid;
  place-items: center;
  flex: none;
  width: 32px;
  height: 32px;
  border-radius: var(--s-r-sm);
}
.s-cal__ficha .s-icono { width: 16px; height: 16px; }

/* `flex-direction: row` explícito: `.s-tarjeta` es una columna, y sin esto la
   leyenda salía apilada — cinco renglones para lo que entra en uno. */
.s-cal__leyenda { display: flex; flex-flow: row wrap; align-items: center; gap: var(--s-e-4); }
.s-cal__leyenda-item { display: flex; align-items: center; gap: var(--s-e-2); font-size: var(--s-t-xs); color: var(--s-tinta-2); }

/* ── Agenda de próximos ─────────────────────────────────────────────────── */
.s-agenda { list-style: none; margin: 0; padding: 0; }
.s-agenda__fila {
  display: flex;
  align-items: center;
  gap: var(--s-e-4);
  padding: var(--s-e-3) 0;
  border-bottom: 1px solid var(--s-borde-suave);
}
.s-agenda__fila:last-child { border-bottom: none; }
.s-agenda__fecha {
  display: flex;
  flex: none;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  width: 52px;
  padding-inline-end: var(--s-e-3);
  border-inline-end: 1px solid var(--s-borde-suave);
}
.s-agenda__dia  { font-size: var(--s-t-lg); font-weight: var(--s-peso-fuerte); line-height: 1; font-variant-numeric: tabular-nums; }
.s-agenda__mes  { font-size: .6875rem; letter-spacing: .06em; color: var(--s-apagado); }
.s-agenda__cuerpo { display: flex; flex-direction: column; gap: var(--s-e-1); flex: 1; min-width: 0; }
.s-agenda__titulo { color: var(--s-tinta); font-size: var(--s-t-sm); font-weight: var(--s-peso-medio); text-decoration: none; }
.s-agenda__titulo:hover { color: var(--s-accento); text-decoration: underline; }
.s-agenda__meta { font-size: var(--s-t-xs); color: var(--s-tenue); }
.s-agenda__hora { flex: none; font-size: var(--s-t-xs); color: var(--s-tenue); font-variant-numeric: tabular-nums; }

.s-invitacion {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-e-3);
  padding: var(--s-e-4);
  border: 1px solid var(--s-borde-suave);
  border-radius: var(--s-r-md);
}
.s-invitacion__cuerpo { display: flex; flex-direction: column; gap: var(--s-e-2); flex: 1 1 260px; min-width: 0; }
.s-invitacion__titulo { font-weight: var(--s-peso-medio); }

/* ───────────────────────────────────────────────────────────────────────────
   15. EVALUACIÓN DE DESEMPEÑO
   ─────────────────────────────────────────────────────────────────────────── */

.s-evaluar__cabecera {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-e-4);
}
.s-evaluar__selector { flex: none; min-width: 230px; }
.s-evaluar__ciclo {
  flex: 1 1 300px;
  padding: var(--s-e-4);
  border-radius: var(--s-r-md);
  background: var(--s-relleno);
  text-align: center;
}
.s-evaluar__ciclo-nombre { margin: 0 0 var(--s-e-1); font-size: var(--s-t-lg); font-weight: var(--s-peso-fuerte); }

.s-evaluar__persona { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-e-4); }
.s-evaluar__nombre  { margin: 0 0 var(--s-e-1); font-size: var(--s-t-xl); }

/* Variante centrada del indicador, la del prototipo para esta pantalla. */
.s-kpi--centrado { align-items: center; text-align: center; }
.s-kpi--centrado .s-kpi__numero { font-size: var(--s-t-2xl); }
.s-kpi__icono--redondo {
  border-radius: 50%;
  background: var(--s-accento-suave);
  color: var(--s-accento-texto);
}

/* ── Competencia y escala ──────────────────────────────────────────────── */
.s-competencia { border: 1px solid var(--s-borde); gap: var(--s-e-3); }
.s-competencia__titulo { padding: 0; font-size: var(--s-t-lg); font-weight: var(--s-peso-fuerte); }
.s-competencia__texto  { margin: var(--s-e-2) 0 0; color: var(--s-tinta-2); text-wrap: pretty; }
.s-competencia__meta   { margin: var(--s-e-2) 0 var(--s-e-3); font-size: var(--s-t-xs); color: var(--s-tenue); }

.s-escala { display: flex; flex-wrap: wrap; gap: var(--s-e-2); margin-bottom: var(--s-e-3); }
/*
 * El radio real se mantiene en el árbol —no se oculta con `display:none`—
 * porque así conserva el foco del teclado y el anuncio del lector de pantalla.
 * `appearance: none` le quita el dibujo y deja la etiqueta hacer de control.
 */
.s-escala__opcion { position: relative; }
.s-escala__opcion input {
  position: absolute;
  inset: 0;
  margin: 0;
  appearance: none;
  border-radius: var(--s-r-md);
  cursor: pointer;
}
.s-escala__numero {
  display: grid;
  place-items: center;
  min-width: var(--s-tactil);
  height: var(--s-tactil);
  padding-inline: var(--s-e-3);
  border: 1px solid var(--s-borde);
  border-radius: var(--s-r-md);
  background: var(--s-superficie);
  font-weight: var(--s-peso-fuerte);
  font-variant-numeric: tabular-nums;
  transition: background var(--s-mov-rapido) var(--s-curva),
              border-color var(--s-mov-rapido) var(--s-curva);
}
.s-escala__opcion input:hover + .s-escala__numero { background: var(--s-relleno); }
.s-escala__opcion input:focus-visible + .s-escala__numero { box-shadow: var(--s-halo); }
.s-escala__opcion input:checked + .s-escala__numero {
  background: var(--s-accento);
  border-color: var(--s-accento-texto);
  color: var(--s-sobre-accento);
}
.s-escala__opcion input:disabled { cursor: default; }
.s-escala__opcion input:disabled + .s-escala__numero { opacity: .7; }

/* ── Tabla de datos: lo que le faltaba, no otra tabla ──────────────────── */
/*
 * ┌─ Esto empezó siendo un componente duplicado ───────────────────────────────┐
 *
 * La primera versión declaraba `.s-tabla__marco` y `.s-tabla` de cero, sin ver
 * que `.s-tabla-marco` y `.s-tabla` ya existían más arriba con relleno,
 * encabezado, resaltado y numeración tabular resueltos.
 *
 * El costo no fue la duplicación sino una línea de ella: `min-width: 760px`.
 * Como la copia venía después, ganaba, y le puso 760 px de ancho mínimo a
 * TODAS las tablas de la plataforma. La de ranking de Logros —cuatro columnas
 * que entran holgadas en un teléfono— pasó a medir 778 px en una ventana de
 * 375, y toda esa pantalla se desplazaba de costado.
 *
 * Lo que quedó acá es sólo lo que de verdad faltaba: un modificador para la
 * tabla ancha y tres piezas de celda.
 *
 * └───────────────────────────────────────────────────────────────────────────┘
 */

/* El ancho mínimo va en un MODIFICADOR y no en la tabla: lo necesita la de
   ocho columnas de desempeño, no la de cuatro del ranking. */
.s-tabla--ancha   { min-width: 760px; }
.s-tabla__tenue   { color: var(--s-tenue); }
.s-tabla__persona { display: flex; align-items: center; gap: var(--s-e-3); }
.s-tabla__accion  { color: var(--s-accento); font-weight: var(--s-peso-medio); text-decoration: none; white-space: nowrap; }
.s-tabla__accion:hover { text-decoration: underline; }

/* ───────────────────────────────────────────────────────────────────────────
   16. FAVORITOS
   ─────────────────────────────────────────────────────────────────────────── */

/*
 * El corazón vive sobre la foto de la tarjeta, arriba a la derecha. Es un
 * formulario de verdad —ver `partials/_corazon.php`— así que lo que se posiciona
 * es el `<form>` y lo que se dibuja es el `<button>`.
 */
.s-corazon {
  position: absolute;
  top: var(--s-e-3);
  inset-inline-end: var(--s-e-3);
  z-index: 2;
  margin: 0;
}
.s-corazon__boton {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border: none;
  border-radius: 50%;
  background: var(--s-superficie);
  color: var(--s-tenue);
  box-shadow: var(--s-sombra-md);
  cursor: pointer;
  transition: color var(--s-mov-rapido) var(--s-curva),
              transform var(--s-mov-rapido) var(--s-curva);
}
.s-corazon__boton .s-icono { width: 17px; height: 17px; }
.s-corazon__boton:hover { color: var(--s-accento); transform: scale(1.08); }
/*
 * El estado marcado sale de `aria-pressed` y no de una clase.
 *
 * Es el MISMO atributo que le dice el estado al lector de pantalla, así que no
 * pueden desincronizarse: el día que alguien actualice sólo la clase, el
 * anuncio seguiría diciendo lo contrario de lo que se ve. Con un solo portador
 * eso no puede pasar.
 */
.s-corazon__boton[aria-pressed='true'] { color: var(--s-accento); }

/* Sin el `position: relative` del medio, el corazón se ancla al primer
   antepasado posicionado —que puede ser la rejilla entera—. */
.s-curso__medio { position: relative; }

/* El título de una tarjeta de favorito es un enlace: la tarjeta entera no
   puede serlo porque adentro ya hay un botón, y un botón dentro de un enlace
   es marcado inválido y dos destinos donde la persona ve uno. */
.s-curso__enlace { color: inherit; text-decoration: none; }
.s-curso__enlace:hover { color: var(--s-accento); text-decoration: underline; }

/* La portada generada de un documento, cuando vive dentro de una tarjeta de
   curso —Favoritos muestra las dos clases en la misma rejilla—. */
.s-curso__medio .s-portada { display: grid; place-items: center; width: 100%; height: 100%; }

/* ───────────────────────────────────────────────────────────────────────────
   17. BÚSQUEDA Y COMUNIDAD
   ─────────────────────────────────────────────────────────────────────────── */

.s-buscador-grande {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-e-3);
  margin-bottom: var(--s-e-5);
}
.s-buscador-grande .s-filtros__campo { max-width: 620px; }

/* ── Una fila de resultado ──────────────────────────────────────────────── */
/*
 * La misma fila para un documento y para una persona: cambia la figura de la
 * izquierda —portada generada o retrato— y nada más. Dos componentes para dos
 * listas que se ven igual divergen a la primera corrección.
 */
.s-resultado {
  display: flex;
  align-items: center;
  gap: var(--s-e-4);
  padding: var(--s-e-3) var(--s-e-4);
  border-bottom: 1px solid var(--s-borde-suave);
  color: inherit;
  text-decoration: none;
}
.s-resultado:last-child { border-bottom: none; }
.s-resultado:hover { background: var(--s-fondo); }
.s-resultado__figura {
  display: grid;
  place-items: center;
  flex: none;
  width: 44px;
  height: 44px;
  border-radius: var(--s-r-sm);
  font-size: var(--s-t-lg);
}
.s-resultado__cuerpo { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.s-resultado__titulo { font-size: var(--s-t-sm); font-weight: var(--s-peso-medio); color: var(--s-tinta); }
.s-resultado__texto  { font-size: var(--s-t-xs); color: var(--s-tenue);
                       overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.s-resultado__meta   { flex: none; font-size: var(--s-t-xs); color: var(--s-apagado); }

/* ── Tarjeta de persona ─────────────────────────────────────────────────── */
.s-persona {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-e-3);
  padding: var(--s-e-6) var(--s-e-4);
  border: 1px solid var(--s-borde);
  border-radius: var(--s-r-lg);
  background: var(--s-superficie);
  text-align: center;
  transition: border-color var(--s-mov-normal) var(--s-curva),
              box-shadow var(--s-mov-normal) var(--s-curva),
              transform var(--s-mov-normal) var(--s-curva);
}
.s-persona:has(.s-persona__enlace:hover),
.s-persona:has(.s-persona__enlace:focus-visible) {
  border-color: var(--s-accento-texto);
  box-shadow: var(--s-sombra-md);
  transform: translateY(-2px);
}
.s-persona:has(.s-persona__enlace:focus-visible) { outline: 2px solid var(--s-accento); outline-offset: 2px; }
.s-persona__enlace:focus-visible { outline: none; }

.s-persona__nombre { margin: 0; font-size: var(--s-t-md); font-weight: var(--s-peso-fuerte); }
/* Enlace extendido: el nombre es el enlace y su pseudoelemento cubre la
   tarjeta, para que el área de clic sea toda la tarjeta sin envolver el botón
   de «Ver perfil» dentro de un `<a>`. */
.s-persona__enlace { color: inherit; text-decoration: none; }
.s-persona__enlace::after { content: ''; position: absolute; inset: 0; z-index: 1; }
.s-persona__cargo  { margin: 0; font-size: var(--s-t-xs); color: var(--s-tenue); text-wrap: pretty; }
.s-persona__unidad { margin: 0; font-size: .6875rem; color: var(--s-apagado); }
.s-persona__accion { width: 100%; margin-top: var(--s-e-2); }

/* ── Panel de perfil ────────────────────────────────────────────────────── */
.s-panel {
  position: fixed;
  inset: 0;
  z-index: 92;
  display: grid;
  place-items: center;
  padding: var(--s-e-4);
}
.s-panel__velo { position: absolute; inset: 0; background: var(--s-velo); }
.s-panel__caja {
  position: relative;
  width: 660px;
  max-width: 100%;
  max-height: calc(100svh - var(--s-e-7));
  overflow-y: auto;
  overscroll-behavior: contain;
  border-radius: var(--s-r-lg);
  background: var(--s-superficie);
  box-shadow: var(--s-sombra-lg);
}

/*
 * ┌─ La banda lleva la MARCA, y no un gris plano ──────────────────────────────┐
 *
 * Eran 112 px de `--marca-neutro-700` sin nada adentro. En pantalla no se leía
 * como un elemento de diseño: se leía como una foto de portada que no cargó, y
 * lo primero que ve quien abre una ficha es un rectángulo oscuro y vacío.
 *
 * Con el degradado de la marca la banda dice algo —de qué organización es esta
 * ficha— y el retrato monta sobre ella con el mismo gesto de antes. Más baja,
 * además: 112 px era la altura de una portada con imagen, y acá no hay imagen.
 *
 * └───────────────────────────────────────────────────────────────────────────┘
 */
.s-perfil__banda {
  /*
   * ⚠️ `sticky` y no `relative`: el ✕ vive acá dentro, y la caja del panel
   * desplaza su contenido. En un perfil largo —cuatro formaciones y seis
   * insignias— el botón se iba hacia arriba y quedaba sólo el velo y la tecla
   * Escape, que no son afordancias visibles. La banda pinchada arriba se lee
   * como una cabecera y mantiene la salida siempre a la vista.
   */
  position: sticky;
  top: 0;
  z-index: 2;
  height: 84px;
  background:
    radial-gradient(120% 160% at 85% 0%, color-mix(in srgb, var(--s-accento) 55%, transparent), transparent 70%),
    linear-gradient(120deg, var(--marca-neutro-800, #17212b), var(--marca-neutro-700));
}
.s-perfil__cerrar {
  position: absolute;
  top: var(--s-e-3);
  inset-inline-end: var(--s-e-4);
  min-width: var(--s-tactil);
  min-height: var(--s-tactil);
  border: none;
  background: none;
  color: color-mix(in srgb, var(--marca-neutro-0) 75%, transparent);
  font-size: var(--s-t-lg);
  cursor: pointer;
}
.s-perfil__cerrar:hover { color: var(--marca-neutro-0); }
/* En pantalla completa no hay panel que cerrar: el botón de volver está abajo. */
.s-perfil--suelto .s-perfil__cerrar { display: none; }
.s-perfil--suelto .s-perfil { border: 1px solid var(--s-borde); border-radius: var(--s-r-lg); overflow: hidden; }

.s-perfil__cuerpo {
  display: flex;
  flex-direction: column;
  gap: var(--s-e-5);
  padding: 0 var(--s-e-6) var(--s-e-6);
}
/* El retrato monta sobre la banda, como el prototipo: es lo que ata la foto a
   la persona en vez de dejarla flotando arriba del nombre. */
.s-perfil__cuerpo .s-retrato { margin-top: -42px; }
.s-retrato--marco { box-shadow: 0 0 0 4px var(--s-superficie), var(--s-sombra-md); }

.s-perfil__nombre   { margin: 0 0 var(--s-e-2); font-family: var(--s-fuente-titulo);
                      font-size: var(--s-t-xl); font-weight: var(--s-peso-negro); letter-spacing: -.3px; }
.s-perfil__cargo    { margin: 0 0 var(--s-e-1); font-size: var(--s-t-sm); color: var(--s-tinta-2); }
.s-perfil__contacto { margin: 0; font-size: var(--s-t-xs); color: var(--s-tenue); }
.s-perfil__titulo   { margin: 0 0 var(--s-e-3); font-size: var(--s-t-sm); font-weight: var(--s-peso-fuerte); }

.s-perfil__cifras {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(130px, 100%), 1fr));
  gap: var(--s-e-3);
}
.s-perfil__cifra {
  display: flex;
  flex-direction: column;
  gap: var(--s-e-1);
  padding: var(--s-e-3) var(--s-e-4);
  border: 1px solid var(--s-borde);
  border-radius: var(--s-r-md);
}
.s-perfil__cifra-valor  { font-size: var(--s-t-lg); font-weight: var(--s-peso-fuerte); font-variant-numeric: tabular-nums; }
.s-perfil__cifra-rotulo { font-size: .6875rem; color: var(--s-tenue); }

.s-perfil__rutas { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s-e-2); }
.s-perfil__ruta {
  display: flex;
  align-items: center;
  gap: var(--s-e-3);
  padding: var(--s-e-3) var(--s-e-4);
  border: 1px solid var(--s-borde);
  border-radius: var(--s-r-md);
}
.s-perfil__ruta-nombre { flex: 1; min-width: 0; font-size: var(--s-t-xs); text-wrap: pretty; }
.s-perfil__ruta .s-progreso { flex: none; width: 90px; }
.s-perfil__ruta-pct { flex: none; width: 38px; text-align: end; font-size: var(--s-t-xs); color: var(--s-tenue); }

.s-perfil__insignias { display: flex; flex-wrap: wrap; gap: var(--s-e-2); }
.s-perfil__insignia {
  display: flex;
  align-items: center;
  gap: var(--s-e-2);
  padding: var(--s-e-2) var(--s-e-4);
  border: 1px solid var(--s-borde);
  border-radius: var(--s-r-plena);
  font-size: var(--s-t-xs);
  color: var(--s-tinta-2);
}

/* La miniatura de una fila de tabla: 44×32, como el prototipo. Es una pista
   visual, no una portada — por eso no lleva el encuadre determinista. */
.s-tabla__miniatura { display: block; flex: none; width: 44px; height: 32px;
                      border-radius: var(--s-r-sm); overflow: hidden; background: var(--s-relleno); }
.s-tabla__miniatura img { width: 100%; height: 100%; object-fit: cover; }

/* ══════════════════════════════════════════════════════════════════════════
   EL ASISTENTE
   ══════════════════════════════════════════════════════════════════════════

   Un cajón anclado al costado derecho, sobre todo lo demás.

   ┌─ Por qué costado y no burbuja flotante ────────────────────────────────┐

   La burbuja de chat que flota en la esquina inferior derecha es el patrón
   más copiado que hay, y arrastra dos problemas que en esta plataforma se
   pagan caro: tapa la esquina donde viven los botones de acción de casi
   todas las pantallas, y en teléfono se le monta encima al pulgar.

   Un cajón no ocupa nada hasta que se abre, y cuando se abre ocupa un
   costado entero — que es lo que hace falta para leer una respuesta con sus
   fuentes sin desplazarse.

   └────────────────────────────────────────────────────────────────────────┘ */

.s-asistente {
  position: fixed;
  inset: 0;
  /* Por encima del panel de perfil (92) y de la lateral (90): el asistente se
     abre DESDE cualquiera de las dos y tiene que quedar arriba. */
  z-index: 95;
  display: flex;
  justify-content: flex-end;
}

.s-asistente__velo {
  position: absolute;
  inset: 0;
  background: var(--s-velo);
}

.s-asistente__panel {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 420px;
  max-width: 100%;
  /* `svh` y no `vh`: en el navegador del teléfono la barra de direcciones se
     esconde al desplazar, y con `vh` el formulario de escribir queda debajo
     del borde justo cuando aparece el teclado. */
  height: 100svh;
  background: var(--s-superficie);
  box-shadow: var(--s-sombra-lg);
  animation: s-asistente-entra var(--s-mov-normal) var(--s-curva);
}

@keyframes s-asistente-entra {
  from { transform: translateX(24px); opacity: 0; }
  to   { transform: none;             opacity: 1; }
}

/* ── Cabecera ───────────────────────────────────────────────────────────── */
.s-asistente__cabecera {
  display: flex;
  align-items: center;
  gap: var(--s-e-3);
  padding: var(--s-e-4);
  border-block-end: 1px solid var(--s-borde-suave);
}

/* El monograma. Se pinta con el color de la marca de la instalación, así que
   no hay una imagen por cliente que alguien tenga que acordarse de cambiar. */
.s-asistente__marca {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 42px;
  height: 42px;
  border-radius: var(--s-r-plena);
  background: var(--s-accento);
  color: var(--s-sobre-accento);
  font-family: var(--s-fuente-titulo);
  font-size: var(--s-t-lg);
  font-weight: var(--s-peso-negro);
  line-height: 1;
}

.s-asistente__quien   { min-width: 0; flex: 1; }
.s-asistente__nombre  { margin: 0; font-size: var(--s-t-md); font-weight: var(--s-peso-fuerte); }
.s-asistente__rol     { margin: 0; font-size: var(--s-t-xs); color: var(--s-tenue); }

.s-asistente__cerrar {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border: 0;
  border-radius: var(--s-r-md);
  background: none;
  color: var(--s-tinta-2);
  cursor: pointer;
}
.s-asistente__cerrar:hover { background: var(--s-relleno); color: var(--s-tinta); }

/* ── El hilo ────────────────────────────────────────────────────────────── */
.s-asistente__hilo {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  display: flex;
  flex-direction: column;
  gap: var(--s-e-3);
  padding: var(--s-e-4);
}

.s-asistente__burbuja {
  max-width: 88%;
  padding: var(--s-e-3) var(--s-e-4);
  border-radius: var(--s-r-lg);
  font-size: var(--s-t-sm);
  line-height: var(--s-interlinea-normal);
}
.s-asistente__burbuja p { margin: 0; text-wrap: pretty; }

.s-asistente__burbuja--bot {
  align-self: flex-start;
  border-start-start-radius: var(--s-r-sm);
  background: var(--s-superficie-2);
  color: var(--s-tinta);
}
.s-asistente__burbuja--yo {
  align-self: flex-end;
  border-start-end-radius: var(--s-r-sm);
  background: var(--s-accento);
  color: var(--s-sobre-accento);
}

.s-asistente__esperando { color: var(--s-tenue); font-style: italic; }
.s-asistente__error     { color: var(--s-peligro); }

/* La fuente de cada respuesta de biblioteca. Va SIEMPRE que exista: sin ella
   una respuesta no se distingue de una invención. */
.s-asistente__fuente {
  display: flex;
  align-items: flex-start;
  gap: var(--s-e-2);
  margin-block-start: var(--s-e-2);
  padding-block-start: var(--s-e-2);
  border-block-start: 1px solid var(--s-borde-suave);
  font-size: var(--s-t-xs);
  color: var(--s-tenue);
}
.s-asistente__fuente .s-icono { flex: 0 0 auto; width: 14px; height: 14px; color: var(--s-ok); }

/* Los enlaces que acompañan a una respuesta calculada. No son fuentes: son a
   dónde ir con lo que se acaba de responder. */
.s-asistente__acciones {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-e-2);
  margin-block-start: var(--s-e-3);
}
.s-asistente__accion {
  padding: var(--s-e-1) var(--s-e-3);
  border: 1px solid var(--s-borde);
  border-radius: var(--s-r-plena);
  background: var(--s-superficie);
  color: var(--s-accento-texto);
  font-size: var(--s-t-xs);
  font-weight: var(--s-peso-medio);
  text-decoration: none;
}
.s-asistente__accion:hover { background: var(--s-accento-suave); }

/* ── Pie ────────────────────────────────────────────────────────────────── */
.s-asistente__pie {
  flex: 0 0 auto;
  padding: var(--s-e-3) var(--s-e-4) var(--s-e-4);
  border-block-start: 1px solid var(--s-borde-suave);
  background: var(--s-superficie);
}

.s-asistente__chips {
  display: flex;
  gap: var(--s-e-2);
  margin-block-end: var(--s-e-3);
  /* Se desplazan en horizontal en vez de apilarse en cuatro renglones: cuatro
     renglones de atajos empujan el campo de escribir fuera de la pantalla en
     teléfono, que es donde el atajo más sirve. */
  overflow-x: auto;
  overscroll-behavior-inline: contain;
  scrollbar-width: none;
  padding-block-end: var(--s-e-1);
}
.s-asistente__chips::-webkit-scrollbar { display: none; }

.s-asistente__chip {
  flex: 0 0 auto;
  padding: var(--s-e-2) var(--s-e-3);
  border: 1px solid var(--s-borde);
  border-radius: var(--s-r-plena);
  background: var(--s-superficie);
  color: var(--s-tinta-2);
  font-family: inherit;
  font-size: var(--s-t-xs);
  cursor: pointer;
  white-space: nowrap;
}
.s-asistente__chip:hover { border-color: var(--s-accento); color: var(--s-accento); }

.s-asistente__forma {
  display: flex;
  align-items: center;
  gap: var(--s-e-2);
  padding: var(--s-e-1) var(--s-e-1) var(--s-e-1) var(--s-e-4);
  border: 1px solid var(--s-borde);
  border-radius: var(--s-r-plena);
  background: var(--s-superficie);
}
.s-asistente__forma:focus-within { border-color: var(--s-accento); box-shadow: var(--s-halo); }

.s-asistente__campo {
  flex: 1;
  min-width: 0;
  border: 0;
  background: none;
  color: var(--s-tinta);
  font-family: inherit;
  /* 16px exactos. Con menos, Safari en iPhone hace zoom al enfocar el campo y
     deja la pantalla torcida hasta que la persona la vuelve a ajustar. */
  font-size: var(--s-t-md);
}
.s-asistente__campo:focus { outline: none; }

.s-asistente__enviar {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border: 0;
  border-radius: var(--s-r-plena);
  background: var(--s-accento);
  color: var(--s-sobre-accento);
  cursor: pointer;
}
.s-asistente__enviar:disabled { opacity: .5; cursor: default; }

/* ── El botón de la lateral ─────────────────────────────────────────────── */
/* Es un `<button>` dentro de una lista de `<a>`. Sin esto hereda el aspecto de
   botón del navegador —fondo gris, borde biselado, tipografía del sistema— y
   canta entre los demás destinos. */
.s-nav__enlace--asistente {
  width: 100%;
  border: 0;
  background: none;
  font: inherit;
  text-align: start;
  cursor: pointer;
}
.s-nav__enlace--asistente .s-icono { color: var(--s-accento); }

@media (max-width: 480px) {
  /* En teléfono ocupa la pantalla entera menos una franja: esa franja es la
     que deja ver que hay algo detrás, y es donde se toca para cerrar. */
  .s-asistente__panel { width: 100%; height: 92svh; align-self: flex-end; border-start-start-radius: var(--s-r-lg); border-start-end-radius: var(--s-r-lg); }
}

/* ══════════════════════════════════════════════════════════════════════════
   DOCUMENTO PÚBLICO
   ══════════════════════════════════════════════════════════════════════════

   La estructura de `layouts/publico.php`: un texto largo que se lee de
   corrido, sin sesión y sin navegación.

   La medida de lectura manda sobre el ancho de pantalla. Un texto legal a
   1320px de ancho no se lee: el ojo pierde el renglón al volver, y en un
   documento donde cada párrafo importa eso es abandono garantizado. */

.s-documento {
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  background: var(--s-fondo);
  color: var(--s-tinta);
}

.s-documento__cabecera,
.s-documento__cuerpo,
.s-documento__pie {
  width: 100%;
  /* 76ch y no 68: el de lectura corrida es 68, pero acá conviven tablas de
     tres columnas con el párrafo. Con 68 las tablas se estrechan tanto que la
     tercera columna parte los números en dos renglones. */
  max-width: 76ch;
  margin-inline: auto;
  padding-inline: var(--s-e-5);
}

.s-documento__cabecera {
  padding-block: var(--s-e-7) var(--s-e-5);
  border-block-end: 1px solid var(--s-borde-suave);
  text-align: center;
}
.s-documento__logo   { height: 52px; width: auto; }
.s-documento__titulo {
  margin: var(--s-e-4) 0 0;
  font-family: var(--s-fuente-titulo);
  font-size: var(--s-t-2xl);
  font-weight: var(--s-peso-negro);
  line-height: var(--s-interlinea-ajustada);
  text-wrap: balance;
}
.s-documento__bajada { margin: var(--s-e-2) 0 0; font-size: var(--s-t-sm); color: var(--s-tenue); }

.s-documento__cuerpo { flex: 1; padding-block: var(--s-e-6); }
.s-documento__cuerpo p,
.s-documento__cuerpo li { line-height: var(--s-interlinea-suelta); text-wrap: pretty; }
.s-documento__cuerpo a { color: var(--s-accento); }

.s-documento__pie {
  padding-block: var(--s-e-5) var(--s-e-7);
  border-block-start: 1px solid var(--s-borde-suave);
  font-size: var(--s-t-sm);
  color: var(--s-tenue);
}
.s-documento__pie p { margin: 0 0 var(--s-e-2); }

/* ── El texto legal ─────────────────────────────────────────────────────── */
.s-legal__seccion { margin-block-start: var(--s-e-7); scroll-margin-block-start: var(--s-e-5); }
.s-legal__h2 {
  margin: 0 0 var(--s-e-4);
  padding-block-end: var(--s-e-2);
  border-block-end: 2px solid var(--s-accento);
  font-family: var(--s-fuente-titulo);
  font-size: var(--s-t-xl);
  font-weight: var(--s-peso-negro);
}
.s-legal__h3 {
  margin: var(--s-e-5) 0 var(--s-e-3);
  font-size: var(--s-t-lg);
  font-weight: var(--s-peso-fuerte);
}
.s-legal__seccion ul { padding-inline-start: var(--s-e-5); }
.s-legal__seccion li { margin-block-end: var(--s-e-2); }

/* El aviso de borrador. Rojo, arriba de todo y con `role="alert"`: es lo
   primero que tiene que registrar quien abre la página, con lector de
   pantalla o sin él. */
.s-legal__borrador {
  margin-block-end: var(--s-e-5);
  padding: var(--s-e-4) var(--s-e-5);
  border: 2px solid var(--s-peligro);
  border-radius: var(--s-r-md);
  background: var(--s-peligro-fondo);
}
.s-legal__borrador-titulo {
  margin: 0 0 var(--s-e-3);
  font-weight: var(--s-peso-negro);
  letter-spacing: .04em;
}
.s-legal__borrador p:last-child { margin-block-end: 0; }

.s-legal__indice {
  padding: var(--s-e-4) var(--s-e-5);
  border: 1px solid var(--s-borde-suave);
  border-radius: var(--s-r-md);
  background: var(--s-superficie);
}
.s-legal__indice .s-legal__h2 { border: 0; padding: 0; font-size: var(--s-t-lg); }
.s-legal__indice ol { margin: 0; padding-inline-start: var(--s-e-5); }

.s-legal__nota {
  padding: var(--s-e-3) var(--s-e-4);
  border-inline-start: 3px solid var(--s-borde);
  background: var(--s-superficie-2);
  font-size: var(--s-t-sm);
}
.s-legal__canal {
  margin-block-start: var(--s-e-4);
  padding: var(--s-e-4) var(--s-e-5);
  border: 1px solid var(--s-accento);
  border-radius: var(--s-r-md);
  background: var(--s-accento-suave);
}
.s-legal__canal .s-legal__h3 { margin-block-start: 0; }
.s-legal__estado {
  padding: var(--s-e-3) var(--s-e-4);
  border-inline-start: 3px solid var(--s-info);
  background: var(--s-info-fondo);
  font-size: var(--s-t-sm);
}

/* Lo que falta completar. Color Y palabra: «PENDIENTE» se lee igual sin ver
   el color, que es la única forma de que sirva para quien no lo distingue. */
.s-legal__pendiente { color: var(--s-peligro); font-weight: var(--s-peso-negro); letter-spacing: .03em; }

.s-legal__tabla { margin-block: var(--s-e-4); }
.s-legal__tabla code { font-size: var(--s-t-xs); }

.s-legal__recordatorio { color: var(--s-peligro); }

/* ── Ficha de documento ─────────────────────────────────────────────────── */

/* Ancho de LECTURA, no de pantalla. Un texto de consulta a 1320px pierde el
   renglón al volver: se lee una vez y no se vuelve. */
.s-lectura { max-width: 76ch; margin-inline: auto; }

/* La insignia del TIPO de documento no es un estado. Faltaba la variante
   neutra y el hueco empujaba a usar `--accento`, que pinta de rojo de marca
   una etiqueta que sólo dice «PDF» — y el rojo de marca ya significa otra
   cosa en esta plataforma. */
.s-insignia--neutra { background: var(--s-relleno); color: var(--s-tinta-2); }

.s-doc__categoria {
  margin: 0 0 var(--s-e-1);
  font-size: var(--s-t-xs);
  font-weight: var(--s-peso-fuerte);
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--s-accento-texto);
}
.s-doc__titulo {
  margin: 0 0 var(--s-e-4);
  font-family: var(--s-fuente-titulo);
  font-size: var(--s-t-2xl);
  font-weight: var(--s-peso-negro);
  line-height: var(--s-interlinea-ajustada);
  text-wrap: balance;
}
.s-doc__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-e-2) var(--s-e-4);
  padding-block-end: var(--s-e-4);
  border-block-end: 1px solid var(--s-borde-suave);
  font-size: var(--s-t-sm);
  color: var(--s-tenue);
}
.s-doc__consultas { margin-inline-start: auto; }
.s-doc__vencido   { color: var(--s-peligro); font-weight: var(--s-peso-fuerte); }

.s-doc__bajada {
  margin-block: var(--s-e-4);
  font-size: var(--s-t-lg);
  line-height: var(--s-interlinea-normal);
  color: var(--s-tinta-2);
}
.s-doc__cuerpo  { white-space: pre-line; line-height: var(--s-interlinea-suelta); }
.s-doc__accion  { margin-block-start: var(--s-e-5); }

.s-doc__asistente {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-e-4);
}
.s-doc__asistente-texto   { flex: 1; min-width: 200px; }
.s-doc__asistente-titulo  { margin: 0; font-weight: var(--s-peso-fuerte); }
.s-doc__asistente-nota    { margin: 0; font-size: var(--s-t-sm); color: var(--s-tenue); }

/* ── Visor de materiales ────────────────────────────────────────────────── */
.s-visor__cabecera {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s-e-4);
}

/* El alto es FUNCIONAL, no estético: un `<iframe>` sin alto explícito colapsa
   a cero y el documento queda invisible. `svh` y no `vh` porque en el
   navegador del teléfono la barra de direcciones entra y sale al desplazar. */
.s-visor {
  height: min(78svh, 900px);
  border: 1px solid var(--s-borde);
  border-radius: var(--s-r-md);
  overflow: hidden;
  background: var(--s-superficie-2);
}
.s-visor iframe { width: 100%; height: 100%; border: 0; display: block; }

.s-visor__nota {
  display: flex;
  align-items: center;
  gap: var(--s-e-2);
  margin: 0;
  font-size: var(--s-t-sm);
  color: var(--s-tenue);
}
.s-visor__nota .s-icono { flex: none; width: 16px; height: 16px; }

/* ── Bienvenida ─────────────────────────────────────────────────────────── */
.s-lead { font-size: var(--s-t-lg); line-height: var(--s-interlinea-normal); color: var(--s-tinta-2); text-wrap: pretty; }
.s-fila-botones { display: flex; flex-wrap: wrap; gap: var(--s-e-2); }

.s-bienvenida {
  overflow: hidden;
  border: 1px solid var(--s-borde-suave);
  border-radius: var(--s-r-lg);
  background: var(--s-superficie);
  box-shadow: var(--s-sombra-sm);
}
/* La imagen va ARRIBA y no de fondo con el texto encima. La versión heredada
   la ponía de fondo con un velo, y el contraste del título dependía de qué tan
   clara fuera la fotografía que cargó el cliente — o sea, no se podía
   garantizar. Separadas, el texto siempre se lee. */
.s-bienvenida__medio { aspect-ratio: 16 / 7; background: var(--s-relleno); }
.s-bienvenida__medio img { width: 100%; height: 100%; object-fit: cover; display: block; }

.s-bienvenida__cuerpo { padding: var(--s-e-5); display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-e-3); }
.s-bienvenida__rotulo {
  margin: 0;
  font-size: var(--s-t-xs);
  font-weight: var(--s-peso-fuerte);
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--s-accento-texto);
}
.s-bienvenida__titulo {
  margin: 0;
  font-family: var(--s-fuente-titulo);
  font-size: var(--s-t-2xl);
  font-weight: var(--s-peso-negro);
  line-height: var(--s-interlinea-ajustada);
  text-wrap: balance;
}
.s-bienvenida__resumen { margin: 0; color: var(--s-tinta-2); }

/* ── Lista de pedidos de soporte ────────────────────────────────────────── */
.s-soporte { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.s-soporte__item { padding-block: var(--s-e-4); }
.s-soporte__item + .s-soporte__item { border-block-start: 1px solid var(--s-borde-suave); }
.s-soporte__cabeza {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-e-2);
  margin-block-end: var(--s-e-1);
}
.s-soporte__asunto { font-weight: var(--s-peso-fuerte); }

/* ══════════════════════════════════════════════════════════════════════════
   LA ENTRADA: PANTALLA DE CARGA Y APARICIÓN DEL TABLERO
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Pantalla de carga ──────────────────────────────────────────────────── */

/* Por encima de todo, incluido el cajón del asistente (95) y los avisos
   flotantes (100): mientras está, no hay nada más. */
.s-splash {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background: var(--s-accento-fuerte);
  animation: s-splash-sale 2.4s ease forwards;
}
/* Apenas empieza el desvanecido deja de interceptar clics. Sin esto la primera
   interacción no responde durante medio segundo, con la aplicación ya visible
   debajo — que se siente como que la plataforma arrancó trabada. */
.s-splash--listo { pointer-events: none; }

.s-splash__trama {
  position: absolute;
  inset: 0;
  opacity: .1;
  background-image: repeating-linear-gradient(115deg, var(--marca-neutro-0) 0 2px, transparent 2px 30px);
}
.s-splash__halo {
  position: absolute;
  width: 560px;
  height: 560px;
  border-radius: var(--s-r-plena);
  background: radial-gradient(circle,
              color-mix(in srgb, var(--marca-neutro-0) 22%, transparent) 0%,
              transparent 68%);
  animation: s-splash-respira 3.2s ease-in-out infinite;
}

.s-splash__pila { position: relative; display: flex; flex-direction: column; align-items: center; }

/*
 * 260px y no 188.
 *
 * El anillo se dibuja con un `viewBox` de 188, asi que escalar la CAJA lo
 * escala entero sin tocar el `stroke-dasharray` — que esta en unidades del
 * viewBox y seguiria siendo 572.
 *
 * El tamano lo decide el LOGOTIPO, no el anillo: el de esta instalacion mide
 * 333x88, y dentro de un circulo de 188 le quedaban 116px de ancho. Un
 * logotipo apaisado aprovecha mal un circulo, y achicarlo hasta que entre es
 * la forma mas rapida de que la marca se vea barata en lo primero que se ve.
 */
.s-splash__marca { position: relative; width: min(260px, 62vw); aspect-ratio: 1; }
/*
 * ┌─ `width`/`height` explícitos, y no alcanza con `inset: 0` ────────────────┐
 *
 * El `<svg>` lleva `width="188" height="188"` como ATRIBUTOS. Con
 * `position: absolute; inset: 0` la caja queda sobredeterminada —izquierda,
 * derecha y ancho, los tres fijados— y la regla de resolución del navegador
 * descarta `right` y `bottom` y respeta el ancho intrínseco.
 *
 * Resultado: el anillo se quedaba en 188 px anclado arriba a la izquierda
 * mientras el disco escalaba con el recuadro a 203, así que el disco era MÁS
 * GRANDE que el anillo y estaba corrido 36 px en diagonal.
 *
 * Antes coincidían por casualidad: el recuadro medía 188 y el atributo también.
 * Agrandar el recuadro rompió esa coincidencia — el defecto no estaba en lo que
 * cambié, estaba esperando a que alguien cambiara el tamaño.
 *
 * Los atributos se dejan: son el tamaño intrínseco si la hoja no carga, y lo
 * que evita que el `<svg>` colapse mientras tanto.
 *
 * └──────────────────────────────────────────────────────────────────────────┘
 */
.s-splash__anillo { position: absolute; inset: 0; width: 100%; height: 100%; }
.s-splash__pista  { stroke: color-mix(in srgb, var(--marca-neutro-0) 30%, transparent); stroke-width: 2; }

/* 572 ≈ 2πr con r = 91. El trazo arranca oculto y se dibuja entero. */
.s-splash__arco {
  stroke: var(--marca-neutro-0);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-dasharray: 572;
  stroke-dashoffset: 572;
  animation: s-splash-traza 1.5s cubic-bezier(.45, .05, .2, 1) .25s forwards;
}

/*
 * `inset` en PORCENTAJE y no en píxeles: el disco tiene que seguir al anillo
 * cuando el recuadro se achica en pantallas angostas. Con un valor fijo, a
 * 62vw el disco se comería el anillo.
 *
 * 8% deja unos 17 px de holgura sobre un radio de 126 — la misma proporción que
 * el original. Con 11% eran 24 px y el anillo se veía suelto, como si el disco
 * hubiera quedado chico adentro.
 */
.s-splash__disco {
  position: absolute;
  inset: 8%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--s-e-5);
  border-radius: var(--s-r-plena);
  background: var(--marca-neutro-0);
  box-shadow: 0 12px 40px rgb(0 0 0 / .28);
  animation: s-splash-marca-entra .8s cubic-bezier(.2, .9, .3, 1) both,
             s-splash-flota 3.4s ease-in-out 1.2s infinite;
}
/* `contain` y no `cover`: un logotipo recortado es un logotipo mal usado, y el
   ancho real cambia con cada cliente. */
.s-splash__disco img {
  display: block;
  max-width: 100%;
  height: auto;
  object-fit: contain;
  animation: s-splash-logo-entra .9s cubic-bezier(.2, .9, .3, 1) .18s both;
}

.s-splash__barra {
  width: 260px;
  height: 4px;
  margin-block-start: var(--s-e-6);
  border-radius: var(--s-r-plena);
  background: color-mix(in srgb, var(--marca-neutro-0) 20%, transparent);
  overflow: hidden;
  animation: s-splash-sube .6s ease 1s both;
}
.s-splash__barra > span {
  display: block;
  width: 100%;
  height: 100%;
  background: var(--marca-neutro-0);
  animation: s-splash-progreso 1.5s cubic-bezier(.5, 0, .2, 1) 1s both;
}

.s-splash__texto {
  margin: var(--s-e-4) 0 0;
  font-family: var(--s-fuente-titulo);
  font-size: var(--s-t-xs);
  font-weight: var(--s-peso-negro);
  letter-spacing: .24em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--marca-neutro-0) 82%, transparent);
  animation: s-splash-sube .6s ease 1.15s both;
}

@keyframes s-splash-sale        { 0%, 78% { opacity: 1; } 100% { opacity: 0; visibility: hidden; } }
@keyframes s-splash-traza       { to { stroke-dashoffset: 0; } }
@keyframes s-splash-marca-entra { from { opacity: 0; transform: scale(.72); } to { opacity: 1; transform: none; } }
@keyframes s-splash-logo-entra  { from { opacity: 0; transform: scale(.6) rotate(-14deg); } to { opacity: 1; transform: none; } }
@keyframes s-splash-flota       { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-7px); } }
@keyframes s-splash-respira     { 0%, 100% { transform: scale(1); opacity: .8; } 50% { transform: scale(1.06); opacity: 1; } }
@keyframes s-splash-sube        { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@keyframes s-splash-progreso    { from { transform: translateX(-100%); } to { transform: none; } }

/* ── Aparición del tablero ──────────────────────────────────────────────── */
/*
 * Los bloques suben escalonados, de arriba abajo, en el orden en que se leen.
 *
 * El retardo lo pone la propia vista con `--orden`, y no una clase por
 * posición: agregar un bloque en el medio no obliga a renumerar los de abajo.
 *
 * `both` importa: sin él, el bloque se ve un fotograma en su posición final
 * antes de saltar al comienzo de la animación. Ese parpadeo es peor que no
 * animar nada.
 */
.s-entra {
  animation: s-entra-sube .5s cubic-bezier(.2, .8, .3, 1) both;
  animation-delay: calc(var(--orden, 0) * 70ms);
}
@keyframes s-entra-sube { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }

/*
 * ┌─ Nada de esto es negociable con quien pidió menos movimiento ─────────────┐
 *
 * Para personas con trastornos vestibulares el movimiento produce náusea real:
 * no es una preferencia estética. La pantalla de carga NO se anima —se muestra
 * quieta y se va— y los bloques aparecen sin desplazarse.
 *
 * El arco necesita su excepción: se dibuja CON una animación, así que apagarla
 * lo dejaría invisible. Se le fija el estado final.
 *
 * └──────────────────────────────────────────────────────────────────────────┘
 */
@media (prefers-reduced-motion: reduce) {
  .s-splash, .s-splash *, .s-entra { animation: none !important; }
  .s-splash__arco { stroke-dashoffset: 0; }
  .s-entra { opacity: 1; transform: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   AGENDA DE SESIONES EN VIVO
   ══════════════════════════════════════════════════════════════════════════

   ┌─ Por qué agrupadas por día y no una pila de tarjetas ────────────────────┐

   Era una pila de tarjetas iguales, cada una repitiendo su fecha completa. Con
   ocho sesiones son ocho fechas escritas y ninguna relación entre ellas: no se
   ve que dos son el mismo día, ni cuánto falta para la próxima, ni qué semana
   está cargada.

   Agrupadas, la fecha se escribe UNA vez y pasa a ser estructura. El ojo lee
   «martes: dos cosas» sin contar tarjetas — que es lo que se le pide a un
   calendario.

   └──────────────────────────────────────────────────────────────────────────┘ */

.s-agenda__barra {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-e-3);
  margin-block-end: var(--s-e-3);
}
.s-agenda__cuenta { margin: 0 0 var(--s-e-5); }

/* Los controles de la barra se dimensionan por su contenido. `.s-selector`
   declara `width: 100%` —correcto dentro de un formulario en columna— y acá los
   dejaba uno debajo del otro, cada uno del ancho de la pantalla. */
.s-agenda__barra .s-selector { width: auto; flex: 0 1 auto; }

/* El campo de búsqueda con su lupa adentro. Es el mismo patrón que la barra
   superior, pero acá ocupa lo que sobre en vez de un ancho fijo. */
.s-buscador-campo {
  display: flex;
  align-items: center;
  gap: var(--s-e-2);
  flex: 1 1 260px;
  min-width: 220px;
  height: var(--s-tactil);
  padding-inline: var(--s-e-3);
  border: 1px solid var(--s-borde);
  border-radius: var(--s-r-md);
  background: var(--s-superficie);
  color: var(--s-tenue);
}
.s-buscador-campo:focus-within { border-color: var(--s-accento); box-shadow: var(--s-halo); }
.s-buscador-campo .s-icono { flex: none; }
.s-buscador-campo input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: none;
  outline: none;
  color: var(--s-tinta);
  font: inherit;
  /* 16px exactos: con menos, Safari en iPhone hace zoom al enfocar el campo. */
  font-size: var(--s-t-md);
}

/* Casilla con su etiqueta al lado, con la misma altura que los demás controles
   de la fila. Sin esto queda flotando a media altura entre los selectores. */
.s-interruptor {
  display: inline-flex;
  align-items: center;
  gap: var(--s-e-2);
  height: var(--s-tactil);
  padding-inline: var(--s-e-3);
  border: 1px solid var(--s-borde);
  border-radius: var(--s-r-md);
  background: var(--s-superficie);
  font-size: var(--s-t-sm);
  cursor: pointer;
  white-space: nowrap;
}
.s-interruptor:has(input:checked) { border-color: var(--s-accento); color: var(--s-accento); }
.s-interruptor input { accent-color: var(--s-accento); }

.s-agenda { display: flex; flex-direction: column; gap: var(--s-e-5); }

/* El día es una columna fija a la izquierda y las sesiones a la derecha. En
   teléfono se apila: una columna de 92px al lado de un título deja el título en
   cuatro palabras por renglón. */
.s-agenda__dia {
  display: grid;
  grid-template-columns: 92px 1fr;
  gap: var(--s-e-4);
  align-items: start;
}
@media (max-width: 640px) { .s-agenda__dia { grid-template-columns: 1fr; gap: var(--s-e-2); } }

.s-agenda__fecha {
  position: sticky;
  /* Debajo de la barra superior, que es fija: sin este desplazamiento la fecha
     se pega DETRÁS de la barra y se lee cortada. */
  top: calc(var(--s-barra-alto) + var(--s-e-3));
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s-e-1);
  padding: var(--s-e-3);
  border-radius: var(--s-r-md);
  background: var(--s-superficie-2);
  text-align: start;
}
@media (max-width: 640px) {
  .s-agenda__fecha { position: static; flex-direction: row; align-items: baseline; gap: var(--s-e-2); }
}
.s-agenda__numero { font-family: var(--s-fuente-titulo); font-size: var(--s-t-2xl); font-weight: var(--s-peso-negro); line-height: 1; }
.s-agenda__mes    { font-size: var(--s-t-sm); font-weight: var(--s-peso-fuerte); text-transform: uppercase; color: var(--s-accento); }
.s-agenda__diasem { font-size: var(--s-t-xs); color: var(--s-tenue); text-transform: capitalize; }
.s-agenda__hoy    { margin-block-start: var(--s-e-1); }

.s-agenda__sesiones { display: flex; flex-direction: column; gap: var(--s-e-3); }

/* ── Una sesión ─────────────────────────────────────────────────────────── */
.s-sesion {
  display: grid;
  grid-template-columns: auto auto 1fr auto;
  gap: var(--s-e-4);
  align-items: center;
  padding: var(--s-e-4);
  border: 1px solid var(--s-borde-suave);
  border-radius: var(--s-r-md);
  background: var(--s-superficie);
  transition: border-color var(--s-mov-rapido) var(--s-curva),
              box-shadow var(--s-mov-rapido) var(--s-curva);
}
.s-sesion:hover { border-color: var(--s-borde); box-shadow: var(--s-sombra-sm); }
/* Lo que ya pasó se apaga pero NO se esconde: sirve para saber que ocurrió y
   para encontrar la grabación. */
.s-sesion--pasada { opacity: .6; }

@media (max-width: 760px) {
  .s-sesion { grid-template-columns: auto 1fr; }
  .s-sesion__medio    { grid-row: 1; }
  .s-sesion__cuerpo   { grid-column: 1 / -1; }
  .s-sesion__acciones { grid-column: 1 / -1; }
}

.s-sesion__hora {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  min-width: 56px;
  font-variant-numeric: tabular-nums;
}
.s-sesion__hora time { font-size: var(--s-t-lg); font-weight: var(--s-peso-negro); line-height: 1.1; }
.s-sesion__dura      { font-size: var(--s-t-xs); color: var(--s-tenue); }

.s-sesion__medio {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: var(--s-r-md);
  background: var(--s-accento-suave);
  color: var(--s-accento-texto);
}

.s-sesion__cuerpo  { min-width: 0; }
.s-sesion__marcas  { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-e-2); margin-block-end: var(--s-e-1); }
.s-sesion__titulo  { margin: 0 0 var(--s-e-1); font-size: var(--s-t-lg); font-weight: var(--s-peso-fuerte); text-wrap: pretty; }
.s-sesion__meta    { margin: 0; font-size: var(--s-t-sm); color: var(--s-tenue); text-wrap: pretty; }
.s-sesion__acciones { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-e-2); }

/* ── El detalle ─────────────────────────────────────────────────────────── */
.s-detalle { position: relative; padding: var(--s-e-6); }
.s-detalle__cerrar { position: absolute; inset-block-start: var(--s-e-3); inset-inline-end: var(--s-e-3); }
.s-detalle__titulo {
  margin: var(--s-e-1) 0 var(--s-e-3);
  padding-inline-end: var(--s-e-6);
  font-family: var(--s-fuente-titulo);
  font-size: var(--s-t-2xl);
  font-weight: var(--s-peso-negro);
  line-height: var(--s-interlinea-ajustada);
  text-wrap: balance;
}
.s-detalle__descripcion { margin: 0 0 var(--s-e-5); color: var(--s-tinta-2); text-wrap: pretty; }

/* Dos columnas de dato-valor. `dl` y no una tabla: son pares, no una matriz. */
.s-detalle__datos {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(180px, 100%), 1fr));
  gap: var(--s-e-4);
  margin: 0 0 var(--s-e-5);
  padding-block: var(--s-e-4);
  border-block: 1px solid var(--s-borde-suave);
}
.s-detalle__datos dt { font-size: var(--s-t-xs); text-transform: uppercase; letter-spacing: .06em; color: var(--s-tenue); }
.s-detalle__datos dd { margin: var(--s-e-1) 0 0; font-weight: var(--s-peso-medio); text-wrap: pretty; }
.s-detalle__acciones { display: flex; flex-wrap: wrap; gap: var(--s-e-2); }

/* ══════════════════════════════════════════════════════════════════════════
   EL MICROSITIO DE BIENVENIDA
   ══════════════════════════════════════════════════════════════════════════

   ┌─ Reemplaza a la reja de seis fichas ─────────────────────────────────────┐

   Lo anterior era una reja de fichas iguales con una línea que las unía. Se
   entendía y era honesta, y aun así no hacía lo que tenía que hacer: seis
   rectángulos del mismo tamaño se leen como un formulario con seis campos.

   Acá la POSICIÓN significa algo. Un eje vertical, hitos alternados a los
   lados y una línea que se llena hasta donde la persona llegó: lo hecho queda
   atrás, lo que falta queda adelante, y el punto donde está se ve sin contar
   nada. Eso una reja no lo puede decir, porque en una reja todas las celdas
   están a la misma distancia de todas las demás.

   └──────────────────────────────────────────────────────────────────────────┘

   ┌─ Toda animación de acá tiene un trabajo ────────────────────────────────┐

   El latido del hito actual dice DÓNDE ESTÁS. El anillo del festejo dice ALGO
   SE CERRÓ. El llenado de la línea dice CUÁNTO LLEVAS. Ninguna decora.

   Y todas se apagan con `prefers-reduced-motion`, al final de esta sección.
   Para personas con trastornos vestibulares el movimiento produce náusea real:
   no es una preferencia estética. Lo que queda apagado tiene que seguir
   diciendo lo mismo — por eso el estado va además con palabra y con color, y
   nunca sólo con movimiento.

   └──────────────────────────────────────────────────────────────────────────┘ */

/* ── La estructura del micrositio ───────────────────────────────────────── */

.s-bien {
  /*
   * Los tokens de esta pantalla, declarados en el contenedor y no en `:root`.
   *
   * Es la diferencia entre un token del sistema y una medida de una pantalla:
   * `--s-e-4` lo usan cuarenta vistas y vive arriba; el grosor del eje del
   * camino lo usa esto y nada más. Poner acá lo que es de acá evita que
   * `:root` se convierta en el cajón donde va a parar todo.
   */
  --s-bien-ancho:    980px;
  --s-bien-eje:      4px;
  --s-bien-medallon: 56px;

  min-block-size: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--s-fondo);
}

/*
 * La cabecera queda pegada arriba, y es lo único de esta pantalla que lo hace.
 *
 * Lleva el avance, y el avance es la respuesta a la pregunta que alguien se
 * hace mientras baja por el camino: «¿cuánto me falta?». Un progreso que hay
 * que ir a buscar desplazando hasta arriba deja de responderla.
 */
.s-bien__cabecera {
  position: sticky;
  inset-block-start: 0;
  z-index: 30;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--s-e-4);
  padding: var(--s-e-3) var(--s-e-5);
  background: color-mix(in srgb, var(--s-superficie) 90%, transparent);
  backdrop-filter: blur(12px);
  border-block-end: 1px solid var(--s-borde-suave);
}
.s-bien__marca  { display: flex; align-items: center; gap: var(--s-e-3); min-width: 0; }
.s-bien__logo   { block-size: 34px; inline-size: auto; }
.s-bien__rotulo { margin: 0; padding-inline-start: var(--s-e-3); border-inline-start: 1px solid var(--s-borde); }

.s-bien__avance     { display: flex; flex-direction: column; gap: var(--s-e-1); min-width: 0; }
.s-bien__avance-eti { margin: 0; font-size: var(--s-t-xs); font-weight: var(--s-peso-fuerte);
                      letter-spacing: .05em; text-transform: uppercase; color: var(--s-tenue); }
.s-bien__barra      { inline-size: min(100%, 320px); }

.s-bien__salir      { white-space: nowrap; }
.s-bien__salir-nota { margin: 0; font-size: var(--s-t-xs); color: var(--s-tenue); max-inline-size: 18ch; text-wrap: pretty; }

.s-bien__cuerpo {
  flex: 1;
  inline-size: min(100% - var(--s-e-6), var(--s-bien-ancho));
  margin-inline: auto;
  padding-block: var(--s-e-7) var(--s-e-8);
}
.s-bien__aviso { margin-block-end: var(--s-e-5); }

.s-bien__pie {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--s-e-3);
  padding: var(--s-e-5);
  border-block-start: 1px solid var(--s-borde-suave);
  color: var(--s-tenue);
  font-size: var(--s-t-sm);
}
.s-bien__legal { margin: 0; max-inline-size: 70ch; text-wrap: pretty; }
.s-bien__ayuda { display: inline-flex; align-items: center; gap: var(--s-e-2); min-block-size: var(--s-tactil); }

/* ── El encabezado del recorrido ────────────────────────────────────────── */

.s-bien__intro { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-e-2); }
.s-bien__titulo {
  margin: 0;
  font-family: var(--s-fuente-titulo);
  font-size: var(--s-t-3xl);
  font-weight: var(--s-peso-negro);
  line-height: var(--s-interlinea-ajustada);
  letter-spacing: -.02em;
  text-wrap: balance;
}
.s-bien__bajada { margin: 0; max-inline-size: var(--s-lectura-max); color: var(--s-tinta-2); text-wrap: pretty; }

/* ══════════════════════════════════════════════════════════════════════════
   LA APERTURA
   ══════════════════════════════════════════════════════════════════════════

   Cubre la pantalla entera con `position: fixed`, y desplaza por dentro.

   No es un truco: la primera vez esto NO se cierra con JavaScript —se sale
   enviando uno de los dos formularios— así que tiene que verse bien y poder
   recorrerse aunque el script nunca llegue. Con `overflow-y: auto` propio,
   quien abra desde un teléfono en horizontal alcanza los botones igual.
   ══════════════════════════════════════════════════════════════════════════ */

.s-apertura {
  position: fixed;
  inset: 0;
  z-index: 80;
  overflow-y: auto;
  overscroll-behavior: contain;
  display: grid;
  place-items: center;
  padding: var(--s-e-5);
  /*
   * El fondo es un halo de la marca sobre el fondo del tema, y no una imagen.
   * Una fotografía de portada acá pesaría cientos de kilobytes en la primera
   * pantalla de alguien y competiría con el video, que es lo que hay que
   * mirar. El halo sale de un token: cambia solo con la marca del cliente y
   * con el modo oscuro.
   */
  background:
    radial-gradient(120% 90% at 50% -20%, var(--s-accento-suave), transparent 62%),
    var(--s-fondo);
}
.s-apertura__caja {
  inline-size: min(100%, 820px);
  display: flex;
  flex-direction: column;
  gap: var(--s-e-5);
  text-align: center;
  animation: s-apertura-entra .7s cubic-bezier(.2, .8, .3, 1) both;
}
.s-apertura__cerrar {
  position: absolute;
  inset-block-start: var(--s-e-4);
  inset-inline-end: var(--s-e-4);
  min-inline-size: var(--s-tactil);
  min-block-size: var(--s-tactil);
  display: grid; place-items: center;
  border: 1px solid var(--s-borde);
  border-radius: var(--s-r-plena);
  background: var(--s-superficie);
  color: var(--s-tinta-2);
  cursor: pointer;
}
.s-apertura__saludo { display: flex; flex-direction: column; align-items: center; gap: var(--s-e-2); }
.s-apertura__logo   { block-size: 48px; inline-size: auto; margin-block-end: var(--s-e-2); }
.s-apertura__rotulo { margin: 0; color: var(--s-accento-fuerte); }

/*
 * El nombre de pila, grande y antes del titular.
 *
 * Es lo único de toda la plataforma que está escrito para UNA persona, y por
 * eso va primero y en el peso más alto: quien entra tiene que leer su nombre
 * antes que ninguna otra palabra de la organización.
 */
.s-apertura__nombre {
  margin: 0;
  font-family: var(--s-fuente-titulo);
  font-size: var(--s-t-2xl);
  font-weight: var(--s-peso-medio);
  color: var(--s-tinta-2);
}
.s-apertura__titulo {
  margin: 0;
  font-family: var(--s-fuente-titulo);
  /* `clamp` y no un salto por consulta de medios: el titular es lo primero que
     se ve, y un escalón brusco al girar el teléfono se nota justo ahí. */
  font-size: clamp(var(--s-t-2xl), 5.5vw, 3.25rem);
  font-weight: var(--s-peso-negro);
  line-height: var(--s-interlinea-ajustada);
  letter-spacing: -.03em;
  text-wrap: balance;
}
.s-apertura__texto {
  margin: 0 auto;
  max-inline-size: 56ch;
  color: var(--s-tinta-2);
  font-size: var(--s-t-lg);
  line-height: var(--s-interlinea-normal);
  text-wrap: pretty;
}
.s-apertura__aviso    { text-align: start; }
.s-apertura__acciones { display: flex; flex-direction: column; align-items: center; gap: var(--s-e-2); }
.s-apertura__acciones form { inline-size: min(100%, 320px); }

@keyframes s-apertura-entra {
  from { opacity: 0; transform: translateY(24px) scale(.98); }
  to   { opacity: 1; transform: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   EL REPRODUCTOR
   ══════════════════════════════════════════════════════════════════════════ */

.s-reproductor { inline-size: 100%; }
.s-reproductor__video {
  inline-size: 100%;
  display: block;
  aspect-ratio: 16 / 9;
  border-radius: var(--s-r-lg);
  /* Fondo de la caja mientras el `poster` carga. Con el fondo del documento se
     ve un hueco; con el relleno del sistema se ve un reproductor apagado. */
  background: var(--s-relleno);
  box-shadow: var(--s-sombra-lg);
}

/*
 * ┌─ Los subtítulos se estilan, y hace falta ────────────────────────────────┐
 *
 * El `::cue` por defecto de los navegadores es texto blanco con un contorno
 * fino: sobre una escena clara —una oficina, una camisa blanca— desaparece.
 * Con caja de fondo el contraste deja de depender de lo que haya en el cuadro.
 *
 * Los colores salen de las primitivas de marca y no de literales: son los
 * mismos dos extremos que usa el resto del sistema.
 *
 * └─────────────────────────────────────────────────────────────────────────┘
 */
.s-reproductor__video::cue {
  background: color-mix(in srgb, var(--marca-neutro-900) 84%, transparent);
  color: var(--marca-neutro-0);
  font-family: var(--s-fuente-cuerpo);
  font-size: var(--s-t-md);
  line-height: var(--s-interlinea-normal);
}
.s-reproductor__nota {
  display: flex; align-items: flex-start; gap: var(--s-e-2);
  margin: var(--s-e-2) 0 0;
  font-size: var(--s-t-sm);
  color: var(--s-tenue);
  text-wrap: pretty;
}
.s-reproductor__nota--falta { color: var(--s-aviso); }
.s-reproductor__nota .s-icono { flex: none; margin-block-start: 2px; }

/* ══════════════════════════════════════════════════════════════════════════
   EL CAMINO
   ══════════════════════════════════════════════════════════════════════════ */

.s-camino {
  position: relative;
  list-style: none;
  margin: var(--s-e-7) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s-e-6);
}

/*
 * ── El eje ────────────────────────────────────────────────────────────────
 *
 * Dos capas: la línea completa en gris y, encima, la parte recorrida. La
 * segunda mide `var(--pct)`, que la vista calcula en el servidor — es el único
 * estilo en línea que este proyecto acepta, porque un porcentaje continuo no
 * puede ser una clase sin inventar ciento un modificadores.
 *
 * Las dos son `::before` y `::after` del propio `<ol>` y no elementos del
 * marcado: una línea decorativa no tiene que existir para el lector de
 * pantalla, que ya recibe «uno de seis» de la propia lista ordenada.
 */
.s-camino::before,
.s-camino::after {
  content: '';
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: calc(var(--s-bien-medallon) / 2 - var(--s-bien-eje) / 2);
  inline-size: var(--s-bien-eje);
  border-radius: var(--s-r-plena);
}
.s-camino::before { block-size: 100%; background: var(--s-borde-suave); }
.s-camino::after {
  block-size: var(--pct, 0%);
  background: linear-gradient(180deg, var(--s-ok), var(--s-accento));
  /* Se anima el ALTO y no un `transform`: el eje tiene que quedar anclado
     arriba, y una escala desde el centro lo despegaría del primer medallón. */
  transition: block-size var(--s-mov-lento) var(--s-curva);
}

.s-camino__hito {
  position: relative;
  display: grid;
  grid-template-columns: var(--s-bien-medallon) 1fr;
  align-items: start;
  gap: var(--s-e-4);
}

.s-camino__medallon {
  position: relative;
  z-index: 1;
  inline-size: var(--s-bien-medallon);
  block-size: var(--s-bien-medallon);
  display: grid;
  place-items: center;
  border: 2px solid var(--s-borde);
  border-radius: var(--s-r-plena);
  background: var(--s-superficie);
  font-size: var(--s-t-xl);
  line-height: 1;
}
.s-camino__medallon .s-icono { inline-size: 26px; block-size: 26px; }

.s-camino__tarjeta {
  border: 1px solid var(--s-borde-suave);
  border-radius: var(--s-r-lg);
  background: var(--s-superficie);
  padding: var(--s-e-5);
  display: flex;
  flex-direction: column;
  gap: var(--s-e-2);
  align-items: flex-start;
  transition: border-color var(--s-mov-normal) var(--s-curva),
              box-shadow var(--s-mov-normal) var(--s-curva);
}
.s-camino__orden   { margin: 0; }
.s-camino__nombre  { margin: 0; font-family: var(--s-fuente-titulo); font-size: var(--s-t-xl);
                     font-weight: var(--s-peso-negro); line-height: var(--s-interlinea-ajustada); text-wrap: pretty; }
.s-camino__bajada  { margin: 0; color: var(--s-tinta-2); text-wrap: pretty; }
.s-camino__estado  { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-e-2); margin: 0; }
.s-camino__minutos { font-size: var(--s-t-xs); color: var(--s-tenue); }
.s-camino__abrir   { margin-block-start: var(--s-e-2); }

/* ── Los tres estados, dichos también con color ──────────────────────────── */
/*
 * «También»: el color acompaña a la palabra que ya está en el marcado —
 * «Completada», «Faltan 2 cosas», «Se abre al terminar la anterior»— y nunca
 * la reemplaza. Quien no distingue el verde del gris lee exactamente lo mismo.
 */
.s-camino__hito--completo .s-camino__medallon { border-color: var(--s-ok); color: var(--s-ok); background: var(--s-ok-fondo); }
.s-camino__hito--completo .s-camino__tarjeta  { border-color: color-mix(in srgb, var(--s-ok) 40%, transparent); }

.s-camino__hito--abierto .s-camino__medallon  { border-color: var(--s-accento); }

/*
 * ┌─ La etapa cerrada se apaga con COLOR, no con `opacity` ────────────────────┐
 *
 * Acá había `opacity: .62` sobre la tarjeta entera. Se ve bien y deja el texto
 * chico —«Etapa 4 de 6», «Se abre al terminar la anterior», la duración— en
 * unos 2,5:1 sobre el fondo. El mínimo son 4,5:1.
 *
 * Y lo que queda ilegible es justo la línea que DICE el estado con palabra: el
 * comentario de acá arriba apoya toda la accesibilidad de esta pantalla en que
 * el estado va escrito además de indicado por color. Iba escrito, y no se leía.
 *
 * `opacity` apaga el grupo entero, texto incluido, y no distingue entre lo
 * decorativo y lo que hay que poder leer. Bajando sólo el color del medallón y
 * del borde, el texto conserva su contraste y la etapa se sigue viendo
 * claramente apagada.
 *
 * El medallón sí puede seguir con `opacity`: es decoración, su número también
 * está en el texto de al lado.
 *
 * └───────────────────────────────────────────────────────────────────────────┘
 */
.s-camino__hito--cerrado .s-camino__medallon  { opacity: .5; }
.s-camino__hito--cerrado .s-camino__tarjeta   {
  background: var(--s-superficie-2);
  border-color: var(--s-borde-suave);
}
.s-camino__hito--cerrado .s-camino__nombre    { color: var(--s-tinta-2); }

/*
 * `--s-tenue` sobre el fondo apagado ya no llega, así que los textos chicos de
 * una etapa cerrada suben a `--s-tinta-2`. Son los que dicen en qué etapa va y
 * qué falta para abrirla: la información que menos se puede perder.
 */
.s-camino__hito--cerrado .s-camino__minutos,
.s-camino__hito--cerrado .s-camino__orden     { color: var(--s-tinta-2); }

/*
 * El hito ACTUAL late despacio.
 *
 * Es la única animación continua de la pantalla, y se la gana: en un camino de
 * seis etapas donde tres están hechas y dos bloqueadas, «dónde estoy» es la
 * pregunta que se hace la persona al abrir. El latido la responde sin leer.
 */
.s-camino__hito--actual .s-camino__medallon {
  box-shadow: var(--s-halo);
  animation: s-camino-late 2.6s ease-in-out infinite;
}
.s-camino__hito--actual .s-camino__tarjeta {
  border-color: var(--s-accento-texto);
  box-shadow: var(--s-sombra-md);
}

@keyframes s-camino-late {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--s-accento) 45%, transparent); }
  50%      { box-shadow: 0 0 0 10px color-mix(in srgb, var(--s-accento) 0%, transparent); }
}

/*
 * ── El festejo ────────────────────────────────────────────────────────────
 *
 * Un anillo que se abre desde el medallón, una sola vez, cuando la persona
 * vuelve del envío con una etapa más cerrada. Lo dispara `bienvenida.js`
 * comparando contra lo que recordaba el navegador.
 *
 * Dura menos de un segundo a propósito. Una celebración que se queda es una
 * animación; una que pasa es un aplauso.
 */
.s-camino__hito--festeja .s-camino__medallon::after {
  content: '';
  position: absolute;
  inset: -6px;
  border: 3px solid var(--s-ok);
  border-radius: var(--s-r-plena);
  animation: s-camino-festeja .9s cubic-bezier(.2, .8, .3, 1) both;
}
@keyframes s-camino-festeja {
  from { opacity: 1; transform: scale(.8); }
  to   { opacity: 0; transform: scale(1.9); }
}

/*
 * ── En pantalla ancha, el camino serpentea ────────────────────────────────
 *
 * Eje al medio y tarjetas alternadas. Debajo de 900 px vuelve a la columna:
 * partir 340 px de ancho en dos columnas produce tarjetas de dieciséis
 * caracteres, y una tarjeta que no entra deja de ser un hito para ser un
 * problema.
 */
@media (min-width: 900px) {
  .s-camino::before,
  .s-camino::after {
    inset-inline-start: calc(50% - var(--s-bien-eje) / 2);
  }
  .s-camino { gap: var(--s-e-7); }
  .s-camino__hito {
    grid-template-columns: 1fr var(--s-bien-medallon) 1fr;
    align-items: center;
    gap: var(--s-e-5);
  }
  .s-camino__medallon { grid-column: 2; grid-row: 1; }
  .s-camino__tarjeta  { grid-column: 3; grid-row: 1; }
  .s-camino__hito:nth-child(odd) .s-camino__tarjeta { grid-column: 1; }
}

/* ══════════════════════════════════════════════════════════════════════════
   EL CIERRE
   ══════════════════════════════════════════════════════════════════════════

   Se dibuja siempre, apagado hasta que llega. Ver el final desde el principio
   es lo que convierte una lista en un recorrido: se sabe adónde lleva.
   ══════════════════════════════════════════════════════════════════════════ */

.s-bien-cierre {
  margin-block-start: var(--s-e-7);
  padding: var(--s-e-7) var(--s-e-6);
  border-radius: var(--s-r-lg);
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-e-3);
  border: 1px dashed var(--s-borde);
}
.s-bien-cierre--cerrado { opacity: .55; }
.s-bien-cierre--abierto {
  border-style: solid;
  border-color: color-mix(in srgb, var(--s-ok) 45%, transparent);
  background:
    radial-gradient(120% 120% at 50% 0%, var(--s-ok-fondo), transparent 70%),
    var(--s-superficie);
  box-shadow: var(--s-sombra-md);
}
.s-bien-cierre__titulo {
  margin: 0;
  font-family: var(--s-fuente-titulo);
  font-size: var(--s-t-2xl);
  font-weight: var(--s-peso-negro);
  text-wrap: balance;
}
.s-bien-cierre__texto { margin: 0; max-inline-size: 56ch; color: var(--s-tinta-2); text-wrap: pretty; }
.s-bien-cierre__nota  { margin: 0; font-size: var(--s-t-sm); color: var(--s-tenue); }

/* ══════════════════════════════════════════════════════════════════════════
   EL PANEL DE UNA ETAPA
   ══════════════════════════════════════════════════════════════════════════ */

/*
 * ┌─ El ancho se fija en la REJA y no en la caja, y costó descubrirlo ────────┐
 *
 * Lo natural es `.s-etapa__caja { inline-size: 760px; max-width: 100% }`, que
 * es lo que hace `.s-panel__caja`. En un teléfono NO funciona, y falla en
 * silencio: la caja mide 760 px sobre una pantalla de 375 y el panel se sale
 * por la derecha.
 *
 * El motivo es de sizing de reja. `.s-panel` es un `grid` con una columna
 * `auto`, y una columna `auto` se dimensiona por el contenido máximo de su
 * elemento. Durante ese cálculo, un `max-width` en PORCENTAJE no se puede
 * resolver —todavía no hay ancho de columna— así que se ignora: la columna
 * queda de 760 px. Recién después, el `100%` se resuelve contra esos mismos
 * 760 px y no recorta nada. El tope se mide contra el problema que tenía que
 * evitar.
 *
 * Con `minmax(0, 760px)` el máximo de la columna es una longitud definida, así
 * que la reja la achica cuando no entra y la caja al 100 % la sigue.
 *
 * └──────────────────────────────────────────────────────────────────────────┘
 */
.s-etapa      { grid-template-columns: minmax(0, 760px); justify-content: center; }
.s-etapa__caja { inline-size: 100%; }
.s-etapa__cabecera {
  display: flex;
  align-items: flex-start;
  gap: var(--s-e-4);
  padding: var(--s-e-6) var(--s-e-6) var(--s-e-3);
  border-block-end: 1px solid var(--s-borde-suave);
}
.s-etapa__emoji      { font-size: 2.5rem; line-height: 1; flex: none; }
.s-etapa__encabezado { min-width: 0; }
.s-etapa__titulo {
  margin: var(--s-e-1) 0 0;
  font-family: var(--s-fuente-titulo);
  font-size: var(--s-t-2xl);
  font-weight: var(--s-peso-negro);
  line-height: var(--s-interlinea-ajustada);
  text-wrap: pretty;
}
.s-etapa__bajada { margin: var(--s-e-1) 0 0; font-size: var(--s-t-sm); color: var(--s-tenue); text-wrap: pretty; }
.s-etapa__cerrar {
  margin-inline-start: auto;
  flex: none;
  min-inline-size: var(--s-tactil);
  min-block-size: var(--s-tactil);
  display: grid; place-items: center;
  border: 1px solid var(--s-borde);
  border-radius: var(--s-r-plena);
  background: transparent;
  color: var(--s-tinta-2);
  cursor: pointer;
}
.s-etapa__cerrar:hover { background: var(--s-relleno); color: var(--s-tinta); }

.s-etapa__estado {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-e-2);
  margin: 0;
  padding: var(--s-e-3) var(--s-e-6);
  background: var(--s-superficie-2);
  border-block-end: 1px solid var(--s-borde-suave);
}
.s-etapa__festejo { font-size: var(--s-t-sm); color: var(--s-ok); }

.s-etapa__items { list-style: none; margin: 0; padding: var(--s-e-4) var(--s-e-6) var(--s-e-6);
                  display: flex; flex-direction: column; }

/* ── Un elemento ────────────────────────────────────────────────────────── */

.s-elemento {
  display: grid;
  grid-template-columns: 40px 1fr;
  gap: var(--s-e-3) var(--s-e-4);
  padding-block: var(--s-e-4);
  align-items: start;
}
.s-elemento + .s-elemento { border-block-start: 1px solid var(--s-borde-suave); }

.s-elemento__icono {
  inline-size: 40px; block-size: 40px;
  display: grid; place-items: center;
  border-radius: var(--s-r-md);
  background: var(--s-relleno);
  color: var(--s-tinta-2);
}
.s-elemento--hecho .s-elemento__icono { background: var(--s-ok-fondo); color: var(--s-ok); }

.s-elemento__cuerpo   { min-width: 0; display: flex; flex-direction: column; gap: var(--s-e-2); }
.s-elemento__titulo   { margin: 0; font-weight: var(--s-peso-fuerte); text-wrap: pretty; }
.s-elemento__opcional {
  margin-inline-start: var(--s-e-2);
  padding: 1px var(--s-e-2);
  border-radius: var(--s-r-plena);
  background: var(--s-relleno);
  font-size: var(--s-t-xs);
  font-weight: var(--s-peso-medio);
  color: var(--s-tenue);
}
.s-elemento__texto { margin: 0; font-size: var(--s-t-sm); color: var(--s-tinta-2);
                     line-height: var(--s-interlinea-normal); text-wrap: pretty; }
.s-elemento__meta  { margin: 0; font-size: var(--s-t-xs); color: var(--s-tenue); }
.s-elemento__nota  { margin: 0; font-size: var(--s-t-sm); color: var(--s-tenue); text-wrap: pretty; }
.s-elemento__acciones { display: flex; flex-wrap: wrap; gap: var(--s-e-2); margin: 0; }

/*
 * La acción arranca en la segunda columna, debajo del cuerpo, y no al costado.
 *
 * Al costado obligaba a que el cuerpo midiera menos, y el cuerpo de un elemento
 * de video ES un reproductor de 16:9. Con la acción abajo, el reproductor usa
 * todo el ancho del panel y el botón queda donde se lo busca: después de haber
 * leído de qué se trata.
 */
.s-elemento__accion {
  grid-column: 2;
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-e-3);
}

/* ── El botón con compuerta ─────────────────────────────────────────────── */

.s-marcar        { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-e-3); }
.s-marcar__estado { font-size: var(--s-t-sm); color: var(--s-tenue); text-wrap: pretty; }
.s-marcar[data-listo='true'] .s-marcar__estado { color: var(--s-ok); font-weight: var(--s-peso-medio); }

/* ── La foto ────────────────────────────────────────────────────────────── */

.s-foto {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--s-e-5);
  align-items: start;
  padding: var(--s-e-4);
  border: 1px solid var(--s-borde-suave);
  border-radius: var(--s-r-md);
  background: var(--s-superficie-2);
}
.s-foto__actual   { display: flex; flex-direction: column; align-items: center; gap: var(--s-e-2); }
.s-foto__retrato  { flex: none; }
.s-foto__retrato img { border-radius: var(--s-r-plena); display: block; }
.s-foto__leyenda  { margin: 0; font-size: var(--s-t-xs); color: var(--s-tenue); text-align: center; max-inline-size: 14ch; }

.s-foto__acciones { display: flex; flex-direction: column; gap: var(--s-e-4); min-width: 0; }
.s-foto__forma    { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-e-2); }
.s-foto__campo    { display: flex; flex-direction: column; gap: var(--s-e-1); inline-size: 100%; }
.s-foto__campo-eti { font-size: var(--s-t-sm); font-weight: var(--s-peso-fuerte); }

/*
 * El campo de archivo se deja con su apariencia NATIVA.
 *
 * Disfrazarlo de botón es la costumbre y cuesta caro: se pierde el nombre del
 * archivo elegido —que es la única confirmación de que se eligió el correcto—
 * y hay que reponer a mano el foco de teclado y el anuncio del lector de
 * pantalla. Lo que sí se toca es el objetivo táctil, que por defecto queda por
 * debajo de los 44 px.
 */
.s-foto__entrada {
  min-block-size: var(--s-tactil);
  font-family: inherit;
  font-size: var(--s-t-sm);
  color: var(--s-tinta-2);
}
.s-foto__entrada:focus-visible { outline: 2px solid var(--s-accento); outline-offset: 2px; }
.s-foto__limite { margin: 0; font-size: var(--s-t-xs); color: var(--s-tenue); text-wrap: pretty; }

.s-foto__alternativa {
  padding-block-start: var(--s-e-4);
  border-block-start: 1px solid var(--s-borde-suave);
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-e-2);
}
.s-foto__alternativa-texto { margin: 0; font-size: var(--s-t-sm); color: var(--s-tinta-2); text-wrap: pretty; }

/* ── La reunión ─────────────────────────────────────────────────────────── */

.s-reunion {
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-e-2);
  padding: var(--s-e-4);
  border: 1px solid var(--s-borde-suave);
  border-radius: var(--s-r-md);
  background: var(--s-superficie-2);
}
.s-reunion__cuando { display: flex; align-items: center; gap: var(--s-e-2); margin: 0; font-weight: var(--s-peso-fuerte); }
.s-reunion__quien  { margin: 0; font-size: var(--s-t-sm); color: var(--s-tenue); }
.s-reunion__estado { margin: 0; }

/* ── Angosto: todo a una columna ────────────────────────────────────────── */

@media (max-width: 640px) {
  /*
   * ── En el teléfono la cabecera NO se queda pegada ──────────────────────────
   *
   * Apilada son tres renglones —marca, avance y salida— y en una pantalla de
   * 812 px eso es un tercio del alto, fijo, encima del contenido. Una cabecera
   * pegajosa que se come un tercio de la pantalla deja de ser orientación y
   * pasa a ser estorbo.
   *
   * No se pierde nada: el «llevas 5 de 6 etapas» está escrito en el titular,
   * dos dedos más abajo. Lo que en el escritorio dice la barra fija, acá lo
   * dice el texto — y el texto lo lee también quien no ve la barra.
   */
  .s-bien__cabecera { position: static; grid-template-columns: 1fr; gap: var(--s-e-2); }
  .s-bien__barra    { inline-size: 100%; }
  /* Sin esto el botón se estira de borde a borde y parece la acción principal
     de la pantalla, que es justo lo que no es. */
  .s-bien__salir    { justify-self: start; }
  .s-etapa__cabecera,
  .s-etapa__estado,
  .s-etapa__items   { padding-inline: var(--s-e-4); }
  /*
   * El saludo baja un escalón en el teléfono.
   *
   * El titular usa `clamp()` y a 375 px toca su mínimo, que es el mismo tamaño
   * que el saludo. Con los dos iguales, «Hola, Luis» y «Te estábamos esperando»
   * compiten y no se ve cuál es el titular. En el escritorio no pasa porque el
   * titular crece con el ancho.
   */
  .s-apertura__nombre { font-size: var(--s-t-xl); }
  .s-elemento       { grid-template-columns: 1fr; }
  .s-elemento__icono { display: none; }
  .s-elemento__accion { grid-column: 1; }
  .s-foto           { grid-template-columns: 1fr; }
}

/* ══════════════════════════════════════════════════════════════════════════
   MENOS MOVIMIENTO
   ══════════════════════════════════════════════════════════════════════════

   Nada de esto es negociable con quien lo pidió. Para personas con trastornos
   vestibulares el movimiento produce náusea real: no es una preferencia.

   Lo importante es que apagarlo NO quita información. El hito actual sigue
   marcado con su halo y con su tarjeta resaltada; el estado de cada etapa
   sigue escrito con palabras; el eje sigue llenándose, sólo que sin transición.
   Si algo de esto sólo se supiera por el movimiento, esta sección estaría
   escondiendo datos en vez de respetar una preferencia.
   ══════════════════════════════════════════════════════════════════════════ */

@media (prefers-reduced-motion: reduce) {
  .s-apertura__caja,
  .s-camino__hito--actual .s-camino__medallon,
  .s-camino__hito--festeja .s-camino__medallon::after {
    animation: none !important;
  }
  .s-apertura__caja { opacity: 1; transform: none; }
  .s-camino::after  { transition: none; }
  /* El halo del hito actual se mantiene fijo: es lo que dice «estás acá». */
  .s-camino__hito--actual .s-camino__medallon { box-shadow: var(--s-halo); }
}

/* ── La vidriera de premios ─────────────────────────────────────────────── */
/*
 * El emoji del premio, grande, cuando no hay fotografía. Ver el comentario de
 * la migración `AddRewardEmoji`: un catálogo vive semanas sin fotos, y diez
 * tarjetas con el mismo icono de regalo no son una vidriera.
 */
.s-premio__medio {
  display: grid;
  place-items: center;
  font-size: 3.5rem;
  line-height: 1;
  background: linear-gradient(135deg,
              var(--s-accento-suave),
              color-mix(in srgb, var(--s-accento-suave) 35%, var(--s-superficie-2)));
}

/*
 * Un emoji delante de un titular necesita su propio aire.
 *
 * El espacio del marcado está, pero el glifo trae su ancho de avance pegado al
 * dibujo y el titular lleva interletrado NEGATIVO — así que «🎁Canjea» se leía
 * junto. Media letra de margen lo resuelve sin tocar el texto.
 */
.s-marca-bloque__titulo > span[aria-hidden],
.s-marca-bloque__nota > span[aria-hidden] { margin-inline-end: .18em; }

/* ══════════════════════════════════════════════════════════════════════════
   ADMINISTRACIÓN
   ══════════════════════════════════════════════════════════════════════════

   Las piezas que faltaban para retirar `admin.css` y el sistema heredado. No
   son un tema aparte: administración usa los mismos tokens, la misma
   tipografía y los mismos botones que el resto. Lo que tiene de propio es que
   trabaja con TABLAS largas y FORMULARIOS anchos, y eso es lo que vive acá. */

/* ── Rejilla de formulario ──────────────────────────────────────────────── */
/*
 * Dos columnas en escritorio, una en teléfono. `auto-fit` y no un número fijo:
 * un formulario de tres campos no tiene por qué dejar un hueco.
 */
.s-form-rejilla {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
  gap: var(--s-e-2) var(--s-e-4);
}
/* El campo que necesita el ancho entero: una dirección, una descripción. */
.s-campo--ancho { grid-column: 1 / -1; }

/* ── Acciones de una fila de tabla ──────────────────────────────────────── */
.s-fila-acciones { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-e-2); }
/*
 * Un enlace de acción dentro de una tabla, no un botón.
 *
 * En una tabla de treinta filas, treinta botones con borde compiten con el
 * dato — que es lo que la persona vino a leer. El enlace se distingue por el
 * color de marca y se agranda al pasar por encima.
 */
.s-fila-accion {
  color: var(--s-accento-texto);
  font-size: var(--s-t-sm);
  font-weight: var(--s-peso-medio);
  text-decoration: none;
  white-space: nowrap;
}
.s-fila-accion:hover { text-decoration: underline; }
.s-fila-accion--peligro { color: var(--s-peligro); }

/* ── La tabla que se apila en teléfono ──────────────────────────────────── */
/*
 * ┌─ Por qué apilar y no sólo desplazar ──────────────────────────────────────┐
 *
 * Una tabla de administración tiene seis o siete columnas. En un teléfono eso
 * es un desplazamiento horizontal de tres pantallas donde la primera columna
 * —la que dice DE QUIÉN es la fila— se pierde apenas se empieza a mover.
 *
 * Apilada, cada fila es una ficha y cada celda lleva su rótulo delante, que
 * sale del `data-label` del propio marcado. La cabecera se oculta porque cada
 * celda ya la lleva puesta.
 *
 * ⚠️ Toda celda necesita su `data-label` menos la primera, que hace de título
 * de la ficha. Sin él, en teléfono se ve un valor sin saber de qué es.
 *
 * └──────────────────────────────────────────────────────────────────────────┘
 */
@media (max-width: 720px) {
  .s-tabla--apilable thead { display: none; }
  .s-tabla--apilable,
  .s-tabla--apilable tbody { display: block; }

  .s-tabla--apilable tr {
    display: block;
    margin-block-end: var(--s-e-3);
    padding: var(--s-e-3) var(--s-e-4);
    border: 1px solid var(--s-borde-suave);
    border-radius: var(--s-r-md);
    background: var(--s-superficie);
  }
  .s-tabla--apilable tbody tr + tr td { border-top: 0; }

  .s-tabla--apilable td {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-e-3);
    padding: var(--s-e-2) 0;
    border: 0;
    text-align: end;
  }
  .s-tabla--apilable td::before {
    content: attr(data-label);
    flex: 0 0 auto;
    font-size: var(--s-t-xs);
    font-weight: var(--s-peso-fuerte);
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--s-tenue);
    text-align: start;
  }
  /* La primera celda es el título de la ficha: sin rótulo y con una línea que
     la separa del resto. */
  .s-tabla--apilable td:first-child {
    display: block;
    margin-block-end: var(--s-e-1);
    padding-block-end: var(--s-e-2);
    border-block-end: 1px solid var(--s-borde-suave);
    text-align: start;
    font-weight: var(--s-peso-fuerte);
  }
  .s-tabla--apilable td:first-child::before { display: none; }
}

/* ── Contador de bytes de un campo largo ─────────────────────────────────── */
.s-contador {
  display: block;
  margin-block-start: var(--s-e-1);
  font-size: var(--s-t-xs);
  color: var(--s-tenue);
  font-variant-numeric: tabular-nums;
}
.s-contador--excedido { color: var(--s-peligro); font-weight: var(--s-peso-fuerte); }

/* ── Separador «o» entre dos caminos ─────────────────────────────────────── */
/*
 * Dos botones apilados sin nada en medio se leen como dos PASOS —primero uno,
 * después el otro—. La palabra en medio de la línea es lo que los convierte en
 * dos caminos alternativos.
 *
 * La línea se dibuja con un `::before` y un `::after` que crecen a los lados,
 * y no con un borde sobre el `<p>`: un borde pasaría POR DEBAJO del texto y
 * habría que taparlo pintando el fondo detrás de la palabra — que se rompe en
 * cuanto la tarjeta tiene un fondo distinto del de la página, o en modo oscuro.
 */
.s-o {
  display: flex;
  align-items: center;
  gap: var(--s-e-3);
  margin-block: var(--s-e-4);
  color: var(--s-tenue);
  font-size: var(--s-t-sm);
}
.s-o::before,
.s-o::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--s-borde);
}

/* ═══════════════════════════════════════════════════════════════════════════
   CONSOLA DE PARAMETRIZACIÓN
   ═══════════════════════════════════════════════════════════════════════════

   Noventa y un ajustes en una pantalla, y el problema de diseño no es el
   espacio: es la JERARQUÍA. Todos los ajustes ocupan lo mismo y no todos pesan
   lo mismo — cambiar un color es cosmético y cambiar el marco legal de un banco
   no lo es.

   La franja de color a la izquierda de cada tarjeta es lo que sostiene esa
   diferencia mientras se recorre la lista. NO es lo único que la dice: cada
   ajuste lleva además la palabra en su distintivo, porque un tinte no comunica
   nada a quien no lo distingue, y porque acá la distinción es el dato
   principal. El color acelera la lectura; la palabra la garantiza.
   ═══════════════════════════════════════════════════════════════════════════ */

.s-ajustes__buscador { display: flex; flex-direction: column; gap: var(--s-e-2); }

/* Las pestañas de grupo son enlaces y no botones: cada grupo es una dirección
   propia, así que se puede compartir, marcar y abrir en otra pestaña. Un
   conmutador de JavaScript perdería las tres cosas a cambio de nada. */
.s-ajustes__grupos .s-pestana {
  display: inline-flex;
  align-items: center;
  gap: var(--s-e-2);
  text-decoration: none;
}
.s-ajustes__cuenta {
  min-width: 1.6em;
  padding-inline: var(--s-e-1);
  border-radius: var(--s-r-plena);
  background: var(--s-relleno);
  color: var(--s-tenue);
  font-size: var(--s-t-xs);
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.s-ajustes__grupos .s-pestana[aria-selected='true'] .s-ajustes__cuenta {
  background: var(--s-accento-suave);
  color: var(--s-accento-texto);
}

.s-ajustes__lista { display: flex; flex-direction: column; gap: var(--s-e-5); }
.s-ajustes__pie {
  position: sticky;
  /* Pegado abajo: la lista de un grupo mide varias pantallas, y un botón de
     guardar que hay que ir a buscar al final es un botón que se olvida a la
     mitad. */
  bottom: 0;
  margin-top: var(--s-e-5);
  padding-top: var(--s-e-4);
  border-top: 1px solid var(--s-borde);
  background: var(--s-superficie);
}

/* ── Un ajuste ───────────────────────────────────────────────────────────── */
.s-ajuste {
  padding-left: var(--s-e-4);
  border-left: 3px solid var(--s-borde);
}
.s-ajuste--operativo { border-left-color: var(--s-info); }
.s-ajuste--critico   { border-left-color: var(--s-peligro); }
.s-ajuste--sellado   { border-left-color: var(--s-aviso); }

.s-ajuste__cabeza {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-e-2);
  margin-bottom: var(--s-e-1);
}

/* La clave técnica ES el rótulo del campo, y va en monoespaciada.
   Es el nombre con el que este ajuste aparece en el código (`ajuste('...')`),
   en la auditoría y en el archivo de exportación. Traducirlo a un título bonito
   obligaría a inventar noventa y un nombres en tres idiomas y, peor, rompería
   la correspondencia justo para quien está diagnosticando algo. */
.s-ajuste__clave {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: var(--s-t-sm);
  font-weight: var(--s-peso-fuerte);
  color: var(--s-tinta);
  word-break: break-all;
}
.s-ajuste__historial {
  display: inline-flex;
  align-items: center;
  gap: var(--s-e-1);
  margin-left: auto;
  color: var(--s-tenue);
  font-size: var(--s-t-xs);
  text-decoration: none;
}
.s-ajuste__historial:hover { color: var(--s-accento); }
.s-ajuste__historial .s-icono { width: 14px; height: 14px; }

.s-ajuste__que  { margin-bottom: var(--s-e-3); color: var(--s-tinta-2); font-size: var(--s-t-sm); }
.s-ajuste__cuerpo { margin-bottom: var(--s-e-2); }

.s-ajuste__ayuda { border: 0; background: transparent; }
.s-ajuste__ayuda > summary { min-height: 0; padding: var(--s-e-1) 0; font-weight: var(--s-peso-normal); }
.s-ajuste__resumen { color: var(--s-tenue); font-size: var(--s-t-xs); }
.s-ajuste__ayuda .s-acordeon__cuerpo { padding: var(--s-e-2) 0 var(--s-e-3); }
.s-ajuste__consecuencia { margin-bottom: var(--s-e-2); color: var(--s-tinta-2); font-size: var(--s-t-sm); }
.s-ajuste__ejemplo,
.s-ajuste__defecto,
.s-ajuste__solo-lectura {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s-e-2);
  margin-bottom: var(--s-e-2);
  font-size: var(--s-t-xs);
}
.s-ajuste__rotulo { color: var(--s-tenue); text-transform: uppercase; letter-spacing: .04em; }

/* ── Lo que no se puede editar ───────────────────────────────────────────── */
/* Fondo de aviso y no de peligro: no hay nada roto. Es una puerta cerrada a
   propósito, y el rojo la haría parecer una falla. */
.s-ajuste__bloqueado {
  padding: var(--s-e-3) var(--s-e-4);
  border-radius: var(--s-r-md);
  background: var(--s-aviso-fondo);
}
.s-ajuste__bloqueado-titulo {
  display: flex;
  align-items: center;
  gap: var(--s-e-2);
  margin-bottom: var(--s-e-1);
  color: var(--s-aviso);
  font-weight: var(--s-peso-fuerte);
  font-size: var(--s-t-sm);
}
.s-ajuste__bloqueado-texto { color: var(--s-tinta-2); font-size: var(--s-t-sm); }

/* ── El antes y el después ───────────────────────────────────────────────── */
.s-ajuste__diff { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--s-e-2); }
.s-ajuste__diff-de { color: var(--s-tenue); text-decoration: line-through; }
.s-ajuste__diff-a  { color: var(--s-tinta); font-weight: var(--s-peso-fuerte); }

/* ── Campos por tipo ─────────────────────────────────────────────────────── */
.s-entrada--codigo { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.s-entrada--numero { max-width: 10rem; }

.s-color-campo { display: flex; align-items: center; gap: var(--s-e-2); }
/* El selector nativo es EL ATAJO, no el control: no recibe foco de teclado
   (`tabindex="-1"`) porque el campo de texto de al lado hace exactamente lo
   mismo y además admite pegar un código. Dos paradas de tabulación para un solo
   valor obligan a pasar dos veces por cada uno de los treinta y tres colores. */
.s-color-muestra {
  flex: none;
  width: var(--s-tactil);
  height: var(--s-tactil);
  padding: 2px;
  border: 1px solid var(--s-borde);
  border-radius: var(--s-r-sm);
  background: var(--s-superficie);
  cursor: pointer;
}
.s-color-campo .s-entrada { max-width: 11rem; text-transform: uppercase; }

/* ── Vista previa de la marca ────────────────────────────────────────────── */
.s-marca-previa__fila {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-e-3);
  margin-bottom: var(--s-e-4);
}
.s-marca-previa__rampa { display: flex; border-radius: var(--s-r-sm); overflow: hidden; }
.s-marca-previa__escalon { flex: 1; height: var(--s-e-6); }
/* Los once escalones salen de los tokens de `marca.css`, o sea de la hoja
   COMPILADA y no de los campos del formulario. Es lo que hace que la muestra
   pueda contestar «¿se recompiló?» en vez de repetir lo que ya dice el
   formulario, que siempre coincide consigo mismo. */
.s-marca-previa__escalon--0   { background: var(--marca-neutro-0); }
.s-marca-previa__escalon--50  { background: var(--marca-neutro-50); }
.s-marca-previa__escalon--100 { background: var(--marca-neutro-100); }
.s-marca-previa__escalon--200 { background: var(--marca-neutro-200); }
.s-marca-previa__escalon--300 { background: var(--marca-neutro-300); }
.s-marca-previa__escalon--400 { background: var(--marca-neutro-400); }
.s-marca-previa__escalon--500 { background: var(--marca-neutro-500); }
.s-marca-previa__escalon--600 { background: var(--marca-neutro-600); }
.s-marca-previa__escalon--700 { background: var(--marca-neutro-700); }
.s-marca-previa__escalon--800 { background: var(--marca-neutro-800); }
.s-marca-previa__escalon--900 { background: var(--marca-neutro-900); }

/* ══════════════════════════════════════════════════════════════════════════
   CONSOLA DE ANALÍTICA
   ══════════════════════════════════════════════════════════════════════════

   ┌─ Ni un color nuevo, ni un hex ────────────────────────────────────────────┐

   Todo lo que se ve acá sale de los tokens `--s-*` que ya existen, y esos
   salen de `marca.css`, que se compila desde la tabla de parametrización. Un
   gráfico con su propia paleta se ve bien en la instalación donde se escribió
   y pelea con la marca de todas las demás — que es el problema que los tokens
   vinieron a resolver.

   Las tres series usan `--s-accento`, `--s-ok` y `--s-info`. Son las tres que
   ya se distinguen entre sí en cualquier paleta de cliente, porque el sistema
   las define para eso.

   └───────────────────────────────────────────────────────────────────────────┘

   ┌─ El color NO es la única señal de cada serie ─────────────────────────────┐

   Cada línea lleva además su propio patrón de trazo: continua, guionada y
   punteada. Con ocho por ciento de los hombres sin visión tricromática normal,
   un gráfico que sólo se distingue por color es un gráfico que una de cada
   doce personas no puede leer — y encima se imprime en blanco y negro para
   llevarlo a una reunión.

   └───────────────────────────────────────────────────────────────────────────┘ */

/* ── Pestañas y período ──────────────────────────────────────────────────── */
.s-analitica__secciones { margin-bottom: var(--s-e-2); }

.s-analitica__rango {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-e-3);
}
.s-analitica__rango-eti {
  color: var(--s-tenue);
  font-size: var(--s-t-sm);
  font-weight: var(--s-peso-medio);
}
.s-analitica__rango-opciones { display: flex; flex-wrap: wrap; gap: var(--s-e-2); }

/* El período elegido es un ENLACE con `aria-current`, no un botón con estado:
   cada período vive en su propia dirección para que la descarga de CSV pueda
   llevar el mismo `?rango=` y bajar exactamente lo que se está mirando. */
.s-chip--activo {
  background: var(--s-accento);
  border-color: var(--s-accento-texto);
  color: var(--s-sobre-accento);
  font-weight: var(--s-peso-fuerte);
}

/* ── Tarjeta de indicador ────────────────────────────────────────────────── */
.s-analitica__kpi {
  display: flex;
  flex-direction: column;
  gap: var(--s-e-2);
  align-items: flex-start;
  text-align: left;
}
.s-analitica__kpi .s-kpi__numero { font-variant-numeric: tabular-nums; }

/* Los tonos: informan de un vistazo y NUNCA solos. La palabra —«Vencido»,
   «Por vencer»— está siempre escrita al lado, porque un borde de color no lo
   lee un lector de pantalla ni sobrevive a una impresión. */
.s-analitica__kpi--ok      { border-inline-start: 3px solid var(--s-ok); }
.s-analitica__kpi--aviso   { border-inline-start: 3px solid var(--s-aviso); }
.s-analitica__kpi--peligro { border-inline-start: 3px solid var(--s-peligro); }
.s-analitica__kpi--neutra  { border-inline-start: 3px solid var(--s-borde); }

/* La ayuda de cada indicador: dice CÓMO SE CUENTA, que es lo único que lo hace
   comparable consigo mismo el mes que viene. Va siempre visible y no en una
   burbuja al pasar el ratón: una burbuja no existe en una pantalla táctil. */
.s-analitica__ayuda {
  color: var(--s-tenue);
  font-size: var(--s-t-sm);
  line-height: var(--s-interlinea-normal);
  max-width: var(--s-lectura-max);
}
.s-analitica__salvedad { margin-inline-start: var(--s-e-2); }

/* ── Tendencia ───────────────────────────────────────────────────────────── */
.s-tendencia {
  display: inline-flex;
  align-items: center;
  gap: var(--s-e-1);
  font-size: var(--s-t-sm);
  font-weight: var(--s-peso-medio);
}
.s-tendencia--sube   { color: var(--s-ok); }
.s-tendencia--baja   { color: var(--s-peligro); }
.s-tendencia--igual,
.s-tendencia--neutra { color: var(--s-tenue); }
.s-tendencia__flecha { display: inline-flex; }
.s-tendencia__flecha .s-icono { width: 1rem; height: 1rem; }

/* La MISMA flecha para los dos sentidos, volteada. Dos iconos serían dos
   dibujos que hay que mantener iguales para siempre. */
.s-tendencia--baja .s-tendencia__flecha .s-icono { transform: scaleY(-1); }

.s-tendencia__antes { color: var(--s-tenue); font-size: var(--s-t-xs); }

/* ── Chispa ──────────────────────────────────────────────────────────────── */
/* Una línea diminuta que muestra la FORMA, no el dato. El dato es el número
   grande de arriba y la comparación numérica de al lado. Por eso lleva
   `aria-hidden`: no hay nada acá que no esté ya escrito. */
.s-chispa { width: 100%; height: 28px; overflow: visible; }
.s-chispa__linea {
  fill: none;
  stroke: var(--s-accento-texto);
  stroke-width: 1.5;
  stroke-linejoin: round;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}

/* ── Embudo ──────────────────────────────────────────────────────────────── */
.s-embudo { display: flex; flex-direction: column; gap: var(--s-e-4); list-style: none; padding: 0; margin: 0; }
.s-embudo__paso { display: flex; flex-direction: column; gap: var(--s-e-2); }
.s-embudo__cabeza {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-e-2);
}
.s-embudo__nombre { font-weight: var(--s-peso-medio); }
.s-embudo__cifra  { display: inline-flex; align-items: baseline; gap: var(--s-e-2); font-variant-numeric: tabular-nums; }
.s-embudo__pct    { color: var(--s-tenue); font-size: var(--s-t-sm); }

.s-embudo__barra {
  height: var(--s-e-5);
  border-radius: var(--s-r-sm);
  background: var(--s-relleno);
  overflow: hidden;
}
/* `--pct` es el único estilo en línea que el proyecto acepta: un valor
   calculado en el servidor no puede ser una clase. Está declarado como tal en
   `tools/tokens-declarados.php`. */
.s-embudo__relleno {
  display: block;
  height: 100%;
  width: var(--pct, 0%);
  background: var(--s-accento);
  border-radius: inherit;
  transition: width var(--s-mov-lento) var(--s-curva);
}
.s-embudo__caida { color: var(--s-aviso); font-size: var(--s-t-sm); }

@media (prefers-reduced-motion: reduce) {
  .s-embudo__relleno { transition: none; }
}

/* ── Gráfico de líneas ───────────────────────────────────────────────────── */
.s-grafico { width: 100%; overflow-x: auto; }
.s-grafico__lienzo { width: 100%; height: auto; min-width: 480px; display: block; }

.s-grafico__leyenda {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-e-4);
  list-style: none;
  padding: 0;
  margin: 0 0 var(--s-e-3);
  font-size: var(--s-t-sm);
}
.s-grafico__clave { display: inline-flex; align-items: center; gap: var(--s-e-2); }
.s-grafico__muestra { width: var(--s-e-4); height: 3px; border-radius: var(--s-r-plena); }

.s-grafico__guia { stroke: var(--s-borde-suave); stroke-width: 1; }
.s-grafico__eti  { fill: var(--s-tenue); font-size: 11px; font-family: var(--s-fuente-cuerpo); }

.s-grafico__linea {
  fill: none;
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
.s-grafico__area { stroke: none; opacity: .12; }

.s-grafico__linea--inscripciones,
.s-grafico__muestra--inscripciones { stroke: var(--s-accento); background: var(--s-accento); }
.s-grafico__area--inscripciones    { fill: var(--s-accento); }

/* Guionada: la segunda señal, además del color. Ver el encabezado del bloque. */
.s-grafico__linea--certificaciones { stroke: var(--s-ok); stroke-dasharray: 6 4; }
.s-grafico__muestra--certificaciones { background: var(--s-ok); }
.s-grafico__area--certificaciones  { fill: var(--s-ok); }

.s-grafico__linea--actividad       { stroke: var(--s-info); stroke-dasharray: 2 3; }
.s-grafico__muestra--actividad     { background: var(--s-info); }
.s-grafico__area--actividad        { fill: var(--s-info); }

/* ── Barra dentro de una celda de tabla ──────────────────────────────────── */
.s-barra-mini { display: flex; flex-direction: column; gap: var(--s-e-1); min-width: 6rem; }
.s-barra-mini__valor { font-variant-numeric: tabular-nums; font-weight: var(--s-peso-medio); }
.s-barra-mini__pista {
  display: block;
  height: var(--s-e-2);
  border-radius: var(--s-r-plena);
  background: var(--s-relleno);
  overflow: hidden;
}
.s-barra-mini__relleno {
  display: block;
  height: 100%;
  width: var(--pct, 0%);
  background: var(--s-accento);
  border-radius: inherit;
}
