/*
  Interfaz del estudiante (§16) — bloque 11.

  Derivado de demo-asesores-ia.html en paleta y orbes, reencuadrado sobre la maqueta
  de referencia aprobada: oscuro con dorado, tres columnas y barra lateral con
  buscador e historial.

  Quitado a propósito respecto al demo (ver plan de construcción):
    - .armazon / .kajabi: barra lateral simulada de Kajabi. En producción Kajabi
      pinta su propia barra fuera del iframe; esta app empieza en .app.
    - .zona-top / alternarVista(): toggle de previsualización, solo existía para
      comparar los dos estados dentro de la misma demo.
    - .agentes-barra / .chip-agente / .conmutador / "mesa redonda": selector de
      agente manual. El documento final (§11) decidió que el estudiante solo ve a
      ARIA; es ella quien decide a qué especialista consultar.

  De la maqueta de referencia se descartaron el selector de modelo y los conmutadores
  "Web search / Research": el estudiante no elige modelo ni modo de búsqueda (§11).
*/

/* ============ TOKENS INVARIANTES AL TEMA ============ */
/*
  Lo que vale igual en oscuro y en claro. Tres familias:

  · Tipografía, pesos, radios y anchos — no son color.
  · La paleta de los orbes. Un orbe es un objeto luminoso, no cromo de interfaz: una
    esfera con núcleo oscuro y halo de color se lee igual sobre blanco que sobre negro,
    y es la identidad de ARIA y de cada especialista (§16). Se tokeniza para que ningún
    literal quede suelto, no para tematizarla.
  · El oro y los semánticos **en su papel de superficie** (relleno, borde, vela de
    gráfica). Su papel de *texto* vive abajo, tematizado: `--oro` sobre blanco da 2.0:1
    y sería ilegible.
*/
:root{
  /* Oro de marca — rellenos, bordes, aros de foco */
  --oro:#D9A441;
  --oro-vivo:#F0A32E;
  --oro-suave:#EBD6A8;
  --oro-hondo:#C07E24;
  /* El degradado de los botones principales estaba copiado literalmente en cuatro
     componentes (.enviar, .confirmar-dictado, el submit del acceso y .dialogo-pie
     .principal). Una sola definición o las cuatro derivan. */
  --oro-degradado:linear-gradient(160deg,var(--oro-vivo),var(--oro-hondo));

  /* Texto sobre un relleno de marca. `.fb-btn-enviar` usaba #000 donde el resto usaba
     #1A1206: la misma decisión escrita dos veces acaba en dos valores. */
  --sobre-oro:#1A1206;
  --sobre-esmeralda:#06231A;

  /* Semánticos como superficie (chips, marcas, velas de las gráficas) */
  --rojo:#F2555A;
  --esmeralda:#2ED098;
  --ambar:#F5A524;

  /* Paleta de orbes — ARIA y los especialistas. El `-2` es el borde profundo de cada
     esfera: `.a-ambar` y `.a-esmeralda` lo piden igual que las demás, aunque su color
     base viva arriba entre los semánticos. */
  --cian:#3BC9F0;   --cian-2:#1668B8;
  --violeta:#9B6DFF;--violeta-2:#4A2AA8;
  --magenta:#FF6BC4;--magenta-2:#9E2069;
  --dorado:#E8C87A; --dorado-2:#8A6B22;
  --ambar-2:#A8480B;
  --esmeralda-2:#0B6B4B;

  /* Materia del orbe: el núcleo oscuro y sus luces especulares. No cambian con el tema
     por lo dicho arriba — la esfera es la misma, se mire donde se mire. */
  --orbe-fondo:#080A0E;
  --luz-orbe:#fff;
  --brillo-orbe:rgba(255,255,255,.85);
  --aro-orbe:rgba(255,255,255,.10);
  --aro-orbe-fuerte:rgba(255,255,255,.2);

  --display:'Bricolage Grotesque',system-ui,sans-serif;
  --body:'Geist',system-ui,-apple-system,sans-serif;
  --mono:'IBM Plex Mono',ui-monospace,monospace;

  /* Escala de pesos tipográficos (Geist variable) */
  --fw-light:300;
  --fw-regular:400;
  --fw-medium:500;
  --fw-semibold:600;
  --fw-bold:700;
  --fw-extrabold:800;

  --r-s:8px; --r-m:12px; --r-l:16px; --r-xl:22px;
  --barra-ancho:268px;
  --riel-ancho:320px;
}

/*
  Los dos bloques que siguen son el tema, y se declaran **con los mismos nombres**. Un
  token de color nuevo entra en los dos o en ninguno: si solo entra en oscuro, el claro
  hereda el valor oscuro y esa esquina de la pantalla se ve mal sin que nada falle.
  `aria/conversaciones/tests/test_tema.py` compara los dos conjuntos y rompe la suite
  cuando uno se queda corto — los rótulos de sección de abajo son lo que esa prueba parsea,
  así que no son decorativos.

  Solo dos estados, no los tres de `static/panel/panel.css`: aquí el sistema operativo no
  decide qué cara tiene la marca. Oscuro es el defecto y el estudiante elige salir de él,
  así que no interviene `@media (prefers-color-scheme)` y basta la especificidad (0,2,0
  contra 0,1,0) para que el claro gane.

  `color-scheme` se declara aquí y no en un <meta>: los controles nativos, las barras de
  desplazamiento y el ::backdrop de <dialog> tienen que seguir al tema elegido, y con dos
  fuentes de verdad una se queda atrás.
*/

/* ============ TOKENS DEL TEMA — OSCURO (defecto) ============ */
:root{
  color-scheme:dark;

  --fondo:#0B0B0E;
  --panel:#0F1013;
  --panel-2:#16181C;
  --panel-3:#1C1F24;
  --linea:#212429;
  --linea-2:#2C3037;

  --texto:#ECEAE6;
  --texto-2:#9CA1AC;
  --texto-3:#686D78;
  /* El cuerpo de un mensaje se lee un punto por debajo del texto de interfaz. */
  --texto-lectura:#DFDDD8;

  /* Oro como color de texto/icono, distinto del oro de relleno. */
  --oro-texto:#D9A441;
  --oro-texto-fuerte:#EBD6A8;
  --oro-tenue:rgba(217,164,65,.12);
  --oro-borde:rgba(217,164,65,.34);

  /* Semánticos como texto, y sus tintes de fondo. Estaban escritos a mano en rgba()
     por todo el archivo: el mismo rojo en siete sitios con cuatro alfas distintas. */
  --rojo-texto:#F2555A;
  --esmeralda-texto:#2ED098;
  --ambar-texto:#F5A524;
  --rojo-tenue:rgba(242,85,90,.12);
  --rojo-pulso:rgba(242,85,90,.5);
  --esmeralda-tenue:rgba(46,208,152,.13);
  --ambar-tenue:rgba(245,165,36,.12);

  /* Sombras y velos completos, no solo su color: `rgba(0,0,0,.85)` en claro no es una
     sombra suave, es una mancha, y el desenfoque también tiene que aflojar. */
  --sombra-alta:0 30px 70px -20px rgba(0,0,0,.85);
  --sombra-media:0 18px 44px -12px rgba(0,0,0,.75);
  --sombra-baja:0 8px 24px rgba(0,0,0,.45);
  --sombra-borde:0 0 60px rgba(0,0,0,.6);
  --velo:rgba(0,0,0,.62);

  --barra-desliz:#262A31;
  --barra-desliz-fuerte:#333842;
}

/* ============ TOKENS DEL TEMA — CLARO ============ */
/*
  No es la inversión del bloque de arriba, y en dos sitios va a contracorriente a propósito:

  · `--panel-2` y `--panel-3` son rellenos y estados de hover. En oscuro un relleno *sube*
    de luminancia respecto al fondo; en claro *baja*. Invertir sus valores daría chips que
    brillan más que la página.
  · Los tokens de texto de marca y semánticos se **oscurecen**: en oscuro «más brillante»
    es más énfasis, en claro lo es «más oscuro». `--oro-texto-fuerte` es el más claro de
    los dos oros arriba y el más oscuro aquí.

  El blanco es cálido (#FAF8F5) a propósito: un blanco frío junto al dorado de la marca se
  lee como un error de calibración de pantalla.
*/
:root[data-tema="claro"]{
  color-scheme:light;

  --fondo:#FAF8F5;
  --panel:#FFFFFF;
  --panel-2:#F2EFEA;
  --panel-3:#E8E4DC;
  --linea:#E4E0D8;
  --linea-2:#CFC9BE;

  --texto:#14151A;
  --texto-2:#55595F;
  --texto-3:#7C818A;
  --texto-lectura:#1F2024;

  --oro-texto:#8A6417;
  --oro-texto-fuerte:#6B4E12;
  --oro-tenue:rgba(217,164,65,.16);
  --oro-borde:rgba(217,164,65,.42);

  --rojo-texto:#BE2A30;
  --esmeralda-texto:#0B7A56;
  --ambar-texto:#8A5A00;
  --rojo-tenue:rgba(190,42,48,.10);
  --rojo-pulso:rgba(190,42,48,.45);
  --esmeralda-tenue:rgba(11,122,86,.11);
  --ambar-tenue:rgba(138,90,0,.11);

  --sombra-alta:0 24px 60px -18px rgba(31,26,16,.20);
  --sombra-media:0 14px 34px -12px rgba(31,26,16,.16);
  --sombra-baja:0 6px 18px rgba(31,26,16,.12);
  --sombra-borde:0 0 60px rgba(31,26,16,.18);
  --velo:rgba(24,22,18,.42);

  --barra-desliz:#D6D2C9;
  --barra-desliz-fuerte:#BDB8AD;
}

*{box-sizing:border-box;margin:0;padding:0;}
html,body{height:100%;}
body{
  background:var(--fondo);color:var(--texto);
  font-family:var(--body);font-weight:var(--fw-regular);font-size:14px;line-height:1.55;
  -webkit-font-smoothing:antialiased;
}
button{font-family:inherit;color:inherit;background:none;border:0;cursor:pointer;}
input,textarea{font-family:inherit;color:inherit;}
a{color:inherit;text-decoration:none;}
:focus-visible{outline:2px solid var(--oro);outline-offset:2px;border-radius:4px;}
/* La barra de desplazamiento solo se pinta con el puntero encima del área que hace
   scroll. El hilo, la barra lateral y el riel scrollean a la vez, y tres carriles
   permanentes son cromo compitiendo con el contenido. Tres acotaciones:
   · Lo que se apaga es el color del pulgar, no la barra: `width` sigue reservando sus
     8px, así que nada se recoloca al aparecer — que es lo que hace inservible
     esconderla con `display:none`.
   · Va dentro de `@media (hover:hover)`. Sin puntero el gesto no existe, y fuera del
     media query un móvil se quedaría sin barra para siempre.
   · Basta con el puntero en la **columna**, no encima del propio carril: el que
     scrollea es el hijo (`.hilo` en `.centro`, `.convs` en `.barra`, `.riel-cuerpo` en
     `.riel`), y exigir que esté justo sobre él dejaría la barra apagada mientras se lee.
     De ahí `:hover > *` — el padre hovereado, no cualquier ancestro: con `:hover *` el
     `body` encendería las tres columnas a la vez y esto volvería a ser cromo fijo. */
