/* ═══════════════════════════════════════════════════════════════════════════
   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);
  --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 ──────────────────────────────────────────────────────────── */
  --s-ok:            var(--marca-ok);
  --s-ok-fondo:      light-dark(var(--marca-ok-suave),      color-mix(in srgb, var(--marca-ok) 20%, transparent));
  --s-aviso:         var(--marca-aviso);
  --s-aviso-fondo:   light-dark(var(--marca-aviso-suave),   color-mix(in srgb, var(--marca-aviso) 20%, transparent));
  --s-peligro:       var(--marca-peligro);
  --s-peligro-fondo: light-dark(var(--marca-peligro-suave), color-mix(in srgb, var(--marca-peligro) 20%, transparent));
  --s-info:          var(--marca-info);
  --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 ─────────────────────────────────────────── */
  --s-barra-alto:      76px;
  --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; }

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);
  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;
}

/* ── 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 y la
 * sombra que el blanco solo no da: la jerarquía la sostiene la elevación, no
 * el color.
 */
.s-barra {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: var(--s-e-3);
  height: var(--s-barra-alto);
  padding-inline: var(--s-e-4);
  background: var(--s-superficie);
  border-bottom: 1px solid var(--s-borde);
  box-shadow: var(--s-sombra-sm);
}

.s-barra__marca { display: flex; align-items: center; gap: var(--s-e-3); min-width: 0; }
.s-barra__logo  { height: 42px; width: auto; }
/*
 * El descriptor va en dos renglones a la izquierda del logotipo, como en el
 * prototipo. `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-size: var(--s-t-xs);
  font-weight: var(--s-peso-fuerte);
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--s-tenue);
  line-height: 1.3;
  max-width: 12ch;
  text-align: end;
}
.s-barra__buscador { flex: 1; max-width: 460px; margin-inline: auto; display: none; }
.s-barra__acciones { display: flex; align-items: center; gap: var(--s-e-1); margin-inline-start: auto; }

/* ── 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;
  width: min(var(--s-lateral-ancho), 84vw);
  padding: var(--s-e-4) var(--s-e-3);
  background: var(--s-superficie);
  border-inline-end: 1px solid var(--s-borde);
  overflow-y: auto;
  overscroll-behavior: contain;
  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; }

.s-nav       { display: flex; flex-direction: column; gap: 2px; }
.s-nav__grupo{ margin-top: var(--s-e-5); }
.s-nav__grupo:first-child { margin-top: 0; }
.s-nav__titulo {
  font-size: var(--s-t-xs);
  font-weight: var(--s-peso-fuerte);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--s-apagado);
  padding: var(--s-e-2) var(--s-e-3);
}

.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-md);
  font-weight: var(--s-peso-medio);
  text-decoration: none;
  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: color y una barra al costado.
 * 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-suave);
  color: var(--s-accento-fuerte);
  font-weight: var(--s-peso-fuerte);
  box-shadow: inset 3px 0 0 var(--s-accento);
}
.s-nav__icono { flex: none; width: 20px; height: 20px; }

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

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

.s-encabezado { margin-bottom: var(--s-e-5); }
.s-encabezado__fila { display: flex; flex-wrap: wrap; align-items: flex-start;
                      justify-content: space-between; gap: var(--s-e-4); }

/* ── Pie ────────────────────────────────────────────────────────────────── */
/*
 * Tres funciones, ninguna decorativa: decir de quién es la plataforma, dejar a
 * mano lo que se necesita desde cualquier pantalla, y mostrar la versión.
 * La versión importa: cuando alguien reporta «no me anda», lo primero que hace
 * falta saber es qué está viendo. Sin eso la respuesta siempre es «recargá».
 */
.s-pie {
  padding: var(--s-e-5) var(--s-e-4);
  background: var(--s-superficie);
  border-top: 1px solid var(--s-borde);
  font-size: var(--s-t-sm);
  color: var(--s-tenue);
}
.s-pie__enlaces { display: flex; flex-wrap: wrap; gap: var(--s-e-4); margin-bottom: var(--s-e-3); }
.s-pie__version { font-variant-numeric: tabular-nums; color: var(--s-apagado); }

/* ── Rejillas ───────────────────────────────────────────────────────────── */
/*
 * `auto-fit` con `minmax` 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.
 */
.s-rejilla       { display: grid; gap: var(--s-e-4); }
.s-rejilla--auto { grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr)); }
.s-rejilla--ancha{ grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr)); }
.s-rejilla--kpi  { grid-template-columns: repeat(auto-fit, 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); }

@media (min-width: 480px) {
  .s-principal { padding: var(--s-e-5); }
}

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

@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; }
  .s-principal { grid-area: principal; padding: var(--s-e-6); }
  .s-pie       { grid-area: pie; }
  .s-lateral__velo,
  .s-barra__menu { display: none; }
}

/* ───────────────────────────────────────────────────────────────────────────
   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); }

/* ── 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; }

/* ───────────────────────────────────────────────────────────────────────────
   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.
 */
.s-portal { display: grid; min-height: 100svh; }
.s-portal__imagen { display: none; position: relative; overflow: hidden; }
.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
   ─────────────────────────────────────────────────────────────────────────── */

/*
 * «Continuar donde quedaste» ocupa el lugar del héroe 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.
 */
.s-retomar, .s-hero {
  display: flex;
  flex-direction: column;
  gap: var(--s-e-4);
  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);
  text-decoration: none;
}
.s-retomar__titulo, .s-hero__titulo {
  color: var(--s-sobre-accento);
  font-size: var(--s-t-2xl);
  margin: 0;
  text-wrap: balance;
}
.s-hero__texto { opacity: .92; margin: 0; max-width: 52ch; }

/* Existe porque el rótulo hereda `--s-tenue`, que sobre la superficie de marca
   queda ilegible. Estaba escrito como estilo en línea hasta que
   `tools/sin-color-en-linea.php` lo marcó en su primera corrida: aunque usaba
   un token y no un literal, seguía siendo una vista decidiendo color. */
.s-retomar__rotulo { color: var(--s-sobre-accento); opacity: .82; }

.s-retomar:hover { color: var(--s-sobre-accento); box-shadow: var(--s-sombra-md); }

/* 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 {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    padding: var(--s-e-6);
  }
  .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); }
/* 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);
  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.
 */
.s-medallones {
  display: grid;
  gap: var(--s-e-3);
  grid-template-columns: repeat(auto-fill, minmax(min(150px, 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; }
.s-medallon__figura {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  font-size: var(--s-t-2xl);
  line-height: 1;
}
/* `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); }