::-webkit-scrollbar{width:8px;height:8px;}
::-webkit-scrollbar-thumb{background:var(--barra-desliz);border-radius:4px;}
::-webkit-scrollbar-thumb:hover{background:var(--barra-desliz-fuerte);}
::-webkit-scrollbar-track{background:transparent;}
@media (hover:hover){
  ::-webkit-scrollbar-thumb{background:transparent;}
  :hover::-webkit-scrollbar-thumb,
  :hover > *::-webkit-scrollbar-thumb{background:var(--barra-desliz);}
  :hover::-webkit-scrollbar-thumb:hover,
  :hover > *::-webkit-scrollbar-thumb:hover{background:var(--barra-desliz-fuerte);}
}
/* Firefox no tiene pseudo-elementos de scrollbar, así que ahí la misma conducta se
   escribe con `scrollbar-color`. Va aislada tras `@supports`, y eso **no es cosmética**:
   desde Chrome 121 declarar `scrollbar-color` o `scrollbar-width` —aunque sea en `:root`,
   que hereda a todo— desactiva por completo `::-webkit-scrollbar`. En macOS eso devuelve
   la barra flotante del sistema, que se esconde sola y no reacciona al puntero: la barra
   deja de aparecer nunca. Un motor, un mecanismo.

   Dentro, el orden es lo que hace el trabajo: `scrollbar-color` hereda, así que
   `:not(:hover)` apaga toda rama por la que el puntero no pasa y `:hover > *` vuelve a
   encenderla después para el hijo de la columna que sí lo está. Al revés, la herencia
   dejaría las tres barras visibles en cuanto el puntero entrara en la app. */
@supports not selector(::-webkit-scrollbar){
  :root{scrollbar-width:thin;scrollbar-color:var(--barra-desliz) transparent;}
  @media (hover:hover){
    :not(:hover){scrollbar-color:transparent transparent;}
    :hover,
    :hover > *{scrollbar-color:var(--barra-desliz) transparent;}
  }
}

/* ============ ICONOS — sprite Lucide incrustado (scripts/construir_iconos.py) ============ */
.sprite-iconos{position:absolute;width:0;height:0;overflow:hidden;}
.ico{
  width:16px;height:16px;flex:none;
  stroke:currentColor;fill:none;vertical-align:-.18em;
  stroke-width:1.75;
}
/* `.ico-12` se usaba en aria.js desde el principio y nunca existió aquí: los iconos
   de los chips salían a 16px, el tamaño base, sin que nada fallara. */
.ico-12{width:12px;height:12px;}
.ico-14{width:14px;height:14px;}
.ico-18{width:18px;height:18px;}
.ico-20{width:20px;height:20px;}

/* ============ ORBES DE ENERGÍA — identidad de ARIA y de los especialistas ============ */
/*
  `.orbe` es solo el contenedor: tamaño y el ángulo de deformación que `.nucleo` hereda. A
  propósito NO lleva fondo, brillo ni overflow:hidden — eso vive en `.nucleo`, un hijo
  aparte. Si el degradado/remolino y su recorte estuvieran en `.orbe` mismo, ese mismo
  overflow:hidden se comería a las `.particula`, que necesitan asomar *fuera* del círculo
  para orbitarlo. Pasó exactamente eso en la primera versión: las partículas nunca se veían,
  recortadas por el propio orbe que las contenía.
*/
.orbe{
  /* `display:block` no es decorativo: el orbe es un <span>, y fuera de un contenedor
     flex un inline ignora width/height — en la pantalla de acceso salía como un punto. */
  position:relative;display:block;flex:none;
  width:var(--s,36px);height:var(--s,36px);
  /* Se deforma solo, siempre: no es un círculo fijo — respira entre formas de blob
     orgánicas, y `.nucleo` seguirá esta misma forma vía border-radius:inherit. */
  border-radius:50%;
  animation:deformar 8s ease-in-out infinite;
}
.orbe .nucleo{
  content:'';position:absolute;inset:0;border-radius:inherit;overflow:hidden;
  background:radial-gradient(circle at 32% 28%,var(--luz-orbe) 0%,var(--c) 26%,var(--c2) 62%,var(--orbe-fondo) 100%);
  /* El brillo escala con --s (calc(), no un px fijo): a 36px se ve como siempre se vio,
     pero a los ~500px del orbe gigante de la bienvenida se vuelve un halo de verdad en vez
     de una sombra insignificante — es justo lo que se veía plano en la captura. */
  box-shadow:
    0 0 0 1px var(--aro-orbe),
    0 0 calc(var(--s,36px) * .55) calc(var(--s,36px) * -.14) var(--c),
    inset 0 0 calc(var(--s,36px) * .33) calc(var(--s,36px) * -.11) var(--luz-orbe);
}
.orbe .nucleo::before{
  content:'';position:absolute;inset:-30%;
  background:conic-gradient(from 200deg,transparent 0deg,var(--c) 60deg,transparent 140deg,var(--c2) 240deg,transparent 320deg);
  opacity:.55;filter:blur(4px);animation:girar 9s linear infinite;
}
.orbe .nucleo::after{
  content:'';position:absolute;inset:0;border-radius:inherit;
  background:radial-gradient(circle at 34% 26%,var(--brillo-orbe) 0%,transparent 34%);
}
@keyframes girar{to{transform:rotate(360deg);}}
@keyframes deformar{
  0%,100%{border-radius:50% 50% 50% 50% / 50% 50% 50% 50%;}
  25%{border-radius:58% 42% 46% 54% / 44% 56% 60% 40%;}
  50%{border-radius:44% 56% 60% 40% / 58% 42% 46% 54%;}
  75%{border-radius:52% 48% 40% 60% / 46% 54% 52% 48%;}
}
@media (prefers-reduced-motion:reduce){
  .orbe .nucleo::before{animation:none;}
  .orbe{animation:none;border-radius:50%;}
  .orbe.pensando .nucleo{animation:latir 1.1s ease-in-out infinite;}
}
.orbe.pensando .nucleo{animation:latir 1.1s ease-in-out infinite,deformar 4.5s ease-in-out infinite;}
@keyframes latir{
  0%,100%{box-shadow:0 0 0 1px var(--aro-orbe),0 0 calc(var(--s,36px) * .5) calc(var(--s,36px) * -.17) var(--c);}
  50%{box-shadow:0 0 0 1px var(--aro-orbe-fuerte),0 0 calc(var(--s,36px) * .83) 0px var(--c);}
}

/* ============ PARTÍCULAS DEL ORBE — variante "activo" (orbe.js) ============ */
/*
  orbe.js solo anima `rotate` en .particula y `scale` en .orbe: nada que dispare layout. La
  posición base (ángulo + radio) vive aquí, en CSS, para que las cuatro partículas —vengan
  del partial de Django o de `marcadoParticulas()` en JS— salgan ya distribuidas como una
  mini constelación aunque anime.js nunca llegue a cargar o el usuario pida movimiento
  reducido (§ plan de animación con anime.js). El radio (.62–.85 de --s) las pone *fuera*
  del círculo (radio .5) a propósito — son electrones orbitando, no motas dentro del orbe —
  y ahora sí se ven, porque `.orbe` ya no tiene overflow:hidden que las recorte.
*/
.orbe .particula{position:absolute;inset:0;transform-origin:center;pointer-events:none;}
.orbe .particula .punto{
  position:absolute;top:50%;left:50%;border-radius:50%;
  background:var(--c);box-shadow:0 0 6px 1px var(--c);
}
.orbe .particula:nth-child(1){transform:rotate(20deg);}
.orbe .particula:nth-child(1) .punto{width:3px;height:3px;margin:-1.5px 0 0 -1.5px;transform:translateX(calc(var(--s,36px) * .62));}
.orbe .particula:nth-child(2){transform:rotate(100deg);}
.orbe .particula:nth-child(2) .punto{width:4px;height:4px;margin:-2px 0 0 -2px;transform:translateX(calc(var(--s,36px) * .78));}
.orbe .particula:nth-child(3){transform:rotate(200deg);}
.orbe .particula:nth-child(3) .punto{width:3px;height:3px;margin:-1.5px 0 0 -1.5px;opacity:.8;transform:translateX(calc(var(--s,36px) * .68));}
.orbe .particula:nth-child(4){transform:rotate(310deg);}
.orbe .particula:nth-child(4) .punto{width:2.5px;height:2.5px;margin:-1.25px 0 0 -1.25px;opacity:.7;transform:translateX(calc(var(--s,36px) * .85));}

.a-cian{--c:var(--cian);--c2:var(--cian-2);}
.a-ambar{--c:var(--ambar);--c2:var(--ambar-2);}
.a-violeta{--c:var(--violeta);--c2:var(--violeta-2);}
.a-esmeralda{--c:var(--esmeralda);--c2:var(--esmeralda-2);}
.a-magenta{--c:var(--magenta);--c2:var(--magenta-2);}
.a-dorado{--c:var(--dorado);--c2:var(--dorado-2);}

/* ============ APP — conversaciones · chat · riel de perfil ============ */
.app{
  display:grid;
  grid-template-columns:var(--barra-ancho) minmax(0,1fr) var(--riel-ancho);
  height:100dvh;overflow:hidden;
}
.app.sin-riel{grid-template-columns:var(--barra-ancho) minmax(0,1fr);}
.app.sin-barra{grid-template-columns:0 minmax(0,1fr) var(--riel-ancho);}
.app.sin-barra.sin-riel{grid-template-columns:0 minmax(0,1fr);}
.app.sin-barra .barra{transform:translateX(-100%);pointer-events:none;}

/* ============ BARRA LATERAL ============ */
.barra{
  background:var(--panel);border-right:1px solid var(--linea);
  display:flex;flex-direction:column;min-height:0;
  transition:transform .22s ease;
}
.barra-cab{display:flex;align-items:center;gap:10px;padding:14px 12px 10px 14px;}
.marca{display:flex;align-items:center;gap:9px;min-width:0;}
.marca b{font-family:var(--display);font-size:15px;font-weight:600;letter-spacing:-.01em;}
.marca .beta{
  font-size:9px;letter-spacing:.09em;color:var(--oro-texto);
  border:1px solid var(--oro-borde);border-radius:4px;padding:1px 4px;
  font-family:var(--body);font-weight:600;
}
.icono-boton{
  display:inline-flex;align-items:center;justify-content:center;
  width:30px;height:30px;border-radius:var(--r-s);color:var(--texto-2);
  transition:background .15s,color .15s;
}
.icono-boton:hover{background:var(--panel-2);color:var(--texto);}
.barra-cab-botones{margin-left:auto;display:flex;align-items:center;gap:2px;}

.barra-acciones{padding:2px 8px 10px;display:flex;flex-direction:column;gap:2px;}
.accion{
  display:flex;align-items:center;gap:10px;width:100%;
  padding:8px 8px;border-radius:var(--r-s);color:var(--texto);
  font-size:13.5px;text-align:left;transition:background .15s;
}
.accion:hover{background:var(--panel-2);}
.accion .ico-plano{color:var(--texto-2);}
.accion form{display:contents;}
kbd{
  margin-left:auto;font-family:var(--body);font-size:10.5px;color:var(--texto-3);
  border:1px solid var(--linea-2);border-radius:5px;padding:1px 5px;background:var(--panel-2);
  white-space:nowrap;
}

/* buscador: modal sobre toda la app, no un campo dentro de la barra — se abre desde el
   icono de la cabecera o con Ctrl+K, y la barra puede estar plegada cuando ocurre. */
.buscador-modal{
  border:1px solid var(--linea-2);border-radius:var(--r-l);
  background:var(--panel-3);color:var(--texto);
  padding:0;width:min(640px,92vw);max-height:min(70vh,560px);
  /* El reset `*{margin:0}` de arriba pisa el `margin:auto` con el que el navegador
     centra un <dialog> modal: sin esto queda pegado a la esquina superior izquierda. */
  margin:min(12vh,96px) auto auto;
  overflow:hidden;box-shadow:var(--sombra-alta);
}
.buscador-modal::backdrop{background:var(--velo);}
.buscador-campo{
  display:flex;align-items:center;gap:11px;
  padding:13px 14px;border-bottom:1px solid var(--linea-2);
}
.buscador-campo .ico{color:var(--texto-3);flex:none;}
.buscador-campo input{
  flex:1;min-width:0;background:none;border:0;outline:0;
  font-size:14.5px;color:var(--texto);
}
.buscador-campo input::placeholder{color:var(--texto-3);}
/* El aspa de Safari/Chrome duplicaría el botón de cerrar con otro significado. */
.buscador-campo input::-webkit-search-cancel-button{display:none;}

.buscador-lista{padding:7px;overflow-y:auto;max-height:calc(min(70vh,560px) - 57px);}
.res{
  display:flex;align-items:center;gap:11px;
  padding:9px 11px;border-radius:var(--r-s);color:var(--texto-2);
  font-size:13.5px;transition:background .12s,color .12s;
}
.res .ico{color:var(--texto-3);flex:none;}
.res b{font-weight:400;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.res .cuando{font-size:11.5px;color:var(--texto-3);flex:none;}
.res:hover,.res.sel{background:var(--panel-2);color:var(--texto);}
.res:hover .ico,.res.sel .ico{color:var(--oro-texto);}
/* La pista de Enter solo tiene sentido en el renglón que Enter abriría. */
.res .entrar{display:none;margin-left:0;}
.res.sel .entrar{display:inline-block;}

.convs{flex:1;min-height:0;overflow-y:auto;padding:0 8px 8px;}
.seccion + .seccion{margin-top:14px;}
.seccion-cab{
  display:flex;align-items:center;gap:6px;width:100%;
  padding:6px 8px;color:var(--texto-3);
  font-size:11px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;
}
.seccion-cab .chevron{margin-left:auto;transition:transform .18s;}
.seccion.plegada .chevron{transform:rotate(-90deg);}
.seccion.plegada .seccion-cuerpo{display:none;}

.conv{
  position:relative;display:flex;align-items:center;gap:8px;
  padding:7px 8px;border-radius:var(--r-s);color:var(--texto-2);
  font-size:13px;transition:background .15s,color .15s;
}
.conv:hover{background:var(--panel-2);color:var(--texto);}
.conv.on{background:var(--panel-3);color:var(--texto);}
.conv.on::before{
  content:'';position:absolute;left:0;top:7px;bottom:7px;width:2px;
  background:var(--oro);border-radius:0 2px 2px 0;
}
.conv b{
  font-weight:400;flex:1;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.conv .estrella{color:var(--oro-texto);flex:none;}
.conv-menu-boton{
  flex:none;width:22px;height:22px;border-radius:5px;color:var(--texto-3);
  display:inline-flex;align-items:center;justify-content:center;
  opacity:0;transition:opacity .12s,background .12s;
}
.conv:hover .conv-menu-boton,
.conv-menu-boton[aria-expanded="true"]{opacity:1;}
.conv-menu-boton:hover{background:var(--linea-2);color:var(--texto);}
.vacio-lista{color:var(--texto-3);font-size:12px;padding:8px;}

/* pie: estudiante y nivel */
/* Conmutador de tema: control segmentado de dos posiciones, encima del bloque del
   estudiante. El activo se marca con `aria-pressed`, que es a la vez el estado accesible
   y el gancho de estilo — un `.activo` aparte sería la misma verdad escrita dos veces, y
   la que se olvida de actualizar es siempre la del atributo. */
.tema-conmutador{
  display:flex;gap:2px;margin:0 8px 8px;padding:2px;
  background:var(--panel-2);border:1px solid var(--linea);border-radius:var(--r-s);
}
.tema-conmutador button{
  flex:1;display:flex;align-items:center;justify-content:center;gap:5px;
  padding:5px 6px;border-radius:6px;font-size:11.5px;font-weight:var(--fw-medium);
  color:var(--texto-3);transition:background .15s,color .15s;
}
.tema-conmutador button:hover{color:var(--texto-2);}
.tema-conmutador button[aria-pressed="true"]{background:var(--panel);color:var(--texto);}
.barra-pie{border-top:1px solid var(--linea);padding:8px;}
.usuario{
  display:flex;align-items:center;gap:10px;width:100%;
  padding:7px 8px;border-radius:var(--r-s);transition:background .15s;
}
.usuario:hover{background:var(--panel-2);}
.avatar{
  width:30px;height:30px;border-radius:50%;flex:none;
  display:grid;place-items:center;font-size:12px;font-weight:600;color:var(--sobre-oro);
  background:linear-gradient(160deg,var(--oro-suave),var(--oro));
}
.usuario .quien{min-width:0;flex:1;text-align:left;}
.usuario .quien b{
  display:block;font-size:12.5px;font-weight:500;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.usuario .quien span{display:block;font-size:11px;color:var(--texto-3);}
.usuario .quien .nivel-pill{display:flex;align-items:center;gap:5px;color:var(--oro-texto-fuerte);}
.usuario .quien .nivel-pill::before{content:'';width:5px;height:5px;border-radius:50%;background:var(--oro);flex:none;}
.usuario .quien .correo-chico{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}

/* ============ MENÚS FLOTANTES ============ */
.menu{
  position:fixed;z-index:60;min-width:186px;
  background:var(--panel-3);border:1px solid var(--linea-2);border-radius:var(--r-m);
  box-shadow:var(--sombra-media);
  padding:5px;display:none;
}
.menu.on{display:block;}
.menu button,.menu a{
  display:flex;align-items:center;gap:9px;width:100%;
  padding:7px 9px;border-radius:var(--r-s);font-size:13px;
  color:var(--texto-2);text-align:left;transition:background .12s,color .12s;
}
.menu button:hover,.menu a:hover{background:var(--panel-2);color:var(--texto);}
.menu .peligro{color:var(--rojo-texto);}
.menu .peligro:hover{background:var(--rojo-tenue);color:var(--rojo-texto);}
.menu hr{border:0;border-top:1px solid var(--linea-2);margin:5px 4px;}
.menu .nota{padding:4px 9px 7px;font-size:11px;color:var(--texto-3);line-height:1.45;}

/* ============ CENTRO ============ */
.centro{position:relative;display:flex;flex-direction:column;min-width:0;min-height:0;background:var(--fondo);}
.cab{
  display:flex;align-items:center;gap:10px;
  padding:12px 20px;border-bottom:1px solid var(--linea);
}
.cab .titulo{
  display:flex;align-items:center;gap:7px;min-width:0;
  font-size:14px;color:var(--texto);padding:4px 8px;border-radius:var(--r-s);
}
.cab .titulo:hover{background:var(--panel-2);}
.cab .titulo .estrella{color:var(--oro-texto);flex:none;}
.cab .titulo span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.cab .derecha{margin-left:auto;display:flex;align-items:center;gap:4px;}

.hilo{position:relative;flex:1;min-height:0;overflow-y:auto;padding:26px 20px 8px;scroll-behavior:smooth;}
.hilo > *{max-width:760px;margin-inline:auto;}

/* ============ MENSAJES ============ */
.msg{display:flex;gap:12px;margin-bottom:26px;}
/* En columna, no en fila. `.msg` es un flex horizontal y las fichas eran hermanas de la
   burbuja: tres archivos y una pregunta corta se repartían el ancho, las fichas envolvían
   a media altura y el mensaje se leía como cuatro cosas sueltas en vez de una pregunta
   con su respaldo. */
.msg.mio{flex-direction:column;align-items:flex-end;gap:8px;}
.burbuja{
  background:var(--panel-2);border:1px solid var(--linea);
  padding:11px 15px;border-radius:var(--r-l);border-bottom-right-radius:6px;
  max-width:76%;font-size:14px;
}
.burbuja p+p{margin-top:8px;}
.msg-cuerpo{flex:1;min-width:0;}
.msg-quien{display:flex;align-items:center;gap:8px;margin-bottom:8px;}
.msg-quien b{font-size:12.5px;font-weight:600;}
.msg-quien time{font-size:11px;color:var(--texto-3);}
.msg-texto{font-size:14.5px;line-height:1.72;color:var(--texto-lectura);}
.msg-texto h1,.msg-texto h2,.msg-texto h3{
  font-family:var(--display);font-weight:600;letter-spacing:-.015em;
  margin:20px 0 8px;line-height:1.25;
}
.msg-texto h1{font-size:23px;} .msg-texto h2{font-size:19px;} .msg-texto h3{font-size:16px;}
.msg-texto p+p,.msg-texto ul,.msg-texto ol{margin-top:11px;}
.msg-texto ul,.msg-texto ol{padding-left:20px;}
.msg-texto li+li{margin-top:5px;}
.msg-texto strong{color:var(--texto);font-weight:600;}
.msg-texto code{
  font-family:var(--mono);font-size:12.5px;
  background:var(--panel-2);border:1px solid var(--linea);border-radius:5px;padding:1px 5px;
}
.msg-texto pre{
  background:var(--panel-2);border:1px solid var(--linea);border-radius:var(--r-m);
  padding:13px 15px;overflow-x:auto;margin-top:11px;
}
.msg-texto pre code{background:none;border:0;padding:0;}
.msg-texto blockquote{
  border-left:2px solid var(--oro-borde);padding-left:13px;margin-top:11px;color:var(--texto-2);
}
.msg-texto a{color:var(--oro-texto-fuerte);text-decoration:underline;text-underline-offset:2px;}
.msg-texto table{border-collapse:collapse;margin-top:12px;width:100%;font-size:13px;}
.msg-texto th,.msg-texto td{border:1px solid var(--linea);padding:7px 10px;text-align:left;}
.msg-texto th{background:var(--panel-2);font-weight:600;}
/* Separa la respuesta de ARIA del dictamen que se publica íntegro debajo (§11). Es un
   cambio de voz, así que se marca con aire, no solo con una línea: pegados se leen como
   un solo texto que se contradice a sí mismo. */
.msg-texto hr{border:0;border-top:1px solid var(--linea-2);margin:22px 0 18px;}
.cursor{
  display:inline-block;width:2px;height:1.05em;background:var(--oro);
  vertical-align:-.16em;animation:parpadeo 1s steps(2) infinite;
}
@keyframes parpadeo{50%{opacity:0;}}

/* Cola de texto recién llegada del streaming (texto-stream.js) — anime.js anima su
   opacity/translateY por flush; el texto ya asentado nunca vuelve a envolverse aquí. */
.texto-nuevo{display:inline;}

/* acciones bajo la respuesta */
.acciones-msg{display:flex;align-items:center;gap:2px;margin-top:10px;opacity:0;transition:opacity .15s;}
.msg:hover .acciones-msg,.acciones-msg:focus-within{opacity:1;}
.acciones-msg button{
  display:inline-flex;align-items:center;justify-content:center;
  width:28px;height:28px;border-radius:var(--r-s);color:var(--texto-3);
  transition:background .12s,color .12s,transform .1s;
}
.acciones-msg button:hover{background:var(--panel-2);color:var(--texto);}
.acciones-msg button.hecho{color:var(--esmeralda-texto);}
.acciones-msg button[data-calificar].activa[data-calificar="positiva"]{color:var(--esmeralda-texto);background:var(--esmeralda-tenue);}
.acciones-msg button[data-calificar].activa[data-calificar="negativa"]{color:var(--rojo-texto);background:var(--rojo-tenue);}
.acciones-msg button[data-calificar]:active{transform:scale(.90);}

/* Micro-popover de retroalimentación */
.feedback-popover{
  position:relative;margin-top:8px;background:var(--panel-2);border:1px solid var(--linea-2);
  border-radius:var(--r-m);padding:12px;width:100%;max-width:380px;
  box-shadow:var(--sombra-baja);animation:aparecerFeedback .18s cubic-bezier(.16,1,.3,1);z-index:20;
}
@keyframes aparecerFeedback{
  from{opacity:0;transform:translateY(-6px) scale(.98);}
  to{opacity:1;transform:translateY(0) scale(1);}
}
.feedback-popover .fb-cab{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;}
.feedback-popover .fb-titulo{font-size:12px;font-weight:600;color:var(--texto);}
.feedback-popover .fb-cerrar{background:transparent;border:none;color:var(--texto-3);cursor:pointer;padding:2px;display:flex;border-radius:4px;}
.feedback-popover .fb-cerrar:hover{color:var(--texto);}
.feedback-popover .fb-motivos{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:8px;}
.feedback-popover .fb-chip{font-size:11.5px;padding:4px 8px;border-radius:6px;background:var(--panel-3);color:var(--texto-2);border:1px solid transparent;cursor:pointer;transition:all .12s;}
.feedback-popover .fb-chip:hover{background:var(--linea);color:var(--texto);}
.feedback-popover .fb-chip.seleccionado{background:var(--oro-tenue);border-color:var(--oro);color:var(--oro-texto-fuerte);}
.feedback-popover textarea{width:100%;background:var(--fondo);border:1px solid var(--linea);border-radius:6px;padding:6px 8px;font-family:var(--body);font-size:12px;color:var(--texto);resize:none;outline:none;margin-bottom:8px;}
.feedback-popover textarea:focus{border-color:var(--oro);}
.feedback-popover .fb-pie{display:flex;justify-content:flex-end;gap:6px;}
.feedback-popover .fb-btn-enviar{font-size:11.5px;font-weight:500;padding:4px 10px;border-radius:6px;background:var(--oro);color:var(--sobre-oro);border:none;cursor:pointer;transition:opacity .12s;}
.feedback-popover .fb-btn-enviar:hover{opacity:.9;}
.feedback-popover .fb-enviado{font-size:12px;color:var(--esmeralda-texto);display:flex;align-items:center;gap:4px;}

/* ============ PROCESO DE ANÁLISIS (herramientas + especialistas) ============ */
/*
  Una sola tarjeta por turno en vez de varias sueltas: agrupa la llamada a
  herramienta y la consulta a especialistas bajo un solo "Proceso de análisis",
  como el "Thought for Xs" de un asistente profesional. Se colapsa sola al
  terminar el turno (aria.js:finalizarMensaje).

  **Y se lee como una nota al pie, no como un módulo.** Era una caja con fondo, borde,
  título en negrita e icono dorado girando: encima de la respuesta, con más peso visual
  que ella, anunciando el andamiaje en vez del resultado. Ahora es una línea apagada
  —«Pensó durante 8.1 s»— con su galón, y al abrirse el detalle se sangra tras una guía
  vertical: se lee como una cita del turno. Lo que se enseña no cambió; dejó de gritarlo.

  El dorado se reserva para lo que el estudiante puede accionar (enviar, adjuntar). Un
  proceso terminado no se acciona: se consulta.
*/
/* ---- Rótulo en vivo del turno (§16) ----
   Un turno con herramientas y especialistas puede tardar bastante sin escribir una sola
   palabra —el texto final se re-emite entero al terminar—, y esa espera muda se lee como
   una avería. La frase la manda el servidor (aria/agentes/rotulos.py) y cambia sola.

   El brillo recorre el propio texto, no una barra aparte: es la línea que hay que mirar,
   y un indicador al lado competiría con ella por la atención. `background-clip:text`
   exige `color:transparent`, así que el color de reserva va en el degradado, nunca fuera:
   si la animación no corre, el texto tiene que seguir siendo legible. */
.estado-turno{display:flex;align-items:center;gap:7px;margin-bottom:11px;min-height:18px;}
.estado-frase{
  font-size:13px;font-weight:500;
  background:linear-gradient(100deg,var(--texto-3) 34%,var(--oro-texto) 50%,var(--texto-3) 66%);
  background-size:260% 100%;
  -webkit-background-clip:text;background-clip:text;
  color:transparent;
  animation:brilloEstado 2.4s linear infinite;
}
/* La clase se quita y se vuelve a poner en cada cambio de frase: sin reiniciar la
   animación, la frase nueva se sustituye de golpe y el cambio —que es la única señal de
   que algo avanzó— pasa desapercibido. */
.estado-frase.entra{animation:brilloEstado 2.4s linear infinite,entraFrase .3s ease-out;}
@keyframes brilloEstado{to{background-position:-260% 0;}}
@keyframes entraFrase{from{opacity:0;transform:translateY(3px);}to{opacity:1;transform:none;}}
@media (prefers-reduced-motion:reduce){
  /* El rótulo **no** se apaga: es información, no adorno — sin él la pantalla se ve igual
     trabajando que colgada. Se queda quieto y en un color plano. */
  .estado-frase{animation:none;background:none;color:var(--texto-3);}
  .estado-frase.entra{animation:none;}
  .estado-turno .puntos-pensando span{animation:none;opacity:.5;}
}

.proceso{margin-bottom:10px;}
.proceso-cab{display:inline-flex;align-items:center;gap:5px;padding:1px 0;font-size:13px;color:var(--texto-3);cursor:pointer;list-style:none;transition:color .15s;}
.proceso-cab::-webkit-details-marker{display:none;}
.proceso-cab:hover{color:var(--texto-2);}
.proceso-cab .resumen{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.proceso-cab .chevron{flex:none;opacity:.7;transition:transform .18s;}
.proceso[open] .proceso-cab .chevron{transform:rotate(180deg);}
/* Sangrado tras una guía vertical en vez de un panel encerrado: el detalle pertenece a
   la línea de arriba y no vuelve a ser una caja dentro de la conversación. */
.proceso-cuerpo{margin:6px 0 2px;padding:2px 0 2px 14px;border-left:1px solid var(--linea);}

.paso{display:flex;align-items:center;gap:8px;padding:5px 0;font-size:12.5px;color:var(--texto-3);}
.paso .chip-ico{width:14px;height:14px;display:grid;place-items:center;flex:none;color:var(--texto-3);}
.paso .rotulo{flex:1;min-width:0;}
.paso .rotulo b{font-weight:400;color:var(--texto-2);}
/* Ni verde ni dorado: que un paso terminara bien es lo esperado, y el color lo convertía
   en una alerta. El color se guarda para lo que sí exige mirar. */
.paso .estado-ok{display:inline-flex;align-items:center;gap:4px;color:var(--texto-3);font-size:11.5px;flex:none;}
/* Un desenlace que no es el esperado sí exige mirar, así que aquí el color sí entra. Va en
   la variante `-texto` de cada token: `--ambar` y `--rojo` son para relleno y borde, y
   sobre el fondo claro no llegan a contraste de lectura. */
.paso .estado-degradado,
.paso .estado-error{display:inline-flex;align-items:center;gap:4px;font-size:11.5px;flex:none;}
.paso .estado-degradado{color:var(--ambar-texto);}
.paso .estado-error{color:var(--rojo-texto);}

.especialistas{padding:4px 0 0;}
.fila-especialista{display:block;padding:8px 0;}
.fila-especialista+.fila-especialista{border-top:1px solid var(--linea);}
.fila-especialista .cuerpo-especialista{min-width:0;}
.fila-especialista .cabeza{display:flex;align-items:center;gap:8px;margin-bottom:5px;}
.fila-especialista .cabeza b{font-size:12.5px;font-weight:500;color:var(--texto-2);}
.fila-especialista .chip-tiempo{margin-left:auto;font-family:var(--mono);font-size:10.5px;color:var(--texto-3);display:flex;align-items:center;gap:4px;}
.fila-especialista .chip-tiempo.ok{color:var(--texto-2);}
.fila-especialista .dictamen-texto{font-size:12.5px;color:var(--texto-3);line-height:1.6;}
.fila-especialista .dictamen-texto.espera{font-style:italic;}

/* El dictamen es Markdown y el recorte tiene que respetarlo: `-webkit-line-clamp` exige
   `display:-webkit-box`, que aplasta títulos, listas y tablas en una sola caja de líneas.
   Con `max-height` + degradado, el bloque conserva su estructura y solo se desvanece. */
.dictamen-cuerpo{position:relative;}
.dictamen-cuerpo.recortado{max-height:8.4em;overflow:hidden;}
.dictamen-cuerpo.recortado::after{
  content:'';position:absolute;left:0;right:0;bottom:0;height:3.4em;pointer-events:none;
  background:linear-gradient(to bottom,transparent,var(--fondo));
}
.ver-dictamen{font-size:11.5px;color:var(--texto-2);padding:4px 0 0;transition:color .15s;}
.ver-dictamen:hover{color:var(--oro-texto-fuerte);}

/* Los bloques del dictamen dentro del recorte: la misma jerarquía que `.msg-texto` pero
   apagada y en pequeño — es material de consulta, no la respuesta. */
.dictamen-texto > * + *{margin-top:7px;}
.dictamen-texto h1,.dictamen-texto h2,.dictamen-texto h3,.dictamen-texto h4{font-size:12.5px;font-weight:600;color:var(--texto-2);}
.dictamen-texto strong{color:var(--texto-2);font-weight:600;}
.dictamen-texto ul,.dictamen-texto ol{padding-left:17px;}
.dictamen-texto table{border-collapse:collapse;width:100%;font-size:11.5px;}
.dictamen-texto th,.dictamen-texto td{border:1px solid var(--linea);padding:4px 7px;text-align:left;}
.dictamen-texto hr{border:0;border-top:1px solid var(--linea);}

/* ============ DIÁLOGO DEL DICTAMEN COMPLETO (§11, §13) ============ */
.dialogo-dictamen{
  border:1px solid var(--linea-2);border-radius:var(--r-l);
  background:var(--panel-3);color:var(--texto);
  padding:0;width:min(720px,94vw);max-height:min(82vh,760px);
  margin:min(9vh,72px) auto auto;  /* ver la nota de .buscador-modal */
  overflow:auto;box-shadow:var(--sombra-alta);
}
.dialogo-dictamen::backdrop{background:var(--velo);}
/* La cabecera se queda a la vista: un dictamen largo deja el aspa fuera de pantalla y la
   única salida sería Escape, que no todo el mundo prueba. */
.dialogo-dictamen .dd-cab{
  position:sticky;top:0;z-index:1;
  display:flex;align-items:center;gap:12px;
  padding:15px 18px;border-bottom:1px solid var(--linea-2);background:var(--panel-3);
}
.dialogo-dictamen .dd-quien{flex:1;min-width:0;display:flex;align-items:baseline;gap:9px;}
.dialogo-dictamen .dd-quien b{font-family:var(--display);font-size:15px;font-weight:600;}
.dialogo-dictamen .dd-meta{font-family:var(--mono);font-size:11px;color:var(--texto-3);}
.dialogo-dictamen .cerrar{color:var(--texto-3);flex:none;display:grid;place-items:center;transition:color .15s;}
.dialogo-dictamen .cerrar:hover{color:var(--texto);}
.dialogo-dictamen .dd-pregunta{padding:13px 18px;border-bottom:1px solid var(--linea);background:var(--panel-2);}
.dialogo-dictamen .dd-pregunta[hidden]{display:none;}
.dialogo-dictamen .dd-rotulo{display:block;font-size:10.5px;text-transform:uppercase;letter-spacing:.07em;color:var(--texto-3);margin-bottom:5px;}
.dialogo-dictamen .dd-pregunta p{font-size:13px;color:var(--texto-2);line-height:1.55;}
.dialogo-dictamen .dd-cuerpo{padding:18px;font-size:13.5px;}

/* ============ LECTOR DE FUENTES (§16) ============
   Leer una publicación sin salir de ARIA. Misma caja que el dictamen a propósito: son el
   mismo gesto —abrir material de consulta encima del hilo— y dos geometrías distintas para
   lo mismo se leen como dos funciones distintas.

   El cuerpo es una columna de lectura, no una tarjeta: 8 000 caracteres a 13.5 px en un
   ancho de 720 son párrafos largos, y aquí el ancho se estrecha y el interlineado se abre.
   El `line-height` del `body` (1.55) es para la interfaz; un artículo pide más. */
.dialogo-lector{
  border:1px solid var(--linea-2);border-radius:var(--r-l);
  background:var(--panel-3);color:var(--texto);
  padding:0;width:min(680px,94vw);max-height:min(84vh,820px);
  /* El reset `*{margin:0}` mata el centrado nativo de `<dialog>`, que el navegador hace
     con `margin:auto`. Se pide a mano, igual que el buscador y el dictamen. */
  margin:min(8vh,64px) auto auto;
  overflow:hidden;box-shadow:var(--sombra-alta);
}
/* El `flex` solo abierto: en la regla base pisa el `dialog:not([open]){display:none}` del
   navegador y el diálogo cerrado se queda pintado, vacío, al pie de la página. */
.dialogo-lector[open]{display:flex;flex-direction:column;}
.dialogo-lector::backdrop{background:var(--velo);}
.dialogo-lector .dl-cab{
  flex:none;display:flex;align-items:flex-start;gap:12px;
  padding:15px 18px;border-bottom:1px solid var(--linea-2);background:var(--panel-3);
}
.dialogo-lector .dl-quien{flex:1;min-width:0;}
.dialogo-lector .dl-quien b{
  display:block;font-family:var(--display);font-size:15px;font-weight:600;line-height:1.3;
}
.dialogo-lector .dl-meta{
  display:block;margin-top:3px;font-family:var(--mono);font-size:11px;color:var(--texto-3);
}
.dialogo-lector .cerrar{color:var(--texto-3);flex:none;display:grid;place-items:center;transition:color .15s;}
.dialogo-lector .cerrar:hover{color:var(--texto);}
.dialogo-lector .dl-avisos{flex:none;display:flex;flex-wrap:wrap;gap:6px;padding:11px 18px 0;}
/* Sin avisos no hay franja: un hueco de 11 px sobre el texto se lee como un fallo. */
.dialogo-lector .dl-avisos:empty{display:none;}
.dl-aviso{padding:2px 9px;border-radius:999px;font-size:10.5px;letter-spacing:.02em;}
/* Ámbar de aviso, no de error, igual que en el bloque de fuentes: un comunicado de prensa
   no es una avería, es material del propio proyecto y hay que leerlo sabiéndolo. */
.dl-aviso.es-promocional{background:var(--ambar-tenue);color:var(--ambar-texto);}
.dl-aviso.es-en-vivo{background:var(--panel-2);color:var(--texto-3);}
/* La columna de lectura. `overflow` aquí y no en el diálogo: así la cabecera y el pie se
   quedan a la vista y la salida nunca se va fuera de pantalla con un artículo largo. */
.dialogo-lector .dl-cuerpo{
  flex:1;min-height:0;overflow:auto;padding:16px 18px 20px;
  font-size:14px;line-height:1.7;color:var(--texto-2);
}
.dialogo-lector .dl-cuerpo p + p{margin-top:11px;}
.dialogo-lector .dl-nota{color:var(--texto-3);font-size:13px;}
.dialogo-lector .dl-pie{
  flex:none;display:flex;align-items:center;justify-content:space-between;gap:12px;
  flex-wrap:wrap;padding:11px 18px;border-top:1px solid var(--linea-2);background:var(--panel-2);
}
.dialogo-lector .dl-pie-nota{font-size:11px;color:var(--texto-3);}
.dialogo-lector .dl-externo{
  display:inline-flex;align-items:center;gap:6px;flex:none;
  color:var(--oro-texto);font-size:12px;text-decoration:none;
}
.dialogo-lector .dl-externo:hover{text-decoration:underline;}

.puntos-pensando{display:inline-flex;gap:3px;align-items:center;}
.puntos-pensando span{width:4px;height:4px;border-radius:50%;background:var(--texto-3);animation:rebote 1.1s infinite ease-in-out;}
.puntos-pensando span:nth-child(2){animation-delay:.15s;}
.puntos-pensando span:nth-child(3){animation-delay:.3s;}
@keyframes rebote{0%,80%,100%{opacity:.25;transform:translateY(0);}40%{opacity:1;transform:translateY(-2px);}}

/* ============ FUENTES / CITAS ============ */
.fuentes{display:flex;flex-wrap:wrap;gap:6px;margin-top:12px;}
.fuente{
  display:inline-flex;align-items:center;gap:6px;
  background:var(--panel-2);border:1px solid var(--linea);border-radius:100px;
  padding:4px 11px;font-size:11.5px;color:var(--texto-2);
  transition:border-color .15s,color .15s;
}
.fuente:hover{border-color:var(--oro-borde);color:var(--texto);}
.fuente .ico{color:var(--oro-texto);}

.cortina{
  position:fixed;inset:0;background:var(--velo);z-index:70;
  opacity:0;pointer-events:none;transition:opacity .2s;
}
.cortina.on{opacity:1;pointer-events:auto;}
.panel-fuente{
  position:fixed;top:0;right:0;bottom:0;width:min(420px,92vw);z-index:80;
  background:var(--panel);border-left:1px solid var(--linea-2);
  padding:22px;overflow-y:auto;transform:translateX(100%);transition:transform .24s ease;
}
.panel-fuente.on{transform:none;}
.panel-fuente h3{font-family:var(--display);font-size:17px;margin-bottom:6px;padding-right:32px;}
.panel-fuente .ref{font-size:12px;color:var(--oro-texto);margin-bottom:14px;}
.panel-fuente .cita{font-size:13.5px;line-height:1.7;color:var(--texto-2);white-space:pre-wrap;}
.panel-fuente .cerrar{
  position:absolute;top:16px;right:16px;width:30px;height:30px;border-radius:var(--r-s);
  display:grid;place-items:center;color:var(--texto-2);
}
.panel-fuente .cerrar:hover{background:var(--panel-2);color:var(--texto);}

/* ============ PANTALLA DE BIENVENIDA — composición centrada ============ */
/*
  Toda una sola composición centrada: el texto, el compositor (que en este estado flota
  centrado en vez de ir pegado abajo — ver `.centro.bienvenida .pie-chat` en COMPOSITOR) y
  un orbe gigante asomando desde el borde inferior. `.vacio` cubre todo `.hilo` con
  position:absolute para poder centrar verticalmente sin depender de cuánto mida el
  compositor, que en este estado ya no ocupa espacio en el flujo normal — si lo hiciera,
  `.vacio` tendría que descontar su alto a mano cada vez que cambie el diseño del compositor.
*/
.vacio{
  position:absolute;inset:0;overflow:hidden;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  padding-bottom:min(16vh,120px);
  text-align:center;
}
/* Sin este overflow:hidden, el desborde a propósito de `.orbe-gigante` (ver más abajo)
   agranda el alto con scroll de `.hilo` en vez de quedarse fuera de vista — `.app` lo
   recorta visualmente, pero `.hilo` seguía "sabiendo" que ahí hay más contenido y
   scrolleaba hasta mostrarlo, arrastrando el título fuera de la pantalla. */
.vacio-texto{position:relative;z-index:1;}
/* El saludo va por encima de la pregunta y un escalón por debajo en jerarquía: el
   nombre da la bienvenida, la pregunta es la que invita a escribir. */
.saludo-vacio{
  font-family:var(--display);font-size:clamp(15px,1.6vw,18px);font-weight:500;
  letter-spacing:-.01em;color:var(--texto-2);margin-bottom:2px;
  /* El `line-height:1.55` del cuerpo es para leer párrafos: en dos líneas de display, y
     con la pregunta a 36px, mete casi 20px de aire entre saludo y pregunta y las separa
     como si fueran dos bloques distintos. Aquí son una sola frase en dos renglones. */
  line-height:1.3;
}
.vacio-texto h1{
  font-family:var(--display);font-size:clamp(24px,3.2vw,36px);font-weight:600;
  letter-spacing:-.02em;color:var(--texto);line-height:1.2;
}
.orbe-gigante{
  /* Se define el tamaño una sola vez en `--tamano` y `bottom` se calcula a partir de él
     (tamaño menos `--asomo`, lo que debe quedar visible), así el arco es el mismo sin
     importar el ancho de pantalla — con un `bottom` fijo en vh, un tamaño más chico en
     pantallas angostas dejaba ver más de la mitad de la esfera en vez de un arco (la
     captura de referencia: el orbe llenaba casi toda la pantalla).

     `--asomo` es el único número que mueve cuánto se ve. Estaba metido dentro del `calc()`
     y repetido en este comentario, que es como se acaba cambiando uno y no el otro. */
  --tamano:min(68vw,640px);
  /* Un `--asomo` en px fijos se come la esfera en pantallas angostas: a 390px de ancho,
     `--tamano` cae a ~265px y 210px de asomo enseñarían casi el hemisferio entero en vez
     de un arco. El tope proporcional lo evita sin tocar el caso de escritorio — es el
     mismo fallo que el `bottom` en vh que ya se corrigió aquí, del revés. */
  --asomo:min(210px,calc(var(--tamano) * .33));
  position:absolute;left:50%;z-index:0;
  bottom:calc(-1 * (var(--tamano) - var(--asomo)));
  transform:translateX(-50%);
  pointer-events:none;
}
.orbe-gigante .orbe{--s:var(--tamano);}

/* ============ ORBE LÍQUIDO (WebGPU) — solo el héroe de la bienvenida ============ */
/*
  `.con-lienzo` la pone `static/js/animaciones/orbe-liquido.js` **solo cuando el renderer
  confirmó que arrancó**. Sin WebGPU la clase nunca llega y lo que se ve es el orbe CSS de
  siempre, intacto: esto añade, no sustituye.

  Se apaga `deformar` porque el shader ya trae su propio contorno; las dos a la vez
  deforman dos veces. El lienzo NO se recorta con border-radius: el shader dibuja su esfera
  con borde suave sobre un lienzo transparente (`alphaMode:"premultiplied"`), y recortarlo
  le devolvería el filo duro que el `edgeSoftness` existe para quitar.
*/
.orbe.con-lienzo{animation:none;}
.orbe.con-lienzo .nucleo{display:none;}
/* Las `.particula` también. Solo se escondía `.nucleo`, así que el orbe del turno
   —variante "activo", la única que las pinta— salía con cuatro puntos dorados flotando
   sueltos a su derecha, ya quietos porque `desactivarGalaxia()` para la órbita pero no
   los saca del DOM. Se veía como suciedad junto al orbe, no como un fallo. */
.orbe.con-lienzo .particula{display:none;}
.orbe-lienzo{
  /* El shader dibuja la esfera a `radius = 0.72` del semiancho del lienzo, así que un
     lienzo del tamaño del orbe daría una esfera ~28 % más chica que la del orbe CSS al
     que sustituye. El lienzo se agranda por el inverso de ese radio y se centra; lo que
     sobra es transparente. Es el único número que hay que tocar para calibrar el tamaño,
     y sale del preset — no se adivina.

     `.orbe` no lleva overflow:hidden a propósito (ver el comentario de la sección de
     orbes), así que este desborde no se recorta. */
  --factor-lienzo:1.39;
  position:absolute;top:50%;left:50%;display:block;
  width:calc(100% * var(--factor-lienzo));
  height:calc(100% * var(--factor-lienzo));
  transform:translate(-50%,-50%);
  pointer-events:none;
}

/* ============ COMPOSITOR ============ */
.pie-chat{padding:10px 20px 14px;}
.pie-chat > *{max-width:760px;margin-inline:auto;}
/* Mientras se ve la bienvenida (bienvenida.js agrega/quita esta clase) el compositor no
   está pegado abajo: flota centrado, formando una sola composición con el texto y el orbe
   gigante. `salir()` mide su posición antes/después de quitar la clase y anima la
   diferencia (FLIP), así se ve bajar en vez de saltar de golpe a su sitio normal. */
.centro.bienvenida .pie-chat{
  position:absolute;left:0;right:0;top:50%;z-index:2;
  transform:translateY(-50%);
}
.compositor{
  display:flex;align-items:flex-end;gap:10px;
  background:var(--panel-2);border:1px solid var(--linea-2);border-radius:var(--r-xl);
  padding:9px 9px 9px 16px;transition:border-color .16s,box-shadow .16s;
}
.compositor:focus-within{border-color:var(--oro-borde);box-shadow:0 0 0 3px var(--oro-tenue);}
/* Arrastrar un archivo encima. Se marca el compositor entero y no una zona aparte: la
   zona de soltar dedicada obliga a apuntar, y aquí el objetivo es todo lo que se ve. */
.compositor.soltando{border-color:var(--oro-borde);background:var(--oro-tenue);}

/* ---- adjuntos del turno ---- */
/* La tira vive fuera de `.compositor` para crecer hacia arriba sin empujar el textarea ni
   falsear el alto que calcula el autoexpand. */
.tira-adjuntos{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:8px;}
.tira-adjuntos[hidden]{display:none;}
.ficha-adjunto{
  display:inline-flex;align-items:center;gap:6px;max-width:100%;
  background:var(--panel-2);border:1px solid var(--linea-2);border-radius:var(--r-l);
  padding:5px 8px;font-size:12.5px;color:var(--texto-2);
}
.ficha-adjunto .ico{color:var(--oro-texto-fuerte);flex:none;}
.ficha-nombre{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:200px;}
.ficha-peso{color:var(--texto-3);flex:none;}
.ficha-quitar{
  flex:none;display:inline-flex;align-items:center;justify-content:center;
  width:18px;height:18px;border:0;border-radius:50%;background:none;cursor:pointer;
  color:var(--texto-3);padding:0;
}
.ficha-quitar:hover{background:var(--linea-2);color:var(--texto);}
.ficha-adjunto.cargando{color:var(--texto-3);font-style:italic;}
/* En la burbuja ya enviada no hay nada que quitar: es historial, no un borrador. Se
   alinean a la derecha con la burbuja y no pasan de su ancho, para que la pregunta y su
   respaldo se lean como un solo bloque. */
.adjuntos-de-burbuja{display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end;max-width:76%;}
.ficha-adjunto.en-burbuja{
  display:flex;align-items:center;gap:9px;
  background:var(--panel);border:1px solid var(--linea);border-radius:var(--r-m);
  padding:8px 11px 8px 9px;max-width:250px;text-align:left;
}
a.ficha-adjunto.en-burbuja{text-decoration:none;transition:border-color .15s,background .15s;}
a.ficha-adjunto.en-burbuja:hover{border-color:var(--oro-borde);background:var(--panel-2);}
.ficha-adjunto.en-burbuja .ficha-ico{
  flex:none;width:34px;height:34px;border-radius:9px;display:grid;place-items:center;
  background:var(--oro-tenue);color:var(--oro-texto-fuerte);
}
/* Dos renglones: el nombre y qué es. Un nombre recortado a la mitad —«CONTRATO DE
   ADHESIÓN BENEF…»— no dice ni el tipo ni el peso, que es lo que se mira para saber si se
   mandó lo correcto. */
.ficha-adjunto.en-burbuja .ficha-datos{display:flex;flex-direction:column;min-width:0;gap:2px;}
.ficha-adjunto.en-burbuja .ficha-nombre{max-width:none;font-size:12.5px;color:var(--texto-2);}
.ficha-adjunto.en-burbuja .ficha-peso{font-size:11px;color:var(--texto-3);font-family:var(--mono);}

/* Una imagen se enseña, no se describe: «Imagen de WhatsApp 2024-09-2…» es la peor
   etiqueta posible para una captura, y era justo lo que se veía. */
.ficha-adjunto.en-burbuja.es-imagen{flex-direction:column;align-items:stretch;gap:0;padding:0;width:172px;overflow:hidden;}
.ficha-miniatura{display:block;width:100%;height:116px;object-fit:cover;background:var(--panel-2);}
.ficha-adjunto.en-burbuja.es-imagen .ficha-datos{padding:7px 10px 8px;}

/* ---- biblioteca de archivos ---- */
.biblioteca-modal{width:min(560px,92vw);}
.biblioteca-titulo{flex:1;font-size:14px;}
.biblioteca-vacia{padding:22px 16px;color:var(--texto-3);font-size:13px;text-align:center;}
.fila-biblioteca{
  display:flex;align-items:center;gap:10px;
  padding:10px 14px;border-bottom:1px solid var(--linea-2);font-size:13px;
}
.fila-biblioteca:last-child{border-bottom:0;}

/* Turno que no llegó a cerrarse (recarga, pestaña cerrada, proceso caído). */
.msg-interrumpido{
  display:flex;align-items:center;gap:7px;margin-top:10px;
  font-size:12.5px;color:var(--texto-3);font-style:italic;
}
.msg-interrumpido .ico{color:var(--texto-3);}
.fila-biblioteca .ico{color:var(--oro-texto-fuerte);}
.fila-biblioteca .ficha-nombre{flex:1;max-width:none;}
.usar-archivo{
  flex:none;border:1px solid var(--linea-2);border-radius:var(--r-m,8px);
  background:none;color:var(--texto-2);padding:4px 11px;font-size:12.5px;cursor:pointer;
}
.usar-archivo:hover{border-color:var(--oro-borde);color:var(--texto);}
.borrar-archivo{flex:none;color:var(--texto-3);}
.borrar-archivo:hover{color:var(--texto);}
.compositor textarea{
  flex:1;min-width:0;background:none;border:0;outline:0;resize:none;
  font-size:14.5px;line-height:1.55;padding:6px 0;max-height:200px;
}
.compositor textarea::placeholder{color:var(--texto-3);}
.enviar{
  flex:none;width:34px;height:34px;border-radius:50%;
  display:grid;place-items:center;color:var(--sobre-oro);overflow:hidden;
  background:var(--oro-degradado);
  transition:
    width .18s ease, margin-left .18s ease, opacity .14s ease,
    transform .26s cubic-bezier(.34,1.56,.64,1);
}
.enviar:hover{transform:translateY(-1px);}

/* Sin nada que enviar el botón **no se ve y no ocupa sitio**: se colapsa a cero y el
   margen negativo se come el `gap` del compositor, para que el micrófono quede pegado al
   borde en vez de dejar un hueco de 34px. Al escribir entra con un rebote corto —la curva
   se pasa de 1 y vuelve—, que es lo que hace que se note que apareció algo nuevo. */
.enviar.oculto{
  width:0;margin-left:-10px;opacity:0;
  transform:scale(.4);pointer-events:none;
}

/* Atenuado, no escondido: hay texto escrito pero ARIA está respondiendo. Esconderlo aquí
   lo haría desaparecer a media respuesta cada vez que se adelanta la siguiente pregunta. */
.enviar:disabled:not(.oculto){
  background:var(--panel-3);color:var(--texto-3);
  cursor:default;transform:none;
}
.enviar:disabled:not(.oculto):hover{transform:none;}
.enviar .ico{stroke-width:2.6;}
.legal{text-align:center;font-size:11px;color:var(--texto-3);margin-top:9px;line-height:1.5;}

/* ============ DICTADO POR VOZ ============ */
/* Tres botones que nunca coinciden: en reposo solo el micrófono; grabando, descartar y
   confirmar. Que el micrófono desaparezca es lo que hace obvio que la salida son esos
   dos y no volver a pulsar el mismo botón. */
.dictado{display:flex;align-items:center;gap:4px;flex:none;min-width:0;}
.cancelar-dictado,.confirmar-dictado{display:none;}
.compositor.grabando .mic{display:none;}
.compositor.grabando .cancelar-dictado{display:grid;}
.compositor.grabando .confirmar-dictado{display:grid;}
/* Enviar se esconde al grabar: el confirmar ya ocupa su sitio y su forma, y dos círculos
   dorados a la vez preguntarían cuál de los dos manda el mensaje. */
.compositor.grabando .enviar{display:none;}

.mic.procesando{color:var(--oro-texto);}
.mic.procesando .ico{animation:girar 1s linear infinite;}

.cancelar-dictado{flex:none;color:var(--texto-2);}
.cancelar-dictado:hover{color:var(--rojo-texto);background:var(--rojo-tenue);}

/* Mismo círculo dorado que enviar: es el botón que confirma y tiene que leerse como el
   de siempre, no como uno nuevo que hay que interpretar. */
.confirmar-dictado{
  flex:none;width:34px;height:34px;border-radius:50%;
  place-items:center;color:var(--sobre-oro);
  background:var(--oro-degradado);
  transition:opacity .15s,transform .12s;
}
.confirmar-dictado:hover{transform:translateY(-1px);}
.confirmar-dictado .ico{stroke-width:3;}

/* ---- la tira de grabación ---- */
/* Oculta hasta que se graba, y entonces se queda con toda la fila. El compositor no
   tiene sitio para una onda y un textarea a la vez: apretada en 96px se leía como una
   línea de puntos. El texto ya escrito no se pierde —el textarea solo se colapsa— y
   vuelve intacto al detener, con el dictado añadido al final. */
.grabacion{
  display:none;align-items:center;gap:10px;
  flex:1;min-width:0;height:34px;
}
.compositor.grabando .grabacion{display:flex;}
/* `.dictado` es `flex:none` en reposo —solo es un botón— y tiene que crecer al grabar, o
   la tira de dentro se queda con ancho cero y no se construye ni una barra. */
.compositor.grabando .dictado{flex:1;min-width:0;}
.compositor.grabando textarea{flex:0 1 0;width:0;min-width:0;padding:0;opacity:0;}
.compositor.grabando{border-color:var(--oro-borde);box-shadow:0 0 0 3px var(--oro-tenue);}

.punto-rec{
  flex:none;width:7px;height:7px;border-radius:50%;background:var(--rojo);
  box-shadow:0 0 0 0 var(--rojo-pulso);
  animation:latido-rec 1.6s ease-out infinite;
}
@keyframes latido-rec{
  0%{box-shadow:0 0 0 0 var(--rojo-pulso);}
  70%{box-shadow:0 0 0 7px transparent;}
  100%{box-shadow:0 0 0 0 transparent;}
}

/* ---- la onda ---- */
/* Las barras las genera dictado.js según el ancho; cada una lleva `--n` (0..1), que
   mueve a la vez la altura y el brillo. Que el nivel también module el color es lo que
   la hace legible de un vistazo: con altura sola, una tira de barras bajas y una de
   barras altas se parecen demasiado en visión periférica. */
.onda{
  display:flex;align-items:center;justify-content:flex-end;
  gap:3px;flex:1;min-width:0;height:30px;overflow:hidden;
}
.onda i{
  flex:none;width:3px;height:100%;border-radius:99px;
  background:linear-gradient(to bottom,var(--oro-vivo),var(--oro));
  transform:scaleY(var(--n,.06));
  opacity:calc(.32 + var(--n,.06) * .68);
  will-change:transform;
}

.dictado-tiempo{
  flex:none;font-family:var(--mono);font-size:12px;color:var(--texto-2);
  min-width:34px;text-align:right;font-variant-numeric:tabular-nums;
  transition:color .3s;
}
.dictado-tiempo.cerca-del-limite{color:var(--rojo-texto);}

.dictado-error{
  text-align:center;font-size:12px;color:var(--rojo-texto);
  margin-top:8px;line-height:1.45;
}

/* En pantalla estrecha la tira se lleva su propio renglón: compartir fila con los
   botones la dejaba en unos 120px, otra vez ilegible. */
@media (max-width:560px){
  .compositor{flex-wrap:wrap;}
  .compositor.grabando .grabacion{order:-1;flex-basis:100%;}
}

/* Movimiento reducido. **Este bloque va aquí y no arriba con el del orbe**: las reglas
   del dictado tienen la misma especificidad que las de esta sección, así que puesto en
   el bloque de la línea 138 las pisaba la sección de abajo y no hacía nada. La cascada
   no avisa de eso — la página carga, el selector existe y el estilo simplemente pierde.

   La onda no se mueve, pero la tira **no desaparece**: sin ninguna señal de que el
   micrófono está abierto, la pantalla se ve igual grabando que en reposo. Queda una
   línea quieta, el punto sin latido y el cronómetro, que es información y no adorno —
   saber cuánto llevas grabando importa cuando cuesta dinero. */
@media (prefers-reduced-motion:reduce){
  /* El botón de enviar sigue apareciendo y desapareciendo —es información sobre si hay
     algo que enviar—, solo que sin rebote ni deslizamiento. */
  .enviar{transition:none;}
  .enviar.oculto{transform:none;}
  .onda i{transition:none;transform:scaleY(.14);opacity:.45;}
  .punto-rec{animation:none;}
  .mic.procesando .ico{animation:none;}
}


/* ============ RIEL DE PERFIL (§16 — o tiene dato, o no aparece) ============ */
/* La columna 3 no scrollea como bloque — el único contenido de largo variable es la
   lista de "Tu camino" (hasta 11 etapas reales), así que es esa lista, no la columna
   entera, la que lleva su propio scroll acotado (ver `.camino` más abajo). */
.riel{background:var(--panel);border-left:1px solid var(--linea);overflow:hidden;min-height:0;}
.app.sin-riel .riel{display:none;}

/* Encabezado del riel: el siguiente paso del estudiante. Sustituye al bloque de
   identidad (nombre, correo y nivel), que repetía lo que ya dice el pie de la barra. */
.riel-siguiente{padding:16px 16px 15px;border-bottom:1px solid var(--linea);}
.riel-siguiente .et{
  display:flex;align-items:center;gap:6px;
  font-size:11px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:var(--texto-3);
}
.riel-siguiente .et .ico{color:var(--oro-texto);}
.riel-siguiente .paso{
  font-family:var(--display);font-size:16px;font-weight:600;letter-spacing:-.01em;
  color:var(--texto);line-height:1.25;margin:7px 0 11px;
}
.medidor{height:5px;border-radius:100px;background:var(--panel-3);overflow:hidden;}
.medidor span{
  display:block;height:100%;border-radius:inherit;
  background:linear-gradient(90deg,var(--oro-suave),var(--oro));
}
.riel-siguiente .cuenta{display:block;margin-top:7px;font-size:11.5px;color:var(--texto-3);}

.riel-cuerpo{padding:16px 14px 22px;}
.riel-seccion+.riel-seccion{margin-top:16px;}
.riel-seccion-titulo{
  display:flex;align-items:center;gap:7px;margin-bottom:9px;
  font-size:11px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:var(--texto-3);
}
.riel-seccion-titulo .ico{color:var(--oro-texto);}
/* `.riel-seccion-titulo` reusa el plegado de `.seccion-cab` (misma clase, mismo
   listener de barra.js) — se anula aquí el padding pensado para la barra angosta,
   que en el riel desalineaba el ícono dorado del borde. */
.riel-seccion .seccion-cab{padding:0;margin-bottom:9px;}
.riel-seccion.plegada .seccion-cab{margin-bottom:0;}
.riel-vacio{font-size:12px;color:var(--texto-2);line-height:1.65;}

.tarj{background:var(--panel-2);border:1px solid var(--linea);border-radius:var(--r-m);padding:13px 14px;}

/* lo que declaraste */
.stats-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;}
.stat-tile{background:var(--panel-2);border:1px solid var(--linea);border-radius:var(--r-m);padding:11px 13px;}
.stat-tile .et{display:block;font-size:10.5px;color:var(--texto-3);}
.stat-tile .val{display:block;font-size:13px;font-weight:600;color:var(--texto);line-height:1.4;margin-top:6px;}
.stat-tile.ancho{grid-column:1 / -1;}

/* tu camino: stepper vertical */
/* Alto acotado con scroll propio: es la única sección de largo variable (hasta 11
   etapas reales), y la columna 3 no scrollea como bloque (ver `.riel` arriba). */
.camino{padding-bottom:2px;max-height:270px;overflow-y:auto;padding-right:4px;}
.paso-camino{position:relative;display:flex;gap:11px;padding-bottom:15px;padding-left:2px;}
.paso-camino:last-child{padding-bottom:0;}
.paso-camino::before{content:'';position:absolute;left:9px;top:20px;bottom:-4px;width:1px;background:var(--linea-2);}
.paso-camino:last-child::before{display:none;}
.paso-camino .marca{flex:none;width:19px;height:19px;border-radius:50%;display:grid;place-items:center;margin-top:1px;z-index:1;}
.paso-camino.hecho .marca{background:var(--esmeralda);color:var(--sobre-esmeralda);}
.paso-camino.pendiente .marca{background:var(--panel-3);border:1.5px dashed var(--linea-2);}
.paso-camino .cont{flex:1;min-width:0;}
.paso-camino .nombre{font-size:13px;color:var(--texto);font-weight:500;}
.paso-camino.pendiente .nombre{color:var(--texto-2);}
.paso-camino .fecha{font-size:11px;color:var(--texto-3);margin-top:2px;}

/* sesiones */
.sesion-proxima{background:var(--oro-tenue);border:1px solid var(--oro-borde);border-radius:var(--r-m);padding:10px 13px;margin-bottom:10px;}
.sesion-proxima .et{display:block;font-size:10.5px;color:var(--texto-3);}
.sesion-proxima .val{display:block;font-size:13px;font-weight:600;color:var(--texto);margin-top:3px;}
.sesion-fila{display:flex;align-items:center;gap:10px;padding:8px 0;}
.sesion-fila+.sesion-fila{border-top:1px solid var(--linea);}
.sesion-fila.proxima{border-left:2px solid var(--oro);padding-left:8px;background:var(--oro-tenue);}
.sesion-fila .num{
  width:24px;height:24px;border-radius:50%;flex:none;display:grid;place-items:center;
  font-family:var(--mono);font-size:10.5px;color:var(--texto-2);background:var(--panel-3);border:1px solid var(--linea-2);
}
.sesion-fila .cont{flex:1;min-width:0;}
.sesion-fila .fecha{font-size:12.5px;color:var(--texto);}
.sesion-fila .marco{font-size:10.5px;color:var(--texto-3);}
.chip-estado{flex:none;font-size:10.5px;padding:3px 9px;border-radius:100px;white-space:nowrap;}
.chip-estado.confirmada{color:var(--esmeralda-texto);background:var(--esmeralda-tenue);}
.chip-estado.pendiente{color:var(--ambar-texto);background:var(--ambar-tenue);}

/* material: nota informativa, no lista (§16 — es dato administrativo que el
   estudiante no puede abrir; ARIA ya lo usa como contexto sin pasar por el riel) */
.riel-nota{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--texto-2);padding:10px 0;border-top:1px solid var(--linea);margin-top:4px;}
.riel-nota .ico{flex:none;color:var(--texto-3);}

/* ============ ACCESO ============ */
.acceso{min-height:100dvh;display:grid;place-items:center;padding:24px;}
.acceso-caja{
  width:min(420px,100%);
  background:var(--panel);border:1px solid var(--linea);border-radius:var(--r-l);
  padding:30px;text-align:center;
}
.acceso-caja .orbe{margin:0 auto 16px;}
.acceso-caja h1{font-family:var(--display);font-size:24px;font-weight:600;letter-spacing:-.02em;margin-bottom:7px;}
.acceso-caja > p{color:var(--texto-2);font-size:13.5px;line-height:1.65;margin-bottom:22px;}
.acceso-caja input{
  width:100%;background:var(--panel-2);border:1px solid var(--linea-2);border-radius:var(--r-s);
  padding:11px 13px;font-size:14px;outline:0;margin-bottom:10px;transition:border-color .15s;
}
.acceso-caja input:focus{border-color:var(--oro-borde);}
.acceso-caja input.codigo{text-align:center;font-family:var(--mono);font-size:22px;letter-spacing:.34em;}
.acceso-caja button[type=submit]{
  width:100%;padding:11px;border-radius:var(--r-s);font-size:14px;font-weight:600;color:var(--sobre-oro);
  background:var(--oro-degradado);
}
.acceso-caja .aviso{font-size:12px;color:var(--texto-2);margin-top:14px;line-height:1.6;}
.acceso-caja .error{color:var(--rojo-texto);font-size:12.5px;margin-bottom:10px;}
.acceso-caja .legal{margin-top:18px;}

/* ============ RESPONSIVE ============ */
@media (max-width:1180px){
  .app{grid-template-columns:var(--barra-ancho) minmax(0,1fr);}
  .riel{display:none;}
  .app.riel-visible{grid-template-columns:var(--barra-ancho) minmax(0,1fr) var(--riel-ancho);}
  .app.riel-visible .riel{display:block;}
}
@media (max-width:820px){
  .app,.app.sin-riel,.app.riel-visible{grid-template-columns:minmax(0,1fr);}
  .barra{
    position:fixed;inset:0 auto 0 0;width:min(300px,86vw);z-index:50;
    transform:translateX(-100%);box-shadow:var(--sombra-borde);
  }
  .app.barra-visible .barra{transform:none;pointer-events:auto;}
  .riel{display:none;}
  .hilo{padding:18px 14px 8px;}
  .pie-chat{padding:8px 14px 12px;}
  .burbuja{max-width:88%;}
  .adjuntos-de-burbuja{max-width:100%;}
}

/* ============ RENGLÓN DE CONVERSACIÓN — el enlace es el área de clic ============ */
.conv-enlace{display:flex;align-items:center;gap:8px;flex:1;min-width:0;color:inherit;}

/* ============ DIÁLOGO NATIVO (renombrar) ============ */
.dialogo{
  border:1px solid var(--linea-2);border-radius:var(--r-l);
  background:var(--panel-3);color:var(--texto);
  padding:20px;width:min(400px,92vw);margin:auto;  /* ver la nota de .buscador-modal */
  box-shadow:var(--sombra-alta);
}
.dialogo::backdrop{background:var(--velo);}
.dialogo h3{font-family:var(--display);font-size:16px;font-weight:600;margin-bottom:13px;}
.dialogo input{
  width:100%;background:var(--panel-2);border:1px solid var(--linea-2);border-radius:var(--r-s);
  padding:10px 12px;font-size:14px;outline:0;transition:border-color .15s;
}
.dialogo input:focus{border-color:var(--oro-borde);}
.dialogo-pie{display:flex;justify-content:flex-end;gap:8px;margin-top:15px;}
.dialogo-pie button{
  padding:8px 15px;border-radius:var(--r-s);font-size:13px;color:var(--texto-2);
  border:1px solid var(--linea-2);transition:background .15s,color .15s;
}
.dialogo-pie button:hover{background:var(--panel-2);color:var(--texto);}
.dialogo-pie .principal{
  background:var(--oro-degradado);color:var(--sobre-oro);
  border-color:transparent;font-weight:600;
}

/* ============ AJUSTES DE MAQUETACIÓN ============ */
/*
  El espaciado del texto de ARIA iba con `p+p`, así que un párrafo después de una
  cita o de una lista quedaba pegado al bloque anterior. La regla general cubre
  cualquier combinación sin enumerarlas una por una.
*/
.msg-texto > * + *{margin-top:11px;}
.msg-texto > h1,.msg-texto > h2,.msg-texto > h3{margin-top:20px;}

/*
  El botón de mostrar la barra vive en la cabecera del chat y solo tiene sentido
  cuando la barra está plegada: con la barra a la vista duplicaba el control que ya
  está dentro de ella.
*/
#mostrar-barra{display:none;}
.app.sin-barra #mostrar-barra{display:inline-flex;}
@media (max-width:820px){
  #mostrar-barra{display:inline-flex;}
  /* A este ancho el riel no cabe y la media query lo oculta: su botón abriría
     algo que no puede verse, así que también se retira. */
  #plegar-riel{display:none;}
}

/* ============ GRÁFICAS DE MERCADO (§11) ============ */
/* Van entre la respuesta y las fuentes: la gráfica es la evidencia de lo que se afirma.
   El alto del lienzo es fijo y en CSS —no en el JS— porque el canvas de Lightweight
   Charts necesita un contenedor con altura antes de dibujarse: sin ella nace con 0 px y
   la gráfica no aparece, sin ningún error. */
.graficas{display:flex;flex-direction:column;gap:12px;margin:14px 0 4px}
.grafica{
  margin:0;border:1px solid var(--linea);border-radius:var(--r-m);
  background:var(--panel-2);overflow:hidden;
}
.grafica-cab{
  display:flex;align-items:baseline;justify-content:space-between;gap:12px;
  padding:10px 14px 8px;flex-wrap:wrap;
}
.grafica-activo{display:flex;align-items:baseline;gap:2px;font-family:var(--body)}
.grafica-activo b{font-size:15px;font-weight:var(--fw-semibold);color:var(--texto)}
.grafica-par{font-size:13px;color:var(--texto-3)}
.grafica-intervalo{
  margin-left:8px;padding:1px 7px;border-radius:999px;
  background:var(--panel-3);color:var(--texto-2);
  font-size:11px;font-family:var(--mono);text-transform:uppercase;letter-spacing:.04em;
}
.grafica-cifras{display:flex;align-items:baseline;gap:8px}
.grafica-precio{font-family:var(--mono);font-size:15px;color:var(--texto)}
.grafica-cambio{font-family:var(--mono);font-size:12.5px}
/* Verde y rojo aquí sí, y es la única excepción a reservar el color para lo accionable:
   en un mercado la dirección ES el dato, y leerla exige distinguirla de un vistazo. */
.grafica-cambio.sube{color:var(--esmeralda-texto)}
.grafica-cambio.baja{color:var(--rojo-texto)}
.grafica-cambio.plana{color:var(--texto-3)}
.grafica-lienzo{width:100%;height:260px}
.grafica-indicadores{
  display:flex;flex-wrap:wrap;gap:6px 18px;list-style:none;margin:0;
  padding:9px 14px;border-top:1px solid var(--linea);
}
.grafica-indicadores li{font-family:var(--mono);font-size:12px;color:var(--texto-2)}
.grafica-indicadores span{color:var(--texto-3);margin-right:6px}
.grafica-pie{
  padding:8px 14px 10px;border-top:1px solid var(--linea);
  font-size:11.5px;color:var(--texto-3);line-height:1.45;
}
@media (max-width:560px){
  .grafica-lienzo{height:200px}
  .grafica-cab{gap:6px}
}

/* ============ FUENTES DE INTERNET (§11) ============ */
/* Deliberadamente distinto de `.fuentes`, que son citas del acervo: el acervo lo curó el
   equipo y esto son afirmaciones de terceros. Si las dos se pintaran igual, la confianza
   en las buenas bajaría al nivel de las otras.
   Va abierto y numerado —no plegado— porque las citas del texto apuntan aquí: una lista
   cerrada obligaría a abrirla antes de poder seguir un enlace. */

/* La cita en el cuerpo del texto. Superíndice discreto: tiene que poderse ignorar al leer
   y encontrarse al querer comprobar. */
.msg-texto sup.cita{line-height:0;margin-left:2px}
/* Dos detalles de geometría, y sin ellos el pill sale aplastado con el dígito fuera:
   el `line-height` va en px porque el `sup` lo pone a 0 —para no ensanchar el renglón— y
   el ancla lo hereda, así que la caja mediría cero de alto. Y el alzado es `top`, no
   `vertical-align:super`: una caja de 16 px alineada como superíndice cuenta para el alto
   de la línea y separaría los párrafos; el posicionamiento relativo no. */
.msg-texto sup.cita a{
  display:inline-block;min-width:16px;padding:0 4px;
  border-radius:4px;background:var(--oro-tenue);
  border:1px solid var(--oro-borde);
  color:var(--oro-texto-fuerte);font-family:var(--mono);font-size:10.5px;
  line-height:14px;text-align:center;text-decoration:none;
  vertical-align:baseline;position:relative;top:-.4em;
}
.msg-texto sup.cita a:hover{background:var(--oro);border-color:var(--oro);color:var(--sobre-oro)}

.fuentes-web{margin-top:14px;border-top:1px solid var(--linea);padding-top:11px}
.fuentes-web-cab{
  display:flex;align-items:center;gap:7px;flex-wrap:wrap;
  margin:0 0 9px;font-family:var(--body);font-size:12.5px;
  font-weight:var(--fw-medium);color:var(--texto-2);
}
.fuentes-web-aviso{font-size:11px;font-weight:var(--fw-regular);color:var(--texto-3)}
.fuentes-web-lista{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}
.fuentes-web-lista li{display:flex;gap:9px;align-items:baseline;scroll-margin-top:80px}
/* `scroll-margin-top` para que al seguir una cita el renglón no quede pegado al borde
   superior del hilo, medio tapado por la cabecera. */
.fuente-web-num{
  flex:none;min-width:17px;padding:1px 4px;border-radius:4px;
  background:var(--panel-3);color:var(--texto-3);
  font-family:var(--mono);font-size:10px;text-align:center;
}
.fuente-web-cuerpo{min-width:0}
.fuentes-web-lista a{color:var(--texto-2);text-decoration:none;font-size:13px;line-height:1.4}
.fuentes-web-lista a:hover{color:var(--texto);text-decoration:underline}
.fuente-web-meta{display:block;margin-top:2px;font-family:var(--mono);font-size:11px;color:var(--texto-3)}
/* Ámbar de aviso, no de error: un comunicado de prensa no es una avería, es material del
   propio proyecto y hay que leerlo sabiéndolo. */
.fuente-web-marca{
  display:inline-block;margin-top:3px;padding:1px 7px;border-radius:999px;
  background:var(--ambar-tenue);color:var(--ambar-texto);
  font-size:10.5px;letter-spacing:.02em;
}
.fuentes-web-lista li.promocional a{color:var(--texto-3)}
/* El destino de una cita se ilumina un instante: sin eso, seguir [7] en una lista de
   catorce deja al lector buscando cuál era. */
.fuentes-web-lista li:target .fuente-web-num{background:var(--oro-tenue);color:var(--oro-texto)}
