/* FrontalCortexAI — sistema visual.
 *
 * Oscuro y comprometido: es un panel de instrumentos que se mira durante
 * horas, no una web de marketing. Un solo acento (ámbar de señal) sobre
 * grises fríos; el color se reserva para el estado, nunca para decorar.
 *
 * Sin fuentes externas a propósito: el VPS sirve estático detrás de Caddy y
 * una petición a un CDN es una dependencia de terceros en la ruta crítica de
 * la primera pintada.
 */

:root {
  /* Rejilla de la página. La cabecera, el menú y el pie ocupan el ancho
     completo —su línea inferior tiene que cruzar la pantalla— pero su
     contenido se alinea con el de `main`, que está limitado y centrado. */
  --ancho-contenido: 1280px;
  --margen-lateral: clamp(20px, 4vw, 48px);
  --margen-alineado: max(
    var(--margen-lateral),
    calc((100% - var(--ancho-contenido)) / 2 + var(--margen-lateral))
  );

  /* Superficies: casi negro con un punto de azul, no gris puro. */
  --fondo:            #05070a;
  --fondo-elevado:    #0d1117;
  --fondo-hundido:    #030507;
  --borde:            #1c212b;
  --borde-vivo:       #2a323f;

  /* Texto: nunca blanco puro sobre oscuro, cansa la vista. */
  --texto:            #e6e9ef;
  --texto-medio:      #98a1b2;
  --texto-tenue:      #5d6675;

  /* Dos acentos con semántica estricta, no decorativa:
     - ámbar  = señal operativa (estados, flujo, marca)
     - cian   = cognición del modelo (todo lo que la IA escribió o decidió)
     La regla que sostiene el lenguaje: si un texto lo produjo el motor, lleva
     cian en algún punto; si lo midió el sistema, no. Así se puede saber de un
     vistazo qué es dato y qué es juicio del modelo. */
  --senal:            #e8b464;
  --senal-suave:      rgba(232, 180, 100, 0.14);
  --ia:               #6fd3e8;
  --ia-suave:         rgba(111, 211, 232, 0.12);
  --ia-borde:         rgba(111, 211, 232, 0.35);
  --ia-brillo:        rgba(111, 211, 232, 0.22);

  /* Estados. Deliberadamente distinguibles también por posición y forma,
     no solo por color: el daltonismo rojo-verde es el más común. */
  --estado-idle:      #3f4756;
  --estado-active:    #e8b464;
  --estado-success:   #5ec48a;
  --estado-warning:   #e0a33a;
  --estado-error:     #e2685f;

  /* Derivados: tintes y lineas. Existen para que el tema claro sea un
     cambio de paleta y no una reescritura de reglas. */
  --linea:              rgba(28, 33, 43, 0.7);
  --fondo-activo:       #14161c;
  --malla-linea:        rgba(255, 255, 255, 0.016);
  --malla-linea-viva:   rgba(232, 180, 100, 0.055);
  --chapa-alto:         #151a22;
  --chapa-bajo:         #0d1116;
  --brillo-cabecera:    rgba(232, 180, 100, 0.03);
  --brillo-diagrama:    rgba(232, 180, 100, 0.035);
  --borde-exito:        rgba(94, 196, 138, 0.5);
  --borde-error:        rgba(226, 104, 95, 0.5);
  --borde-aviso:        rgba(224, 163, 58, 0.5);
  --tinte-exito:        rgba(94, 196, 138, 0.12);
  --tinte-error:        rgba(226, 104, 95, 0.16);
  --tinte-error-suave:  rgba(226, 104, 95, 0.07);
  --tinte-aviso:        rgba(224, 163, 58, 0.14);
  --tinte-aviso-suave:  rgba(224, 163, 58, 0.06);
  --tinte-neutro:       rgba(255, 255, 255, 0.05);
  --sombra:             none;
  /* Realce del canto de una pieza sobre fondo hundido. En oscuro el
     contraste entre superficies es débil y hace falta dibujar el borde
     superior con luz en vez de con una línea. */
  --sombra-pieza:       inset 0 1px 0 rgba(255, 255, 255, 0.04);

  --radio:            10px;
  --radio-chico:      6px;

  /* Space Grotesk para hablar, JetBrains Mono para medir. La serif se retira:
     era voz de editorial y esto es un instrumento. */
  --display: "Space Grotesk", -apple-system, "Segoe UI", sans-serif;
  --serif: "Space Grotesk", -apple-system, "Segoe UI", sans-serif;
  --sans: "Space Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

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

/* Micro-rótulo técnico: la tipografía de instrumento que cose la interfaz. */
.rotulo-tec {
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--texto-tenue);
}

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

body {
  margin: 0;
  min-height: 100vh;
  /* Bóveda: la luz cae desde arriba al centro, como en una sala de control.
     Fija, para que el scroll mueva el contenido y no la iluminación. */
  background:
    radial-gradient(120% 70% at 50% -10%, rgba(232, 180, 100, 0.05), transparent 60%),
    radial-gradient(90% 60% at 85% 110%, rgba(111, 211, 232, 0.04), transparent 55%),
    var(--fondo);
  background-attachment: fixed;
  color: var(--texto);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  display: flex;
  flex-direction: column;
}

/* Filo de luz en el canto superior de cada panel: un degradado de un píxel
   que sugiere una superficie iluminada desde arriba. Es el detalle que separa
   una tarjeta plana de una pieza de instrumento; a plena atención ni se ve. */
.panel { position: relative; }
.panel::before {
  content: '';
  position: absolute;
  top: 0;
  left: 8%;
  right: 8%;
  height: 1px;
  pointer-events: none;
  background: linear-gradient(90deg, transparent, var(--senal-suave) 30%, var(--senal-suave) 70%, transparent);
}

/* Malla de fondo: da profundidad sin dibujar nada. Muy tenue a propósito;
   si se nota, está mal. */
.malla {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background-image:
    linear-gradient(var(--malla-linea) 1px, transparent 1px),
    linear-gradient(90deg, var(--malla-linea) 1px, transparent 1px);
  background-size: 44px 44px;
  mask-image: radial-gradient(ellipse 90% 60% at 50% 0%, #000 40%, transparent 100%);
}

/* --- Cabecera ------------------------------------------------------------ */

.cabecera {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
  /* El fondo y la línea siguen cruzando la pantalla entera; lo que se alinea
     con el contenido es lo de dentro. De ahí el padding calculado en vez de
     un contenedor extra en el HTML. */
  padding: 22px var(--margen-alineado);
  border-bottom: 1px solid var(--borde);
  background: linear-gradient(180deg, var(--brillo-cabecera), transparent);
}

/* El isotipo se alinea con el RÓTULO, no con el bloque de dos líneas.
   Con `align-items: center` quedaba centrado contra el conjunto —rótulo más
   nombre del cliente— y parecía pertenecer a la segunda línea, que es la que
   cambia según a quién se observe. El isotipo es de la marca y se alinea con
   la marca. */
.marca { display: flex; align-items: flex-start; gap: 13px; }

/* Telemetría del motor. El sistema entero depende del juicio de un modelo:
   cuál es y si está pensando no puede ser un dato enterrado en una pestaña.
   Vive en la cabecera, como el cuentarrevoluciones en un tablero. */
.motor {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 14px;
  border: 1px solid var(--borde);
  border-radius: 999px;
  background:
    linear-gradient(180deg, rgba(111, 211, 232, 0.045), transparent 65%),
    var(--fondo-hundido);
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--texto-medio);
  white-space: nowrap;
}
.motor-nucleo {
  position: relative;
  width: 9px;
  height: 9px;
  flex: none;
}
.motor-nucleo::before,
.motor-nucleo::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: var(--ia);
}
.motor-nucleo::after {
  animation: nucleo 2.2s ease-out infinite;
}
/* Cuando el motor está ejecutando de verdad, el pulso se acelera: la
   diferencia entre «en escucha» y «analizando» se ve sin leer nada. */
.motor[data-pensando="true"] .motor-nucleo::after { animation-duration: .8s; }
.motor[data-pensando="true"] .motor-modelo { color: var(--ia); }
@keyframes nucleo {
  0%   { opacity: .7; transform: scale(1); }
  100% { opacity: 0;  transform: scale(2.6); }
}
.motor-modelo { color: var(--texto); transition: color .3s ease; }
.motor-sep { color: var(--texto-tenue); opacity: .6; }
.motor-detalle { color: var(--texto-tenue); }
@media (max-width: 900px) { .motor-detalle, .motor-sep { display: none; } }
@media (prefers-reduced-motion: reduce) { .motor-nucleo::after { animation: none; } }

/* Sello de la cabecera. El logotipo son 91 puntos rellenos, no anillos con
   trazo: el color va por `currentColor` desde aquí, así el mismo fichero
   sirve en tema claro y oscuro sin duplicarlo. */
.sello {
  /* A 34 px, junto a una tipografía tan ancha como Bruno Ace, el punteado se
     leía como una mota. Con 44 px se distinguen los puntos dispersos del
     borde, que son los que cuentan de qué va la marca. */
  width: 44px;
  height: 44px;
  flex: none;
  color: var(--senal);
  /* Alineado por el CENTRO ÓPTICO de las letras, no por la caja de la línea.
     La caja de un renglón incluye el espacio para las bajantes —que en
     «FrontalCORTEX» casi no existen— así que su centro cae más abajo que el
     centro real de la tinta. Medido sobre la fuente ya cargada: el centro de
     la tinta está 2,9 px por debajo del centro de la caja, y este margen es
     esa diferencia. Si se cambia la tipografía o el cuerpo del rótulo, hay
     que volver a medirlo. */
  margin-top: -6px;
}


.cabecera h1 { margin: 0; }

.marca-ia {
  color: var(--senal);
  font-weight: 400;
}

.marca-sub {
  margin: 2px 0 0;
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--texto-medio);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.pulso {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 12.5px;
  color: var(--texto-medio);
  font-variant-numeric: tabular-nums;
}
.pulso-sep { color: var(--texto-tenue); }
.pulso-ciclo b { color: var(--texto); font-family: var(--mono); font-weight: 500; }

.pulso-punto {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--estado-idle);
  flex: none;
  position: relative;
}
.pulso-punto[data-estado="vivo"] { background: var(--estado-success); }
.pulso-punto[data-estado="vivo"]::after {
  content: "";
  position: absolute; inset: -4px;
  border-radius: 50%;
  border: 1px solid var(--estado-success);
  animation: latido 2.4s ease-out infinite;
}
.pulso-punto[data-estado="caido"] { background: var(--estado-error); }

@keyframes latido {
  0%   { transform: scale(0.7); opacity: 0.9; }
  100% { transform: scale(1.7); opacity: 0; }
}

/* --- Estructura ---------------------------------------------------------- */

main {
  position: relative;
  z-index: 1;
  flex: 1;
  width: 100%;
  max-width: var(--ancho-contenido);
  margin: 0 auto;
  padding: clamp(20px, 3vw, 34px) var(--margen-lateral) 48px;
  display: flex;
  flex-direction: column;
  gap: 22px;
}

/* --- Cifras -------------------------------------------------------------- */

/* Seis cifras en 3×2. `auto-fit` las repartía según sobrara ancho y podía
   dejar una sola colgando en la segunda fila, que es la forma más rápida de
   que un panel parezca inacabado. */
.cifras {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
}

@media (max-width: 980px) { .cifras { grid-template-columns: repeat(2, 1fr); } }

/* En móvil siguen siendo dos columnas, no una pila.
   Apiladas, las seis cifras ocupaban tres pantallas y media y había que
   desplazarse para comparar dos números que existen precisamente para leerse
   juntos: el volumen no dice nada sin el sentimiento al lado. En dos columnas
   entran cuatro de un vistazo. Lo que se reduce es el cuerpo de la cifra, no
   el número de columnas. */
@media (max-width: 560px) {
  .cifras { grid-template-columns: repeat(2, 1fr); gap: 10px; }
  .cifra { padding: 13px 14px; }
  .cifra-etiqueta { font-size: 9.5px; letter-spacing: .05em; }
  .cifra-valor { font-size: 27px; }
  .cifra-valor--texto { font-size: 17px; }
  .cifra-pie { font-size: 11px; line-height: 1.35; }
}

.cifra {
  background: var(--fondo-elevado);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  padding: 16px 18px;
  position: relative;
  overflow: hidden;
}
/* Filo superior: detalle de instrumento, apenas visible. */
.cifra::before {
  content: "";
  position: absolute; top: 0; left: 0; right: 0; height: 1px;
  background: linear-gradient(90deg, transparent, var(--borde-vivo) 30%, transparent);
}

.cifra-etiqueta {
  margin: 0;
  font-size: 11px;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--texto-tenue);
}

.cifra {
  position: relative;
}
/* Marca de esquina única, arriba a la izquierda: registro de visor, no marco
   completo. Cuatro esquinas serían disfraz; una es firma. */
.cifra::before {
  content: '';
  position: absolute;
  top: 10px;
  left: 10px;
  width: 9px;
  height: 9px;
  border-top: 1px solid var(--senal);
  border-left: 1px solid var(--senal);
  opacity: .45;
}

.cifra-valor {
  text-shadow: 0 0 22px var(--senal-suave);
  margin: 8px 0 4px;
  font-family: var(--mono);
  font-size: 42px;
  font-weight: 500;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  font-weight: 500;
}
.cifra-valor--texto { font-size: 19px; font-family: var(--sans); }
.cifra-valor[data-tono="alerta"] { color: var(--estado-error); }
.cifra-valor[data-tono="aviso"]  { color: var(--estado-warning); }
.cifra-valor[data-tono="bien"]   { color: var(--estado-success); }

.cifra-pie { margin: 0; font-size: 12px; color: var(--texto-medio); }

/* --- Paneles ------------------------------------------------------------- */

.panel {
  background: var(--fondo-elevado);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  overflow: hidden;
}

.panel-cabecera {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 14px;
  padding: 15px 18px;
  border-bottom: 1px solid var(--borde);
}

.panel-cabecera h2 {
  margin: 0;
  font-family: var(--display);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
/* Guion corto de acento delante del título: firma visual de cada panel. */
.panel-cabecera h2::before {
  content: '';
  display: inline-block;
  width: 14px;
  height: 2px;
  margin-right: 9px;
  vertical-align: middle;
  background: var(--senal);
}

.panel-nota {
  margin: 0;
  font-size: 11.5px;
  color: var(--texto-tenue);
  letter-spacing: 0.05em;
}

.columnas {
  display: grid;
  grid-template-columns: 1.55fr 1fr;
  gap: 22px;
  align-items: start;
}

@media (max-width: 860px) {
  .columnas { grid-template-columns: 1fr; }
}

/* --- Diagrama ------------------------------------------------------------ */

.diagrama-caja {
  position: relative;
  padding: 16px 10px 8px;
  overflow: hidden;
  background:
    radial-gradient(ellipse 55% 75% at 50% 45%, var(--brillo-diagrama), transparent 72%),
    var(--fondo-hundido);
}

/* Barrido lento sobre el lienzo. Muy tenue a propósito: da la sensación de
   instrumento vivo sin convertirse en un elemento que pide atención cada vez
   que pasa, que es lo que cansa de este tipo de efecto. */
.diagrama-caja::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(100deg, transparent 42%, var(--senal-suave) 50%, transparent 58%);
  opacity: .5;
  animation: barrido 9s ease-in-out infinite;
}
@keyframes barrido {
  0%, 100% { transform: translateX(-60%); }
  50%      { transform: translateX(60%); }
}
@media (prefers-reduced-motion: reduce) {
  .diagrama-caja::after { animation: none; opacity: 0; }
}

.rejilla-linea { stroke: var(--malla-linea-viva); stroke-width: 1; }
.malla-diagrama { pointer-events: none; }

.chapa-alto { stop-color: var(--chapa-alto); }
.chapa-bajo { stop-color: var(--chapa-bajo); }

/* El alto lo fija el guion a partir del viewBox; aquí solo se garantiza que
   nunca desborde el panel. */
#diagrama { display: block; width: 100%; max-width: 100%; }

.arista {
  fill: none;
  stroke: var(--borde-vivo);
  stroke-width: 1.25;
  stroke-linecap: round;
  opacity: .8;
}
/* Cuando una etapa corre, el flujo hacia ella se dibuja moviéndose. Es lo
   que hace visible que la IA está trabajando y no solo esperando. */
.arista-flujo {
  fill: none;
  stroke: var(--senal);
  stroke-width: 1.5;
  stroke-dasharray: 5 9;
  opacity: 0;
  transition: opacity .3s ease;
}
.arista-flujo.corriendo {
  opacity: .9;
  filter: url(#brillo);
  animation: fluir 1.1s linear infinite;
}
@keyframes fluir { to { stroke-dashoffset: -28; } }

/* Paquete que viaja por la arista. Existe solo mientras la etapa destino
   trabaja: el resto del tiempo estorbaría. */
/* La opacidad la conduce la animación SMIL —sube mientras el paquete acelera
   y se apaga al llegar—, así que aquí solo se controla si el paquete existe
   visualmente y cuánto brilla. Poner `opacity` en CSS pisaría la animación. */
.paquete {
  fill: var(--senal);
  transition: filter .3s ease;
}
.paquete:not(.corriendo) { opacity: .18 !important; }
.paquete.corriendo { filter: url(#brillo); }
@media (prefers-reduced-motion: reduce) { .paquete { opacity: 0; } .nodo-orbita { animation: none; } }

/* Núcleo orbital. El vocabulario: un anillo discontinuo que SIEMPRE gira
   —lento en reposo, rápido al procesar— alrededor de un núcleo que emite con
   el color del estado. Nada de cajas: las cajas con conectores se leen como
   un diagrama de base de datos, y esto es un sistema vivo. */
.nodo-orbita {
  fill: none;
  stroke: var(--borde-vivo);
  stroke-width: 1.4;
  transform-box: fill-box;
  transform-origin: center;
  animation: orbitar 40s linear infinite;
  transition: stroke .3s ease;
}
@keyframes orbitar { to { transform: rotate(360deg); } }
.nodo[data-estado="active"] .nodo-orbita {
  stroke: var(--estado-active);
  animation-duration: 2.6s;
}
.nodo[data-estado="success"] .nodo-orbita { stroke: var(--borde-exito); }
.nodo[data-estado="warning"] .nodo-orbita { stroke: var(--borde-aviso); }
.nodo[data-estado="error"]   .nodo-orbita { stroke: var(--estado-error); animation-duration: 6s; }

.nodo-anillo {
  fill: var(--fondo-elevado);
  stroke: var(--borde);
  stroke-width: 1;
}

.nodo-nucleo {
  fill: var(--estado-idle);
  transition: fill .3s ease;
}
.nodo[data-estado="active"]  .nodo-nucleo { fill: var(--estado-active); }
.nodo[data-estado="success"] .nodo-nucleo { fill: var(--estado-success); }
.nodo[data-estado="warning"] .nodo-nucleo { fill: var(--estado-warning); }
.nodo[data-estado="error"]   .nodo-nucleo { fill: var(--estado-error); }

.nodo-indice {
  fill: var(--texto-tenue);
  font-family: var(--mono);
  font-size: 8.5px;
  letter-spacing: .1em;
}
.nodo[data-estado="active"] .nodo-indice { fill: var(--estado-active); }

/* Halo del diodo: el nodo emite luz en vez de estar pintado de color. */
.nodo-halo {
  fill: none;
  stroke: var(--estado-active);
  stroke-width: 2;
  opacity: 0;
}
.nodo[data-estado="active"] .nodo-halo {
  opacity: .9;
  filter: url(#brillo);
  animation: latido 1.4s ease-in-out infinite;
}
@keyframes latido {
  0%, 100% { opacity: .35; stroke-width: 1.5; }
  50%      { opacity: 1;   stroke-width: 3; }
}
.nodo[data-estado="active"]  .nodo-caja { stroke: var(--estado-active); }
.nodo[data-estado="success"] .nodo-caja { stroke: var(--borde-exito); }
.nodo[data-estado="warning"] .nodo-caja { stroke: var(--borde-aviso); }
.nodo[data-estado="error"]   .nodo-caja { stroke: var(--estado-error); }

.nodo-titulo {
  fill: var(--texto);
  font-family: var(--sans);
  font-size: 12.5px;
  font-weight: 500;
}
.nodo-detalle {
  fill: var(--texto-tenue);
  font-family: var(--sans);
  font-size: 10px;
}
.nodo-ms {
  fill: var(--texto-medio);
  font-family: var(--mono);
  font-size: 9.5px;
  font-variant-numeric: tabular-nums;
}

/* Título centrado bajo el núcleo, en la voz de la marca. */
.nodo-titulo { font-family: var(--display); font-weight: 600; }

.nodo-aura {
  fill: none;
  stroke: var(--estado-active);
  stroke-width: 1;
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
}
.nodo[data-estado="active"] .nodo-aura { animation: aura 1.8s ease-out infinite; }
@keyframes aura {
  0%   { opacity: .6; transform: scale(1); }
  100% { opacity: 0;  transform: scale(1.9); }
}
@media (prefers-reduced-motion: reduce) {
  .nodo-aura, .nodo-halo, .arista-flujo.corriendo { animation: none; }
}

.leyenda {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
  margin: 0;
  padding: 12px 18px;
  list-style: none;
  border-top: 1px solid var(--borde);
  font-size: 11.5px;
  color: var(--texto-medio);
}
.leyenda li { display: flex; align-items: center; gap: 7px; }

.punto { width: 7px; height: 7px; border-radius: 50%; background: var(--estado-idle); }
.punto[data-estado="active"]  { background: var(--estado-active); }
.punto[data-estado="success"] { background: var(--estado-success); }
.punto[data-estado="warning"] { background: var(--estado-warning); }
.punto[data-estado="error"]   { background: var(--estado-error); }

/* --- Flujo de actividad -------------------------------------------------- */

.flujo {
  margin: 0;
  padding: 6px 0;
  list-style: none;
  max-height: 380px;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--borde-vivo) transparent;
}

.flujo-linea {
  display: grid;
  grid-template-columns: 62px 96px 1fr auto;
  gap: 12px;
  align-items: baseline;
  padding: 8px 18px;
  font-size: 12.5px;
  border-left: 2px solid transparent;
  animation: entrar .35s ease both;
}
.flujo-linea + .flujo-linea { border-top: 1px solid var(--linea); }
.flujo-linea[data-estado="active"]  { border-left-color: var(--estado-active); }
.flujo-linea[data-estado="success"] { border-left-color: var(--estado-success); }
.flujo-linea[data-estado="warning"] { border-left-color: var(--estado-warning); }
.flujo-linea[data-estado="error"]   { border-left-color: var(--estado-error); background: var(--tinte-error-suave); }

@keyframes entrar {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}

.flujo-hora  { font-family: var(--mono); font-size: 11px; color: var(--texto-tenue); font-variant-numeric: tabular-nums; }
.flujo-nodo  { color: var(--texto); font-weight: 500; }
.flujo-texto { color: var(--texto-medio); overflow-wrap: anywhere; }
.flujo-ms    { font-family: var(--mono); font-size: 11px; color: var(--texto-tenue); font-variant-numeric: tabular-nums; }
.flujo-vacio { padding: 22px 18px; color: var(--texto-tenue); font-size: 13px; }

/* --- Integraciones ------------------------------------------------------- */

.integraciones { margin: 0; padding: 6px 0; list-style: none; }

.integracion {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 18px;
  font-size: 13px;
}
.integracion + .integracion { border-top: 1px solid var(--linea); }
.integracion-nombre { flex: 1; }
.integracion-estado { font-size: 11.5px; color: var(--texto-tenue); letter-spacing: 0.04em; }
.integracion[data-activa="true"] .integracion-estado { color: var(--estado-success); }

.aviso {
  margin: 0;
  padding: 12px 18px;
  border-top: 1px solid var(--borde);
  background: var(--tinte-aviso-suave);
  color: var(--texto-medio);
  font-size: 12.5px;
  line-height: 1.45;
}

/* Respeta a quien pidió menos movimiento: aquí hay mucha animación y para
   algunas personas es un problema real, no una preferencia estética. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  .arista-flujo.corriendo { opacity: .85; }
}

/* --- Menú ---------------------------------------------------------------- */

.menu {
  position: sticky;
  top: 0;
  z-index: 5;
  backdrop-filter: blur(14px) saturate(1.3);
  -webkit-backdrop-filter: blur(14px) saturate(1.3);
  display: flex;
  gap: 2px;
  /* Centrado. Antes se alineaba a la izquierda con el contenido y, con catorce
     secciones, el último enlace acababa 200 px más a la derecha que el panel:
     el menú se veía descuadrado respecto de todo lo demás.
     `safe` evita que al desbordar se recorte el primer enlace, que es lo que
     pasa con un `center` a secas. */
  justify-content: center;
  justify-content: safe center;
  padding: 0 var(--margen-alineado);
  border-bottom: 1px solid var(--borde);
  /* Translúcido: el contenido pasa por debajo al hacer scroll y el desenfoque
     lo convierte en profundidad en vez de en ruido. */
  background: color-mix(in srgb, var(--fondo-hundido) 78%, transparent);
  overflow-x: auto;
  scrollbar-width: none;
}
.menu::-webkit-scrollbar { display: none; }

.menu a {
  position: relative;
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 13px 13px;
  color: var(--texto-medio);
  text-decoration: none;
  font-size: 13.5px;
  white-space: nowrap;
  border-bottom: 2px solid transparent;
  transition: color .18s ease, border-color .18s ease;
}
/* Corte visual antes del bloque de configuración —Bot, Alertas, Fuentes—:
   son secciones que se tocan una vez y se olvidan, y mezcladas con las de
   consulta diaria alargaban el menú sin aportar. */
.menu a[data-vista="bot"] {
  margin-left: 10px;
  padding-left: 20px;
  border-left: 1px solid var(--borde);
}

.menu a:hover { color: var(--texto); }
.menu a[aria-current="page"] {
  text-shadow: 0 0 18px var(--senal-suave);
  color: var(--texto);
  border-bottom-color: var(--senal);
}

.insignia {
  min-width: 18px;
  padding: 1px 6px;
  border-radius: 9px;
  background: var(--senal-suave);
  color: var(--senal);
  font-family: var(--mono);
  font-size: 10.5px;
  text-align: center;
}
.insignia--alerta { background: var(--tinte-error); color: var(--estado-error); }

.vista { display: flex; flex-direction: column; gap: 22px; }
.vista[hidden] { display: none; }

/* --- Filtros ------------------------------------------------------------- */

/* Fila de filtros.
   Era un flex con la etiqueta a la izquierda de cada control: al envolver,
   cada celda medía distinto —«Orden» junto a «Por alcance» ocupa otra cosa
   que «Plataforma» junto a «Todas»— y la segunda línea nunca coincidía con
   la primera. Rejilla de columnas iguales y etiqueta encima: todos los
   controles empiezan y terminan en la misma vertical, envuelvan o no, y la
   estructura es la misma en escritorio y en móvil. */
.filtros {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(172px, 1fr));
  gap: 12px 14px;
  align-items: end;
  padding: 14px 18px;
  border-bottom: 1px solid var(--borde);
}

.filtros label,
.filtros .campo-fecha {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  min-width: 0;
  font-size: 11px;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--texto-tenue);
}

.filtro-rotulo {
  font-size: 11px;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--texto-tenue);
}

/* Un solo alto para todo lo que se pueda pulsar en la fila: selectores,
   buscador y el disparador de fecha. Sin esto el calendario sobresalía dos
   píxeles y la fila se veía torcida sin que se supiera por qué. */
.filtros select,
.filtros input,
.filtros .fecha-disparador {
  width: 100%;
  height: 36px;
  box-sizing: border-box;
  background: var(--fondo-hundido);
  color: var(--texto);
  border: 1px solid var(--borde-vivo);
  border-radius: var(--radio-chico);
  padding: 0 11px;
  font-family: var(--sans);
  font-size: 13px;
  letter-spacing: 0;
  text-transform: none;
}
.filtros .fecha-disparador { justify-content: space-between; }
.filtros select:focus-visible,
.filtros input:focus-visible { outline: 2px solid var(--senal); outline-offset: 1px; }

/* --- Menciones ----------------------------------------------------------- */

.menciones { margin: 0; padding: 0; list-style: none; }

.mencion { padding: 15px 18px; display: grid; gap: 8px; }
.mencion + .mencion { border-top: 1px solid var(--linea); }

.mencion-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  font-size: 11.5px;
  color: var(--texto-tenue);
}
.mencion-autor { color: var(--texto-medio); font-weight: 500; }
.mencion-texto { margin: 0; font-size: 14px; line-height: 1.55; color: var(--texto); }
.mencion-texto a { color: var(--senal); }

.etiqueta {
  padding: 2px 8px;
  border-radius: 20px;
  border: 1px solid var(--borde-vivo);
  font-size: 10.5px;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--texto-medio);
}
.etiqueta--support   { border-color: var(--borde-exito); color: var(--estado-success); }
.etiqueta--oppose    { border-color: var(--borde-error); color: var(--estado-error); }
.etiqueta--mixed     { border-color: var(--borde-aviso); color: var(--estado-warning); }
.etiqueta--toxica    { border-color: var(--borde-error);  color: var(--estado-error); }
.etiqueta--pendiente { border-style: dashed; }

.marcos { display: flex; flex-wrap: wrap; gap: 6px; }
.marco {
  padding: 2px 8px;
  border-radius: var(--radio-chico);
  background: var(--senal-suave);
  color: var(--senal);
  font-size: 11px;
}

.alcance { font-family: var(--mono); font-variant-numeric: tabular-nums; }

/* --- Hallazgos ----------------------------------------------------------- */

.hallazgos { display: grid; gap: 1px; background: var(--linea); }

.hallazgo { padding: 16px 18px; background: var(--fondo-elevado); display: grid; gap: 9px; }
.hallazgo-cabecera { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.hallazgo-titulo { margin: 0; font-size: 15px; font-weight: 500; }
.hallazgo-resumen { margin: 0; font-size: 13.5px; color: var(--texto-medio); line-height: 1.55; }

.hallazgo-accion {
  margin: 0;
  padding: 10px 12px;
  background: var(--senal-suave);
  font-size: 13px;
}

.severidad {
  padding: 2px 8px;
  border-radius: 20px;
  font-size: 10.5px;
  letter-spacing: .05em;
  text-transform: uppercase;
}
.severidad[data-nivel="critical"] { background: var(--tinte-error); color: var(--estado-error); }
.severidad[data-nivel="high"]     { background: var(--tinte-error-suave); color: var(--estado-error); }
.severidad[data-nivel="medium"]   { background: var(--tinte-aviso); color: var(--estado-warning); }
.severidad[data-nivel="low"]      { background: var(--tinte-exito); color: var(--estado-success); }
.severidad[data-nivel="info"]     { background: var(--tinte-neutro); color: var(--texto-medio); }

/* Barra de confianza: la cifra sola se lee como certeza. */
.confianza { display: flex; align-items: center; gap: 8px; font-size: 11.5px; color: var(--texto-tenue); }
.confianza-barra { width: 62px; height: 3px; border-radius: 2px; background: var(--borde-vivo); overflow: hidden; }
/* `display: block`: es un <span>, y en inline el ancho y el alto se ignoran.
   La barra se pintaba siempre vacía. */
.confianza-relleno { display: block; height: 100%; background: var(--senal); }

/* --- Series -------------------------------------------------------------- */

.series { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.serie {
  position: relative;
  padding: 18px;
  border-top: 1px solid var(--linea);
  border-right: 1px solid var(--linea);
}
/* Serie con muestra insuficiente: se atenúa entera y se marca el canto. Un
   50% sobre dos menciones no puede tener el mismo peso visual que uno sobre
   doscientas —era el fallo más grave de esta vista—. */
.serie[data-flojo="true"] { background: var(--tinte-aviso-suave); }


.serie-cabecera { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.serie-nombre { margin: 0; font-size: 12px; letter-spacing: .04em; color: var(--texto-medio); }
.serie-cifra { display: flex; align-items: baseline; gap: 9px; flex: none; }
.serie-valor {
  font-family: var(--mono);
  font-size: 30px;
  font-weight: 500;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  text-shadow: 0 0 22px var(--senal-suave);
}
/* La variación juzga: en toxicidad subir es malo y en sentimiento es bueno.
   El mismo triángulo verde para todo enseñaría lo contrario de lo que pasa. */
.serie-delta {
  font-family: var(--mono);
  font-size: 11px;
  padding: 2px 7px;
  border-radius: 20px;
  border: 1px solid var(--borde-vivo);
  white-space: nowrap;
}
.serie-delta[data-juicio="bien"] { color: var(--estado-success); border-color: var(--borde-exito); }
.serie-delta[data-juicio="mal"]  { color: var(--estado-error);   border-color: var(--borde-error); }
.serie-delta[data-juicio="neutro"] { color: var(--texto-medio); }

.serie-grafico { display: block; width: 100%; height: 92px; margin-top: 12px; overflow: visible; }

/* Color por naturaleza del indicador, no por decoración. */
.serie-linea { fill: none; stroke-width: 1.6; stroke-linejoin: round; }
.serie-linea--neutro     { stroke: var(--texto-medio); }
.serie-linea--divergente { stroke: var(--estado-success); }
.serie-linea--alarma     { stroke: var(--estado-error); }
.serie-area { stroke: none; }
.serie-area--neutro     { fill: var(--tinte-neutro); }
.serie-area--divergente { fill: var(--tinte-exito); }
.serie-area--alarma     { fill: var(--tinte-error-suave); }
.serie-punto--neutro     { fill: var(--texto); }
.serie-punto--divergente { fill: var(--estado-success); }
.serie-punto--alarma     { fill: var(--estado-error); }

/* Referencias del gráfico: sin una mediana y un cero, una línea que sube no
   dice si está alta o baja PARA ESTA serie. */
.serie-mediana {
  stroke: var(--borde-vivo);
  stroke-width: 1;
  stroke-dasharray: 3 4;
  opacity: .7;
}
.serie-cero { stroke: var(--texto-tenue); stroke-width: 1; opacity: .6; }
.serie-extremo { fill: none; stroke-width: 1.2; }
.serie-extremo--max { stroke: var(--senal); }
.serie-extremo--min { stroke: var(--texto-tenue); }
/* Puntos calculados sobre muestra insuficiente, marcados sobre la línea. */
.serie-flojo { fill: var(--estado-warning); opacity: .85; }
.serie-eje {
  fill: var(--texto-tenue);
  font-family: var(--mono);
  font-size: 7.5px;
  letter-spacing: .04em;
}

/* Fila de datos duros bajo el gráfico. */
.serie-datos {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--linea);
}
.serie-dato { display: flex; flex-direction: column; gap: 2px; }
.serie-dato-et {
  font-family: var(--mono);
  font-size: 8.5px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--texto-tenue);
}
.serie-dato-v {
  font-family: var(--mono);
  font-size: 13px;
  color: var(--texto);
  font-variant-numeric: tabular-nums;
}

.serie-aviso {
  margin: 10px 0 0;
  padding: 8px 11px;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--estado-warning);
  border: 1px solid var(--borde-aviso);
  border-radius: var(--radio-chico);
  background: var(--tinte-aviso-suave);
}
.serie-pie { margin: 8px 0 0; font-size: 11px; color: var(--texto-tenue); }

/* --- Tabla --------------------------------------------------------------- */

/* La caja ya tenía scroll horizontal, pero la tabla era `width: 100%` y por
   tanto nunca lo usaba: en vez de desbordar, comprimía. El resultado era una
   columna de consultas partiendo URLs a tres letras por línea y filas de
   ciento veinte píxeles de alto. Con un ancho mínimo, la tabla se mantiene
   legible y es la caja la que se desplaza. */
.tabla-caja { overflow-x: auto; }
.tabla { width: 100%; min-width: 860px; border-collapse: collapse; font-size: 13px; }

/* Una línea por celda. La consulta puede ser una URL larga o un booleano de
   tres cláusulas: se recorta y el texto completo queda en el tooltip. */
.tabla td { white-space: nowrap; }
.tabla td .tabla-consulta {
  max-width: 34ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Los botones se separan con margen y no con `display: flex` en la celda:
   un `td` en flex deja de ser celda de tabla y se sale de la rejilla —los
   botones aparecían fuera del panel, a la derecha del borde—. En móvil, donde
   la tabla ya no es tabla sino fichas, sí puede ser flex. */
.tabla-acciones .boton + .boton { margin-left: 8px; }
.tabla th {
  text-align: left;
  padding: 11px 18px;
  font-size: 10.5px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--texto-tenue);
  font-weight: 500;
  border-bottom: 1px solid var(--borde);
  white-space: nowrap;
}
.tabla td { padding: 12px 18px; border-bottom: 1px solid var(--linea); vertical-align: top; }
.tabla .num { text-align: right; font-family: var(--mono); font-variant-numeric: tabular-nums; }
.tabla-consulta { color: var(--texto-medio); font-family: var(--mono); font-size: 11.5px; overflow-wrap: anywhere; }
.tabla-error { color: var(--estado-error); font-size: 11.5px; }

/* En vertical una tabla de siete columnas no cabe de ninguna manera, y un
   scroll lateral en móvil esconde la mitad de los datos. Cada fila pasa a ser
   una ficha con sus rótulos, que es como se lee de todos modos en el pulgar. */
@media (max-width: 820px) {
  .tabla { min-width: 0; }
  .tabla thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .tabla, .tabla tbody, .tabla tr, .tabla td { display: block; }
  .tabla tr {
    padding: 14px 18px;
    border-bottom: 1px solid var(--linea);
  }
  .tabla td {
    display: grid;
    grid-template-columns: 92px minmax(0, 1fr);
    gap: 10px;
    align-items: baseline;
    padding: 3px 0;
    border: none;
    white-space: normal;
  }
  .tabla td::before {
    content: attr(data-rotulo);
    font-size: 10.5px;
    letter-spacing: .07em;
    text-transform: uppercase;
    color: var(--texto-tenue);
  }
  /* Todo el contenido de la celda va a la columna del valor. Sin esto, una
     celda con dos hijos —el nombre de la fuente y su «x · search»— empujaba
     el segundo a una fila nueva, bajo el rótulo. */
  .tabla td > * { grid-column: 2; }
  .tabla td .tabla-consulta { max-width: none; white-space: normal; }
  /* Las etiquetas de estado son chips, no barras: sin esto se estiran hasta
     el borde derecho y «OK» queda dentro de una cápsula de 300 px. */
  .tabla td .etiqueta { justify-self: start; }
  .tabla .num { text-align: left; }
  /* Sube la especificidad: `.tabla td { display: grid }` gana por sí solo a
     `.tabla-acciones` y dejaría los dos botones uno encima de otro. */
  .tabla td.tabla-acciones { display: flex; gap: 8px; margin-top: 8px; }
  .tabla td.tabla-acciones .boton + .boton { margin-left: 0; }
  .tabla td.tabla-acciones::before { display: none; }
  .tabla td.tabla-acciones .boton { flex: 1; justify-content: center; }
}

/* --- Ficha de Persona ---------------------------------------------------- */

.ficha { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.campo { padding: 15px 18px; border-top: 1px solid var(--linea); border-right: 1px solid var(--linea); }
.campo-nombre { margin: 0 0 6px; font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase; color: var(--texto-tenue); }
.campo-valor { margin: 0; font-size: 13.5px; line-height: 1.55; color: var(--texto); }
.campo-valor--vacio { color: var(--texto-tenue); font-style: italic; }
.campo-lista { display: flex; flex-wrap: wrap; gap: 6px; }

.aviso--fijo { border-top: none; border-bottom: 1px solid var(--borde); }

/* --- Vacíos -------------------------------------------------------------- */

.vacio { padding: 34px 18px; text-align: center; color: var(--texto-tenue); font-size: 13px; line-height: 1.6; }
.vacio b { color: var(--texto-medio); font-weight: 500; }


/* --- Barra de posturas ---------------------------------------------------- */

.barra {
  display: flex;
  height: 8px;
  margin-top: 12px;
  border-radius: 4px;
  overflow: hidden;
  background: var(--fondo-hundido);
}
.barra-tramo { transition: width .3s ease; }
.barra-tramo--support   { background: var(--estado-success); }
.barra-tramo--neutral   { background: var(--estado-idle); }
.barra-tramo--mixed     { background: var(--estado-warning); }
.barra-tramo--unrelated { background: var(--borde-vivo); }
.barra-tramo--oppose    { background: var(--estado-error); }


/* --- Vídeos --------------------------------------------------------------- */

/* Cada aparición es una pieza cerrada, no una fila de una lista.
   Separadas por una línea de un píxel, y con cinco secciones plegables
   dentro que dibujan sus propias líneas, no se distinguía dónde acababa un
   vídeo y empezaba el siguiente: todo parecía la misma retahíla de rótulos.
   Van sobre un fondo hundido y separadas, así que el límite lo marca el aire
   y el contraste en vez de un borde —que es lo que se pidió evitar—. */
.videos {
  display: grid;
  gap: 12px;
  padding: 14px 16px;
  background: var(--fondo-hundido);
}

.video {
  padding: 16px 18px;
  background: var(--fondo-elevado);
  border-radius: var(--radio);
  /* En claro la sombra ya distingue la tarjeta del fondo; en oscuro la
     variable es `none` y el límite lo da solo el contraste, que a esas
     luminancias es débil. Este realce interior dibuja el canto de la pieza
     sin añadir un borde. */
  box-shadow: var(--sombra-pieza);
  display: grid;
  gap: 12px;
}

.video-cabecera { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.video-titulo { margin: 0; font-size: 15px; font-weight: 500; }
.video-titulo a { color: inherit; text-decoration: none; border-bottom: 1px solid var(--borde-vivo); }
.video-titulo a:hover { border-bottom-color: var(--senal); }
.video-resumen { margin: 0; font-size: 13.5px; line-height: 1.6; color: var(--texto-medio); }

/* Dos líneas y corta. Con diecinueve apariciones, un resumen largo por ficha
   convierte el listado en un documento: hay que hacer scroll para ver de qué
   va la siguiente. Dos bastan para decidir si te detienes; el resto está a un
   clic. */
.video-resumen--recorte {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Fila bajo el resumen: la acción a la izquierda, la firma del motor a la
   derecha. */
.video-pie-resumen {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
.firma-ia {
  font-family: var(--mono);
  font-size: 8.5px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ia);
  opacity: .65;
}
/* Doble clase para ganarle a `.ia-marco::after`, que se declara más abajo en
   la hoja: a igual especificidad manda el último, y la firma salía dos veces. */
.ia-marco.ia-marco--sin-firma::after { content: none; }

.video-mas {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: 12px;
  color: var(--senal);
  cursor: pointer;
}
.video-mas:hover { text-decoration: underline; }

/* --- Secciones plegables de la ficha de vídeo ----------------------------- */

.seccion { border-top: 1px solid var(--linea); }
.seccion > summary {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 9px 0;
  cursor: pointer;
  list-style: none;
  font-size: 12px;
}
.seccion > summary::-webkit-details-marker { display: none; }
.seccion > summary::before {
  content: '▸';
  color: var(--senal);
  font-size: 10px;
}
.seccion[open] > summary::before { content: '▾'; }
.seccion-titulo {
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--texto-tenue);
}
/* La pista va a la derecha y en tono menor: informa del contenido cerrado sin
   competir con el rótulo de la sección. */
.seccion-pista {
  margin-left: auto;
  font-size: 11.5px;
  color: var(--texto-medio);
  text-align: right;
}
.seccion-cuerpo { padding: 4px 0 14px; }

/* Barras de desempeño: la cifra sola invita a leerla como medición. */
/* Seis dimensiones en 3×2. Con `auto-fit` el número de columnas dependía del
   ancho sobrante y una fila podía quedar con un solo elemento colgando. */
.desempeno { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px 20px; }
@media (max-width: 620px) { .desempeno { grid-template-columns: repeat(2, 1fr); } }
.dim { display: grid; gap: 4px; }
.dim-nombre { font-size: 10.5px; letter-spacing: .07em; text-transform: uppercase; color: var(--texto-tenue); }
.dim-fila { display: flex; align-items: center; gap: 8px; }
.dim-barra { flex: 1; height: 4px; border-radius: 2px; background: var(--fondo-hundido); overflow: hidden; }
.dim-relleno { display: block; height: 100%; background: var(--estado-success); }
/* La evasión se lee al revés: más alta es peor. */
.dim[data-invertida="true"] .dim-relleno { background: var(--estado-error); }
.dim-valor { font-family: var(--mono); font-size: 12px; font-variant-numeric: tabular-nums; color: var(--texto); }

.listas { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
@media (max-width: 720px) { .listas { grid-template-columns: 1fr; } }
.lista-titulo { margin: 0 0 6px; font-size: 10.5px; letter-spacing: .07em; text-transform: uppercase; color: var(--texto-tenue); }
.lista { margin: 0; padding-left: 18px; font-size: 13px; line-height: 1.55; color: var(--texto-medio); }
.lista li + li { margin-top: 6px; }
.lista--debil li::marker { color: var(--estado-error); }
.lista--fuerte li::marker { color: var(--estado-success); }

/* Momentos: la cita y su minuto, para poder verificar el juicio en la fuente. */
.momentos { display: grid; gap: 8px; }
.momento {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 12px;
  padding: 10px 12px;
  background: var(--senal-suave);
  border-radius: 0 var(--radio-chico) var(--radio-chico) 0;
}
/* Cian, no ámbar: el minuto lo eligió el modelo al citar. */
.momento-min { font-family: var(--mono); font-size: 12px; color: var(--ia); white-space: nowrap; }
.momento-cita { margin: 0; font-size: 13px; font-style: italic; }
.momento-por-que { margin: 4px 0 0; font-size: 12px; color: var(--texto-tenue); }

/* --- Tema claro -----------------------------------------------------------
 *
 * No es el oscuro invertido. Sobre blanco, el ámbar de señal (#e8b464) no
 * llega al contraste mínimo para texto, así que se oscurece a un ocre; los
 * estados también bajan de luminosidad para poder leerse. Las superficies son
 * papel cálido en vez de blanco puro, que sobre una pantalla grande deslumbra
 * en las mismas sesiones largas para las que se diseñó el modo oscuro.
 *
 * En claro sí hay sombras: sin ellas, sin el contraste del fondo oscuro, las
 * tarjetas se pierden contra la página.
 */

:root[data-tema="claro"] {
  --fondo:            #f2f5f8;
  --fondo-elevado:    #ffffff;
  --fondo-hundido:    #e8edf2;
  --borde:            #dde3ea;
  --borde-vivo:       #c3ccd6;

  --texto:            #14161b;
  --texto-medio:      #565d6a;
  --texto-tenue:      #6b7280;

  /* Ocre: mismo tono que el ámbar del modo oscuro, con luminosidad bajada
     hasta pasar el contraste sobre blanco. */
  --senal:            #9a640a;
  --senal-suave:      rgba(154, 100, 10, 0.10);

  --estado-idle:      #a8aeb9;
  --estado-active:    #9a640a;
  --estado-success:   #1a7a4b;
  --estado-warning:   #96650d;
  --estado-error:     #b0322a;

  --linea:              rgba(20, 22, 27, 0.09);
  --fondo-activo:       #fdf7ec;
  --malla-linea:        rgba(20, 22, 27, 0.028);
  /* Cognición del modelo en claro: el mismo cian bajado a tinta, porque el
     original es luz y sobre crema no contrasta (2,1:1). */
  --ia:               #176e84;
  --ia-suave:         rgba(23, 110, 132, 0.07);
  --ia-borde:         rgba(23, 110, 132, 0.30);
  --ia-brillo:        rgba(23, 110, 132, 0.12);
  /* En claro la chapa va al revés: el nodo es más claro arriba y se apoya en
     un gris apenas más oscuro abajo. Copiar los valores del tema oscuro daría
     cajas negras sobre fondo crema. */
  --malla-linea-viva:   rgba(154, 100, 10, 0.075);
  --chapa-alto:         #ffffff;
  --chapa-bajo:         #eef2f6;
  --brillo-cabecera:    rgba(154, 100, 10, 0.045);
  --brillo-diagrama:    rgba(154, 100, 10, 0.05);
  --borde-exito:        rgba(26, 122, 75, 0.45);
  --borde-error:        rgba(176, 50, 42, 0.45);
  --borde-aviso:        rgba(150, 101, 13, 0.45);
  --tinte-exito:        rgba(26, 122, 75, 0.10);
  --tinte-error:        rgba(176, 50, 42, 0.12);
  --tinte-error-suave:  rgba(176, 50, 42, 0.05);
  --tinte-aviso:        rgba(150, 101, 13, 0.12);
  --tinte-aviso-suave:  rgba(150, 101, 13, 0.06);
  --tinte-neutro:       rgba(20, 22, 27, 0.05);
  --sombra:             0 1px 2px rgba(20, 22, 27, 0.05), 0 1px 8px rgba(20, 22, 27, 0.03);
  --sombra-pieza:       0 1px 2px rgba(20, 22, 27, 0.06), 0 6px 16px -10px rgba(20, 22, 27, 0.18);
}

/* Las superficies elevadas necesitan sombra en claro y ninguna en oscuro; la
   variable resuelve las dos sin duplicar reglas. */
.cifra, .panel { box-shadow: var(--sombra); }

/* --- Interruptor de tema ---------------------------------------------------
 *
 * Segmentado en vez de botón con etiqueta: se ven las dos opciones y cuál
 * está activa, sin una palabra compitiendo con el estado en vivo que tiene al
 * lado. El contorno es del mismo peso que el resto de bordes del panel para
 * que no reclame atención: es un ajuste, no una acción.
 */

.tema {
  display: inline-flex;
  align-items: center;
  gap: 1px;
  padding: 2px;
  border: 1px solid var(--borde);
  border-radius: 999px;
  background: var(--fondo-hundido);
}

.tema button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 22px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--texto-tenue);
  cursor: pointer;
  transition: color .18s ease, background-color .18s ease;
}

.tema button:hover { color: var(--texto-medio); }
.tema button:focus-visible { outline: 2px solid var(--senal); outline-offset: 2px; }

/* La opción activa se eleva sobre la pista, como una tecla pulsada. */
.tema button[aria-pressed="true"] {
  background: var(--fondo-elevado);
  color: var(--senal);
  box-shadow: var(--sombra);
}

.tema svg {
  width: 13px;
  height: 13px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
}


/* --- Formularios ---------------------------------------------------------- */

.formulario { padding: 18px; border-top: 1px solid var(--borde); display: grid; gap: 16px; }
.form-titulo { margin: 0; font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase; color: var(--texto-tenue); }

.campos {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 16px;
  /* Cada campo se ancla arriba de su fila. Sin esto, dos campos vecinos de
     alturas distintas —un textarea largo junto a uno corto— estiraban su
     etiqueta al alto de la fila y el campo bajo quedaba centrado: los
     rótulos alineados y los recuadros a distinta altura. */
  align-items: start;
}
.campo-ancho { grid-column: 1 / -1; }

.formulario label {
  display: grid;
  grid-template-rows: auto auto;
  align-content: start;
  gap: 6px;
  font-size: 11.5px;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--texto-tenue);
}

.formulario input,
.formulario select,
.formulario textarea {
  width: 100%;
  padding: 9px 11px;
  background: var(--fondo-hundido);
  color: var(--texto);
  border: 1px solid var(--borde-vivo);
  border-radius: var(--radio-chico);
  font-family: var(--sans);
  font-size: 13.5px;
  letter-spacing: 0;
  text-transform: none;
  resize: vertical;
}
.formulario input:focus-visible,
.formulario select:focus-visible,
.formulario textarea:focus-visible { outline: 2px solid var(--senal); outline-offset: 1px; }

/* La ayuda va bajo el campo y en minúscula: es contexto, no una etiqueta más. */
.ayuda { font-size: 11.5px; letter-spacing: 0; text-transform: none; color: var(--texto-tenue); line-height: 1.45; }
.ayuda b { color: var(--texto-medio); font-weight: 500; }

.boton {
  justify-self: start;
  padding: 9px 18px;
  border: 1px solid var(--senal);
  border-radius: var(--radio-chico);
  background: var(--senal-suave);
  color: var(--senal);
  font-family: var(--sans);
  font-size: 13px;
  cursor: pointer;
  transition: background-color .18s ease;
}
.boton:hover { background: transparent; }
.boton:focus-visible { outline: 2px solid var(--senal); outline-offset: 2px; }
.boton[disabled] { opacity: .5; cursor: progress; }

.boton--fantasma {
  border-color: var(--borde-vivo);
  background: transparent;
  color: var(--texto-medio);
  padding: 5px 10px;
  font-size: 12px;
}
.boton--fantasma:hover { border-color: var(--estado-error); color: var(--estado-error); }

/* La respuesta del guardado: los avisos importan tanto como el «guardado». */
.respuesta { padding: 14px 18px; border-top: 1px solid var(--borde); font-size: 13px; }
.respuesta[data-estado="ok"] { background: var(--tinte-exito); }
.respuesta[data-estado="error"] { background: var(--tinte-error-suave); }
.respuesta ul { margin: 8px 0 0; padding-left: 18px; color: var(--texto-medio); line-height: 1.55; }
.respuesta li + li { margin-top: 6px; }

/* --- Reglas de alerta ------------------------------------------------------ */

.regla { padding: 14px 18px; display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.regla + .regla { border-top: 1px solid var(--linea); }
.regla-nombre { font-weight: 500; }
.regla-detalle { flex: 1; font-size: 12px; color: var(--texto-tenue); }

/* --- Desglose de indicadores ---------------------------------------------
   Las bandas de clic son invisibles pero el cursor tiene que decir que se
   puede pulsar: un desglose que nadie descubre es un desglose que no existe. */
.serie-zona {
  fill: transparent;
  cursor: pointer;
}
.serie-zona:hover { fill: var(--senal-suave); }

.desglose {
  margin-top: 0.85rem;
  padding-top: 0.85rem;
  border-top: 1px solid var(--borde);
}
.desglose-cabecera {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  flex-wrap: wrap;
  margin-bottom: 0.6rem;
  font-size: 0.86rem;
}
.desglose-cabecera > span { color: var(--texto-tenue); }
.desglose-cabecera > .boton { margin-left: auto; }
.desglose-cargando {
  color: var(--texto-tenue);
  font-size: 0.82rem;
  margin: 0.35rem 0;
}
/* El desglose puede traer 60 menciones: se limita el alto para que no empuje
   el resto de las series fuera de la pantalla. */
.desglose .menciones {
  max-height: 26rem;
  overflow-y: auto;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* --- Evaluación de situación ---------------------------------------------
   Es lo primero que se lee. El tono cambia el color del borde izquierdo y no
   el fondo entero: un panel rojo grita antes de que nadie haya leído por qué,
   y este sistema tiene que informar, no alarmar. */
.situacion {
  position: relative;
  background:
    linear-gradient(115deg, var(--ia-suave), transparent 34%),
    var(--fondo-elevado);
  border: 1px solid var(--ia-borde);
  border-radius: 10px;
  padding: 18px 22px;
  margin-bottom: 18px;
  box-shadow: 0 0 34px -18px var(--ia-brillo), inset 0 1px 0 rgba(255, 255, 255, 0.03);
}

/* Firma del motor en la esquina: dice QUIÉN escribió este texto. Es la pieza
   central del lenguaje visual: dato medido no la lleva, juicio del modelo sí. */
.situacion::after {
  content: 'ANÁLISIS · ' attr(data-motor);
  position: absolute;
  top: 10px;
  right: 14px;
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: .16em;
  color: var(--ia);
  opacity: .75;
}

/* Mientras el modelo recalcula, el marco respira en cian. */
.situacion[data-pensando="true"] {
  border-color: var(--ia);
  animation: pensando 1.6s ease-in-out infinite;
}
@keyframes pensando {
  0%, 100% { box-shadow: 0 0 24px -16px var(--ia-brillo); }
  50%      { box-shadow: 0 0 44px -12px var(--ia-brillo); }
}
@media (prefers-reduced-motion: reduce) { .situacion[data-pensando="true"] { animation: none; } }
/* El tono no se marca con una barra de color al costado —el recurso que más
   delata una interfaz generada— sino donde ya se está mirando: el punto que
   precede al titular. */
.situacion-titular::before {
  content: '';
  display: inline-block;
  width: 7px;
  height: 7px;
  margin-right: 9px;
  border-radius: 50%;
  vertical-align: middle;
  background: var(--estado-idle);
}
.situacion[data-tono="tranquilo"] .situacion-titular::before { background: var(--estado-success); }
.situacion[data-tono="atencion"]  .situacion-titular::before { background: var(--estado-warning); }
.situacion[data-tono="riesgo"]    .situacion-titular::before { background: var(--estado-error); }

.situacion-cabecera {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  margin: 14px 0 8px;
}
.situacion-titular {
  font-family: var(--display);
  font-size: 18px;
  font-weight: 700;
  line-height: 1.4;
  margin: 0;
  flex: 1;
}
.situacion-texto {
  margin: 0;
  font-size: 13.5px;
  line-height: 1.65;
  color: var(--texto-medio);
}

.chips { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 12px; }
.chip {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  padding: 4px 10px;
  border: 1px solid var(--borde-vivo);
  border-radius: 999px;
  font-size: 11.5px;
  background: var(--fondo-hundido);
}
.chip-etiqueta { color: var(--texto-medio); }
.chip-valor { color: var(--texto); font-weight: 600; font-variant-numeric: tabular-nums; }
.chip--riesgo { border-color: var(--borde-error); }
.chip--termino { border-style: dashed; }
.chip--nota { color: var(--texto-tenue); font-style: italic; }

/* --- Informes ------------------------------------------------------------- */
.informe {
  padding: 16px 20px;
  border-bottom: 1px solid var(--borde);
}
.informe-cabecera { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.informe-resumen {
  margin: 8px 0;
  font-size: 13px;
  line-height: 1.6;
  color: var(--texto-medio);
}
.informe-cuerpo { margin-top: 12px; font-size: 13.5px; line-height: 1.7; }
.informe-h {
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--senal);
  margin: 18px 0 8px;
}
.informe-p { margin: 0 0 10px; color: var(--texto-medio); }
.informe-lista { margin: 0 0 12px; padding-left: 20px; color: var(--texto-medio); }
.informe-lista li { margin-bottom: 7px; }
.informe-cuerpo strong { color: var(--texto); }

/* --- Voz ------------------------------------------------------------------ */
.audio-item {
  padding: 14px 20px;
  border-bottom: 1px solid var(--borde);
}
.audio-item audio {
  width: 100%;
  max-width: 420px;
  margin-top: 10px;
  height: 34px;
}
.formulario textarea {
  width: 100%;
  font: inherit;
  resize: vertical;
}

/* --- Propuestas de prensa ------------------------------------------------- */
.botonera { display: flex; gap: 10px; flex-wrap: wrap; }
.propuesta {
  padding: 14px 0;
  border-top: 1px solid var(--borde);
}
.propuesta .tabla-consulta { margin: 6px 0 10px; }
#propuestas-prensa { padding: 0 20px 16px; }
#propuestas-prensa details { margin-top: 14px; }
#propuestas-prensa summary { cursor: pointer; }

/* --- Transcripción -------------------------------------------------------
   Cerrada por defecto: lo que se decide mirando la ficha de un vídeo son el
   origen y el análisis. La transcripción es el respaldo para comprobarlos. */
.transcripcion {
  margin-top: 14px;
  border-top: 1px solid var(--borde);
  padding-top: 12px;
}
.transcripcion > summary {
  cursor: pointer;
  display: flex;
  gap: 10px;
  align-items: baseline;
  font-size: 13px;
  color: var(--texto-medio);
  list-style: none;
}
.transcripcion > summary::before {
  content: '▸';
  color: var(--senal);
  transition: transform 0.15s;
}
.transcripcion[open] > summary::before { content: '▾'; }
.transcripcion > summary::-webkit-details-marker { display: none; }
.transcripcion-cifra { color: var(--texto-tenue); font-size: 11.5px; }

.transcripcion-cuerpo {
  margin-top: 12px;
  max-height: 30rem;
  overflow-y: auto;
  padding-right: 10px;
}
.transcripcion-parrafo {
  margin: 0 0 12px;
  font-size: 13px;
  line-height: 1.75;
  color: var(--texto-medio);
}
/* El resaltado señala «aquí pasa algo»; el porqué está en el tooltip. */
.transcripcion-parrafo--clave {
  background: var(--ia-suave);
  padding: 8px 12px;
  border-radius: 6px;
  color: var(--texto);
  cursor: help;
}
.transcripcion-parrafo--clave:focus-visible {
  outline: 2px solid var(--ia);
  outline-offset: 2px;
}
a.transcripcion-marca,
.transcripcion-marca {
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  color: var(--ia);
  white-space: nowrap;
  text-decoration: none;
}
a.transcripcion-marca:hover { text-decoration: underline; }

/* El cuerpo desplegado de una mención larga —una transcripción— se limita en
   alto y hace scroll dentro: si no, un item de 20.000 caracteres empuja al
   resto del listado fuera de la pantalla. */
.mencion-texto--resto {
  max-height: 22rem;
  overflow-y: auto;
  white-space: pre-wrap;
  padding: 10px 12px;
  background: var(--fondo-hundido);
  border-radius: 6px;
}
.mencion-enlace { font-size: 12px; }

/* --- Bot: lista de chats -------------------------------------------------
   Tres zonas por fila: quién escribe, en qué estado está, y qué se puede
   hacer. Antes era todo un bloque pegado a la izquierda con los botones
   colgando debajo, que dejaba media pantalla vacía y no dejaba comparar dos
   chats de un vistazo. */
.chat {
  display: grid;
  grid-template-columns: minmax(180px, 1.4fr) minmax(160px, 1fr) auto;
  align-items: center;
  gap: 18px;
  padding: 16px 20px;
  border-bottom: 1px solid var(--linea);
}
.chat:last-child { border-bottom: none; }

.chat-quien { display: flex; align-items: center; gap: 11px; min-width: 0; }
/* El punto lleva el estado al borde izquierdo: se escanea la columna sin
   tener que leer la etiqueta de cada fila. */
.chat-punto {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: none;
  background: var(--estado-idle);
  box-shadow: 0 0 0 3px var(--fondo-hundido);
}
.chat[data-autorizado="true"] .chat-punto { background: var(--estado-success); }

.chat-nombre {
  margin: 0;
  font-size: 13.5px;
  color: var(--texto);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.chat-id {
  margin: 2px 0 0;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--texto-tenue);
}

.chat-datos { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.chat-meta { font-size: 11.5px; color: var(--texto-tenue); }

.chat-acciones { display: flex; gap: 8px; justify-content: flex-end; }
.chat-acciones .boton { padding: 7px 14px; font-size: 12px; justify-self: auto; }

/* El hilo ocupa la fila completa debajo, no una columna. */
.chat-hilo {
  grid-column: 1 / -1;
  margin-top: 6px;
  max-height: 24rem;
  overflow-y: auto;
  border-top: 1px solid var(--linea);
  padding-top: 12px;
}
.turno {
  padding: 9px 13px;
  border-radius: 8px;
  margin-bottom: 8px;
  font-size: 12.5px;
  line-height: 1.6;
}
.turno--user {
  background: var(--fondo-hundido);
  color: var(--texto-medio);
}
.turno--assistant {
  background: var(--ia-suave);
  color: var(--texto);
}
.turno-texto { margin: 0; white-space: pre-wrap; }
.turno-citas {
  margin: 6px 0 0;
  font-size: 10.5px;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--texto-tenue);
}

@media (max-width: 720px) {
  .chat { grid-template-columns: 1fr; gap: 10px; }
  .chat-acciones { justify-content: flex-start; }
}

/* El hilo cuelga de su fila: la sangría dice a quién pertenece cuando hay
   varios chats abiertos a la vez. */
.chat-hilo { padding-left: 19px; }
.turno .informe-p,
.turno .informe-lista { color: inherit; margin-bottom: 8px; }
.turno .informe-lista { padding-left: 18px; }
.turno .informe-h { margin: 10px 0 6px; font-size: 11px; }
.turno > :last-child { margin-bottom: 0; }

/* Término con definición disponible: subrayado punteado discreto. Señala que
   hay algo que preguntar sin gritar; el detalle llega al pasar el ratón. */
.glosado {
  text-decoration: underline dotted var(--borde-vivo);
  text-underline-offset: 3px;
  cursor: help;
}
.glosado:hover,
.glosado:focus-visible {
  text-decoration-color: var(--ia);
  outline: none;
}

/* --- Marco de cognición ---------------------------------------------------
   Todo texto ESCRITO por el modelo lo lleva; lo medido, no. Es la regla
   central del lenguaje: distinguir dato de juicio sin leer nada. */
.ia-marco {
  position: relative;
  padding: 12px 16px;
  border-radius: 8px;
  background: linear-gradient(105deg, var(--ia-suave), transparent 55%);
}
.ia-marco::after {
  content: '◈ ' attr(data-motor);
  position: absolute;
  top: 8px;
  right: 10px;
  font-family: var(--mono);
  font-size: 8.5px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ia);
  opacity: .65;
  pointer-events: none;
}
/* Dentro del marco, los encabezados del informe hablan la voz del modelo. */
.ia-marco .informe-h { color: var(--ia); }
.ia-marco > :first-child { margin-top: 0; }
.ia-marco > :last-child { margin-bottom: 0; }

/* --- Nota de panel -------------------------------------------------------
   Texto explicativo dentro de un panel. Comparte el sangrado de las filas que
   acompaña, para que no quede flotando contra el borde de la tarjeta. */
.nota-panel {
  margin: 0;
  padding: 14px 20px;
  font-size: 12px;
  line-height: 1.65;
  color: var(--texto-medio);
  background: var(--fondo-hundido);
  border-bottom: 1px solid var(--borde);
}
.nota-panel--pie {
  background: transparent;
  border-bottom: none;
  border-top: 1px solid var(--linea);
  color: var(--texto-tenue);
}
.nota-panel code {
  font-family: var(--mono);
  font-size: 11.5px;
  padding: 1px 5px;
  border-radius: 4px;
  background: var(--senal-suave);
  color: var(--senal);
}

/* --- Candidatos de encuesta ----------------------------------------------- */
.candidato {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 8px 0;
  border-top: 1px solid var(--linea);
}
.candidato .tabla-consulta { overflow-wrap: anywhere; }
.candidato .boton { flex: none; padding: 5px 12px; font-size: 11.5px; }

/* --- Intervenciones de una pauta ------------------------------------------ */
.intervencion { margin-bottom: 14px; }
.intervencion-meta {
  margin: 0 0 4px;
  font-size: 10.5px;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--texto-tenue);
}
.intervencion-meta strong { color: var(--senal); }
.intervencion-texto {
  margin: 0;
  font-size: 13px;
  line-height: 1.7;
  color: var(--texto-medio);
}
#lista-reuniones audio { width: 100%; max-width: 420px; margin-top: 10px; height: 34px; }
.formulario input[type="file"] { padding: 8px; font-size: 12px; }

/* --- Miniatura de vídeo ---------------------------------------------------
   Relación 16:9 fija para que la fila no dé un salto cuando carga la imagen,
   y ancho acotado: en el listado de menciones la portada acompaña al texto,
   no lo sustituye. */
/* La portada va al lado del texto, no encima: es la referencia visual del
   origen, y lo que se lee es el análisis.

   No se usa `float` porque tanto `.video` como `.mencion` son contenedores
   grid, y dentro de un grid el float es inerte —se aplicó y no pasó nada, la
   portada seguía apilada arriba—. Se resuelve con el propio grid: una columna
   extra a la derecha que la portada ocupa durante las primeras filas. */
.video:has(> .miniatura),
.mencion:has(> .miniatura) {
  grid-template-columns: minmax(0, 1fr) minmax(200px, 268px);
}
.video:has(> .miniatura) > *,
.mencion:has(> .miniatura) > * {
  grid-column: 1;
}
.video:has(> .miniatura) > .miniatura,
.mencion:has(> .miniatura) > .miniatura {
  grid-column: 2;
  grid-row: 1 / span 3;
}

/* La portada solo acompaña a las tres primeras filas —título, origen y
   resumen—. Todo lo que viene después es el análisis, y dejarlo encerrado en
   la columna estrecha vaciaba media tarjeta: barras de desempeño en dos
   columnas con hueco a la derecha, listas comprimidas y un tercio del ancho
   en blanco desde la mitad de la ficha hacia abajo. A partir de la portada,
   el contenido usa el ancho completo. */
.video:has(> .miniatura) > :nth-child(n + 5),
.mencion:has(> .miniatura) > :nth-child(n + 5) {
  grid-column: 1 / -1;
}

@media (max-width: 720px) {
  /* En vertical no hay dos columnas que repartir: la portada va debajo del
     título, a todo el ancho, y el resto en una sola columna. */
  .video:has(> .miniatura),
  .mencion:has(> .miniatura) { grid-template-columns: minmax(0, 1fr); }
  .video:has(> .miniatura) > .miniatura,
  .mencion:has(> .miniatura) > .miniatura { grid-column: 1; grid-row: auto; }
}

.miniatura {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  align-self: start;
  border-radius: 8px;
  overflow: hidden;
  border: 1px solid var(--borde);
  background: var(--fondo-hundido);
  cursor: pointer;
}
.miniatura img,
.miniatura iframe {
  width: 100%;
  height: 100%;
  display: block;
  border: 0;
  object-fit: cover;
}
.miniatura-play {
  position: absolute;
  inset: 0;
  margin: auto;
  border-radius: 50%;
  border: 1px solid var(--senal);
  background: rgba(0, 0, 0, 0.55);
  color: var(--senal);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  transition: transform .15s ease, background-color .15s ease;
}
.miniatura:hover .miniatura-play {
  transform: scale(1.08);
  background: rgba(0, 0, 0, 0.75);
}
/* Al reproducir crece: ahí sí es el contenido principal. */
.miniatura[data-abierto] { cursor: default; }

.miniatura-play { width: 40px; height: 40px; font-size: 13px; }

@media (max-width: 720px) {
  /* En pantalla estrecha no hay sitio para dos columnas: vuelve a apilarse. */
  .video:has(> .miniatura),
  .mencion:has(> .miniatura) { grid-template-columns: 1fr; }
  .video:has(> .miniatura) > .miniatura,
  .mencion:has(> .miniatura) > .miniatura { grid-column: 1; grid-row: auto; }
  .miniatura,
  .miniatura[data-abierto] { width: 100%; max-width: 320px; }
}

/* --- Mediciones de encuesta ------------------------------------------------ */
.medicion-sujeto { color: var(--texto-tenue); }
.medicion-pregunta { color: var(--ia); cursor: help; font-size: 11px; }

/* --- Traza de razonamiento -------------------------------------------------
   Cada nodo escupe una línea de terminal mientras trabaja. Un pipeline que
   solo enciende una luz verde no comunica que hay un motor razonando; esto
   sí. Solo se ve en los nodos activos: inventar actividad en una etapa
   parada sería mentir sobre el estado del sistema. */
/* La burbuja está SIEMPRE visible: el diagrama tiene que leerse como un
   sistema despierto. Lo que cambia con el estado es la intensidad, no la
   presencia. */
.nodo-burbuja {
  opacity: 1;
  transition: opacity .35s ease;
  pointer-events: none;
  overflow: visible;
}

.traza {
  font-family: var(--mono);
  font-size: 7.5px;
  line-height: 1.45;
  letter-spacing: .01em;
  color: var(--ia);
  text-align: center;
  padding: 5px 7px;
  border: 1px solid var(--ia-borde);
  border-radius: 5px;
  background: color-mix(in srgb, var(--fondo-hundido) 88%, transparent);
  backdrop-filter: blur(3px);
  overflow-wrap: anywhere;
  box-shadow: 0 0 18px -8px var(--ia-brillo);
}
/* En reposo la traza baja de intensidad: dice lo que la etapa vigila, no que
   esté procesando. El núcleo del nodo sigue diciendo la verdad del estado. */
.traza[data-reposo="true"] {
  opacity: .5;
  border-color: var(--borde);
  box-shadow: none;
}
.traza[data-reposo="true"]::after { opacity: .4; }

/* Traza real del backend: ámbar, porque es hecho, no ilustración. */
.traza[data-real="true"] {
  color: var(--senal);
  border-color: var(--senal-suave);
}
.nodo[data-estado="error"] .traza {
  color: var(--estado-error);
  border-color: var(--borde-error);
}

/* Entrada de cada línea: aparece desde abajo, como en un log que avanza. */
.traza--entra { animation: traza-entra .32s ease-out; }
@keyframes traza-entra {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Cursor de terminal al final de la línea. */
.traza::after {
  content: '▌';
  margin-left: 2px;
  animation: cursor 1.05s steps(2) infinite;
}
@keyframes cursor { 50% { opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
  .traza--entra { animation: none; }
  .traza::after { animation: none; }
}

/* El resumen de facturación y las listas que cuelgan de un formulario iban
   pegadas al borde del panel: el resto del contenido tiene sangrado y estas
   no, así que se leían como texto suelto. */
#lista-audios > .serie-pie,
#lista-reuniones > .serie-pie,
#lista-informes > .serie-pie {
  padding: 12px 20px 0;
  margin: 0;
}
#lista-audios,
#lista-informes,
#lista-reuniones,
#lista-encuestas,
#bot-chats,
#reglas { padding-bottom: 4px; }

/* --- Cierre de alertas ----------------------------------------------------- */
/* El formulario vive en un modal: ya no necesita marco propio ni fondo
   distinto, que ahí dentro solo añadirían una caja sobre otra.

   `.modal-cuerpo` no trae sangrado horizontal —cada contenido pone el suyo,
   porque algunos son bandas a sangre—, así que aquí hay que ponerlo: sin él
   el título citado y el área de texto llegaban pegados a los dos bordes
   mientras la cabecera del modal sí respiraba, y la caja se veía descuadrada. */
#cierre-cuerpo { padding: 18px 20px 20px; }

.hallazgo-form { display: grid; gap: 14px; }

/* Titular de la alerta que se está cerrando. Abierto sobre un listado de
   varias, sin esto no se sabe cuál es; y va con acento a la izquierda porque
   es una cita de lo que hay detrás del modal, no un texto del formulario. */
.cierre-titular {
  margin: 0 0 16px;
  padding: 10px 14px;
  border-left: 2px solid var(--senal);
  border-radius: 0 var(--radio-chico) var(--radio-chico) 0;
  background: var(--senal-suave);
  font-size: 14px;
  line-height: 1.5;
  color: var(--texto);
}

.hallazgo-form textarea,
.hallazgo-form select {
  width: 100%;
  font: inherit;
  font-size: 13px;
  padding: 9px 11px;
  color: var(--texto);
  background: var(--fondo-hundido);
  border: 1px solid var(--borde-vivo);
  border-radius: var(--radio-chico);
  resize: vertical;
}
.hallazgo-form textarea { min-height: 96px; line-height: 1.6; }
.hallazgo-form textarea::placeholder { color: var(--texto-tenue); }
.hallazgo-form textarea:focus-visible,
.hallazgo-form select:focus-visible {
  outline: 2px solid var(--senal);
  outline-offset: 1px;
}

/* El estado a la izquierda y el botón que confirma a la derecha: son dos
   cosas distintas —qué se decide y ejecutarlo— y pegadas se leían como un
   grupo de dos botones. */
.hallazgo-form .botonera { align-items: center; justify-content: space-between; }
.hallazgo-form select { width: auto; min-width: 150px; }

/* El texto de error del envío, bajo el formulario y sin desplazar nada. */
.hallazgo-form .tabla-error { margin: 0; }

@media (max-width: 620px) {
  .hallazgo-form .botonera { flex-direction: column; align-items: stretch; }
  .hallazgo-form select { width: 100%; }
  .hallazgo-form .botonera .boton { justify-content: center; }
}
.hallazgo-cierre {
  margin: 10px 0 0;
  padding: 9px 12px;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--texto-medio);
  background: var(--tinte-neutro);
  border-radius: 6px;
}
.hallazgo-cierre strong { color: var(--texto); }

/* --- Origen de una mención -------------------------------------------------
   Icono de la red + enlace al posteo. Antes era el nombre en texto y el enlace
   vivía suelto al final del párrafo: poder saltar a la fuente en un clic es lo
   que separa «leí un resumen» de «verifiqué», y cualquier mención que se cite
   en una reunión tiene que ser comprobable en dos segundos. */
.origen {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 9px;
  border: 1px solid var(--borde-vivo);
  border-radius: 20px;
  font-size: 10.5px;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--texto-medio);
  text-decoration: none;
  transition: border-color .18s ease, color .18s ease;
}
a.origen:hover,
a.origen:focus-visible {
  border-color: var(--senal);
  color: var(--senal);
  outline: none;
}
.icono-red { flex: none; display: block; }
.origen-flecha { font-size: 9px; opacity: .6; }
a.origen:hover .origen-flecha { opacity: 1; }

/* Tono «aviso»: alto es señal de vigilar, no de daño. La concentración de la
   conversación es el caso: mucha actividad de pocas cuentas hay que mirarla,
   pero no es en sí una mala noticia. */
.serie-linea--aviso { stroke: var(--estado-warning); }
.serie-area--aviso  { fill: var(--tinte-aviso-suave); }
.serie-punto--aviso { fill: var(--estado-warning); }

.serie-detalle {
  margin: 10px 0 0;
  font-size: 11.5px;
  color: var(--texto-medio);
}
.serie-detalle strong { color: var(--texto); font-family: var(--mono); font-size: 11px; }

/* Corte entre los indicadores de conversación y los de canales propios. Ocupa
   la fila completa para que la cuadrícula de percepción quede limpia. */
.series-corte {
  grid-column: 1 / -1;
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
  padding: 14px 18px 10px;
  border-top: 1px solid var(--borde);
  background: var(--fondo-hundido);
}
.series-corte-titulo {
  font-family: var(--display);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--texto-medio);
}
.series-corte-nota { font-size: 11.5px; color: var(--texto-tenue); }

/* Autenticidad de quien responde, dentro de la tarjeta de cada canal. El
   color sube con la proporción: un signo negativo se lee distinto si un
   tercio de quien lo produce son cuentas con señales. */
.canal-autenticidad {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin: 8px 0 0;
  padding-top: 8px;
  border-top: 1px solid var(--linea);
  font-size: 11.5px;
  color: var(--texto-tenue);
  cursor: help;
}
.canal-aut-valor {
  font-family: var(--mono);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  color: var(--texto-medio);
}
.canal-autenticidad[data-nivel="medio"] .canal-aut-valor { color: var(--estado-warning); }
.canal-autenticidad[data-nivel="alto"]  .canal-aut-valor { color: var(--estado-error); }

/* Nombre de canal con su logo. El icono hereda el color del texto para que
   funcione igual en claro y en oscuro sin duplicar reglas. */
.canal-nombre {
  display: flex;
  align-items: center;
  gap: 7px;
}
.canal-nombre .icono-red { flex: none; opacity: .85; }

/* --- Menú móvil -----------------------------------------------------------
   Por debajo de 860 px catorce secciones en una tira horizontal obligaban a
   arrastrar a ciegas: no se veía cuántas había ni dónde estaba uno. Se
   convierte en un panel lateral que entra desde la derecha, que es el lado
   donde está el pulgar. */

.hamburguesa {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  width: 38px;
  height: 34px;
  padding: 0 8px;
  border: 1px solid var(--borde-vivo);
  border-radius: var(--radio-chico);
  background: transparent;
  cursor: pointer;
}
.hamburguesa span {
  display: block;
  height: 1.5px;
  background: var(--texto-medio);
  border-radius: 2px;
  transition: transform .22s ease, opacity .18s ease;
}
/* Las tres barras se convierten en aspa al abrir: el mismo botón cierra. */
.hamburguesa[aria-expanded="true"] span:nth-child(1) { transform: translateY(5.5px) rotate(45deg); }
.hamburguesa[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.hamburguesa[aria-expanded="true"] span:nth-child(3) { transform: translateY(-5.5px) rotate(-45deg); }

/* El velo solo existe en móvil y solo cuando el panel está abierto.
   `display: block` a secas pisaba el atributo `hidden` —que es una regla de la
   hoja del navegador y pierde contra cualquier regla de autor—, así que en
   móvil la aplicación quedaba permanentemente tapada por una capa oscura sin
   forma de quitarla. El `:not([hidden])` es lo que devuelve el control al
   atributo. */
.menu-velo { display: none; }

@media (max-width: 860px) {
  .menu-velo:not([hidden]) { display: block; }
}

.menu-velo {
  position: fixed;
  inset: 0;
  z-index: 8;
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(2px);
  animation: velo .2s ease;
}
@keyframes velo { from { opacity: 0; } to { opacity: 1; } }

@media (max-width: 860px) {
  .hamburguesa {
    display: flex;
    /* Último elemento de la cabecera. Sin esto quedaba antes del control de
       tema y el botón que abre el menú no era el de la esquina, que es donde
       la mano lo busca. */
    order: 9;
    margin-left: auto;
  }
  /* En móvil la cabecera se reordena: identidad a la izquierda, estado en
     medio, controles a la derecha. */
  .pulso { width: 100%; justify-content: flex-end; gap: 10px; }
  .pulso-ciclo, .motor-detalle { display: none; }

  /* Cabecera fija al desplazar.
     En escritorio el menú se queda pegado arriba y hace de ancla, pero en
     móvil el menú es un cajón lateral, así que al bajar por una lista larga
     —menciones, vídeos— no quedaba nada en pantalla: ni dónde estás, ni el
     botón para cambiar de sección, ni el estado del sistema. Había que subir
     hasta arriba del todo para cualquier cosa.

     `z-index` por debajo del cajón (9) y de su velo (8): la cabecera se queda
     fija, pero cuando se abre el menú el cajón pasa por encima. Al revés, el
     menú aparecería recortado por la cabecera. */
  .cabecera {
    position: sticky;
    top: 0;
    z-index: 6;
    /* Menos aire que en escritorio: fija, cada píxel de alto es contenido que
       ya no se ve. */
    padding-top: 13px;
    padding-bottom: 11px;
    gap: 10px;
    /* Opaca y con desenfoque. El degradado a transparente de escritorio deja
       ver el contenido pasando por debajo, y sobre texto se vuelve ilegible. */
    background: color-mix(in srgb, var(--fondo) 90%, transparent);
    backdrop-filter: blur(16px) saturate(1.3);
    -webkit-backdrop-filter: blur(16px) saturate(1.3);
  }

  /* El isotipo y el rótulo bajan un punto: fijos y a todo lo ancho pesan más
     que en una cabecera que se va al desplazar. */
  .cabecera .sello { width: 34px; height: 34px; margin-top: -5px; }
  .marca { gap: 10px; min-width: 0; }

  /* El nombre del cargo se corta en vez de envolver. Fija, cada línea que se
     parte es contenido que deja de verse; y «PRESIDENTA DEL SENADO» partido
     en dos no aporta nada sobre el mismo texto cortado. */
  .marca-sub {
    font-size: 9.5px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .marca > div { min-width: 0; }

  /* La fila de estado en una sola línea. «En vivo» partido en dos y el reloj
     desalineado hacían que la cabecera midiera casi un tercio de la pantalla. */
  .pulso { gap: 8px; flex-wrap: nowrap; }
  .pulso-texto { white-space: nowrap; }
  .motor { padding: 5px 10px; }
  .pulso-sep { display: none; }

  .menu {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: auto;
    z-index: 9;
    width: min(78vw, 300px);
    flex-direction: column;
    justify-content: flex-start;
    gap: 0;
    padding: 74px 0 24px;
    overflow-y: auto;
    overflow-x: hidden;
    border-bottom: none;
    border-left: 1px solid var(--borde);
    background: var(--fondo-elevado);
    box-shadow: -24px 0 60px -30px #000;
    right: -320px;
    transition: right .26s cubic-bezier(.32, .72, 0, 1), visibility .26s;
    visibility: hidden;
  }
  .menu[data-abierto="true"] { right: 0; visibility: visible; }

  /* Bloqueo del scroll de fondo mientras el panel está abierto. Vive aquí y no
     en JS: al pasar a escritorio la regla deja de aplicar sola. */
  body.con-menu { overflow: hidden; }

  .menu a {
    padding: 15px 22px;
    font-size: 15px;
    border-bottom: none;
    /* En vertical el subrayado de pestaña no significa nada: la sección
       activa se marca con una barra a la izquierda. */
    border-left: 2px solid transparent;
  }
  .menu a[aria-current="page"] {
    border-left-color: var(--senal);
    background: var(--senal-suave);
  }
  .menu a[aria-current="page"]::after { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .menu { transition: none; }
  .menu-velo { animation: none; }
}

/* --- Diagrama plegable ----------------------------------------------------
   Plegado por defecto en los dos soportes. El diagrama es lo que se enseña, no lo que
   se consulta: ocupa media pantalla y por debajo están las cifras y la
   evaluación de situación, que es a lo que se entra. Se despliega a un clic y
   la tira de puntos conserva lo único que se mira de pasada —si alguna etapa
   corre o falló—. */
#toggle-diagrama {
  display: inline-flex;
  padding: 6px 12px;
  font-size: 12px;
}
.diagrama-resumen { display: none; }

.panel--diagrama[data-colapsado="true"] .diagrama-caja,
.panel--diagrama[data-colapsado="true"] .leyenda { display: none; }

.panel--diagrama[data-colapsado="true"] .diagrama-resumen {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  padding: 14px 18px;
  border-top: 1px solid var(--linea);
}

.diagrama-resumen .punto { width: 9px; height: 9px; }
/* El punto de una etapa ejecutando late, igual que en el diagrama. */
.diagrama-resumen .punto[data-estado="active"] {
  box-shadow: 0 0 0 3px var(--senal-suave);
  animation: latido-punto 1.4s ease-in-out infinite;
}
@keyframes latido-punto {
  0%, 100% { box-shadow: 0 0 0 2px var(--senal-suave); }
  50%      { box-shadow: 0 0 0 5px var(--senal-suave); }
}
.diagrama-resumen-texto {
  margin-left: 6px;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--texto-tenue);
}

/* La cabecera del panel deja sitio a los mandos sin apretar el título. */
@media (max-width: 860px) {
  .panel--diagrama .panel-cabecera { flex-wrap: wrap; row-gap: 8px; }
}

@media (prefers-reduced-motion: reduce) {
  .diagrama-resumen .punto[data-estado="active"] { animation: none; }
}

/* --- Registro de cuentas en vigilancia ------------------------------------ */
.boton--vigilancia { display: inline-flex; align-items: center; gap: 8px; }

.etiqueta--vigilada {
  border-color: var(--estado-error);
  color: var(--estado-error);
  cursor: help;
}

.modal {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.modal[hidden] { display: none; }
.modal-velo {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(3px);
}
.modal-caja {
  position: relative;
  width: min(760px, 100%);
  max-height: 86vh;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--borde-vivo);
  border-radius: var(--radio);
  background: var(--fondo-elevado);
  box-shadow: 0 40px 90px -40px #000;
  overflow: hidden;
}
.modal-cabecera {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  padding: 18px 20px;
  border-bottom: 1px solid var(--borde);
}
.modal-cabecera h2 {
  margin: 0 0 4px;
  font-family: var(--display);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
}
.modal-cuerpo { overflow-y: auto; padding-bottom: 8px; }
body.con-modal { overflow: hidden; }

.vig-cifras {
  display: flex;
  gap: 26px;
  padding: 16px 20px;
  border-bottom: 1px solid var(--linea);
}
.vig-cifras .serie-dato-v { font-size: 22px; }

.vig-cuenta {
  padding: 16px 20px;
  border-bottom: 1px solid var(--linea);
}
/* Denunciable: acoso o ataque por género, que es lo que una plataforma
   tramita. El resto es desacuerdo y no se marca igual. */
.vig-cuenta[data-denunciable="true"] { background: var(--tinte-error-suave); }
.vig-cuenta .serie-datos { border-top: none; padding-top: 8px; margin-top: 8px; }
.vig-prueba { padding: 8px 0; border-bottom: 1px solid var(--linea); }
.vig-prueba:last-child { border-bottom: none; }

@media (max-width: 620px) {
  .modal { padding: 0; }
  .modal-caja { max-height: 100vh; height: 100%; border-radius: 0; border: none; }
  .vig-cifras { gap: 18px; }
}

/* El botón de vigilancia cierra la fila de filtros por la derecha: es una
   acción, no un filtro, y mezclado entre los selectores se leía como uno más. */
/* Ocupa la última columna de la rejilla, no una celda intermedia: así queda
   pegado al borde derecho sin que dependa de cuántos filtros haya. Mismo alto
   que los selectores para que la fila tenga una sola línea de base. */
.filtros .boton--vigilancia {
  grid-column: -2 / -1;
  justify-content: center;
  height: 36px;
}
@media (max-width: 620px) {
  /* `.boton` trae `justify-self: start`, que en una columna única deja el
     botón a media anchura mientras los selectores llegan al borde. */
  .filtros .boton--vigilancia { grid-column: 1 / -1; justify-self: stretch; }
}

/* En Voz propia el botón vive en la cabecera del panel en vez de en una fila
   de filtros: se separa del título igual que allí se separa de los selectores,
   para que el botón sea el mismo en las dos vistas. */
.panel-cabecera .boton--vigilancia { margin-left: auto; }

/* --- Iconos del menú ------------------------------------------------------ */
.icono-menu { flex: none; opacity: .7; transition: opacity .18s ease; }
.menu a:hover .icono-menu { opacity: 1; }
.menu a[aria-current="page"] .icono-menu { opacity: 1; color: var(--senal); }

/* --- Engranaje de la ficha ------------------------------------------------
   La ficha de la Persona no es una sección que se visite a diario, pero es lo
   que orienta todo el análisis: vive a un clic del nombre del cliente. */
.marca-sub { display: flex; align-items: center; gap: 8px; }
.engranaje {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--radio-chico);
  background: transparent;
  color: var(--texto-tenue);
  cursor: pointer;
  transition: color .18s ease, border-color .18s ease, transform .35s ease;
}
.engranaje:hover,
.engranaje:focus-visible {
  color: var(--senal);
  border-color: var(--borde-vivo);
  transform: rotate(45deg);
  outline: none;
}

/* --- Ficha en solo lectura -------------------------------------------------
   El estado por defecto es mirar, no escribir: un campo que se toca sin querer
   al leer cambia cómo se juzga cada texto que entre después. */
.formulario[data-editando="false"] input,
.formulario[data-editando="false"] textarea,
.formulario[data-editando="false"] select {
  border-color: transparent;
  background: var(--fondo-hundido);
  color: var(--texto-medio);
  cursor: default;
}
.formulario[data-editando="false"] input:focus,
.formulario[data-editando="false"] textarea:focus {
  outline: none;
  border-color: var(--borde);
}
/* El botón de guardar no tiene sentido si no se está editando. */
.formulario[data-editando="false"] button[type="submit"] { display: none; }

/* --- Campos de la ficha ----------------------------------------------------
   El modal tiene scroll propio: es preferible un campo alto y legible dentro
   de una caja que se desplaza, a un campo de dos líneas con scroll interno
   donde hay que arrastrar para leer una biografía. El alto exacto lo fija el
   guion según el contenido; esto es el mínimo. */
#persona-cuerpo textarea {
  min-height: 64px;
  line-height: 1.6;
  overflow-y: hidden;
  resize: vertical;
}
/* En solo lectura el campo no debe parecer un cuadro de escritura: se lee
   como el párrafo que es. */
.formulario[data-editando="false"] textarea { resize: none; }

/* La rejilla de la ficha respira: campos largos en una sola columna, cortos
   de a dos o tres. */
#persona-cuerpo .campos { gap: 18px; }

/* La ayuda cuelga del campo sin empujar al vecino: pertenece a la etiqueta,
   no a la rejilla, así que ocupa su tercera fila y no altera la altura de la
   fila de al lado. */
.formulario label > .ayuda { grid-row: 3; margin: 0; }

/* --- Selector de día -------------------------------------------------------
   Calendario propio en vez de `<input type="date">`: el nativo deja elegir
   cualquier fecha del año, y aquí solo tienen sentido las que tienen
   contenido. El punto bajo cada número marca el volumen de ese día, así se
   elige mirando dónde ocurrió algo en vez de probando fechas. */
.campo-fecha { position: relative; }

.fecha-disparador {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 9px 13px;
  border: 1px solid var(--borde-vivo);
  border-radius: var(--radio-chico);
  background: var(--fondo-hundido);
  color: var(--texto);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  transition: border-color .18s ease;
}
.fecha-disparador:hover,
.fecha-disparador[data-abierto="true"] { border-color: var(--senal); }
.fecha-disparador .rotulo-tec { color: var(--texto-tenue); }
.fecha-rotulo { font-variant-numeric: tabular-nums; }
.fecha-flecha { color: var(--texto-tenue); font-size: 10px; }

.fecha-popover {
  position: absolute;
  z-index: 12;
  top: calc(100% + 8px);
  left: 0;
  width: 268px;
  padding: 14px;
  border: 1px solid var(--borde-vivo);
  border-radius: var(--radio);
  background: var(--fondo-elevado);
  box-shadow: 0 24px 60px -30px #000;
  animation: fecha-entra .16s ease;
}
@keyframes fecha-entra {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) { .fecha-popover { animation: none; } }

.fecha-cabecera {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 10px;
}
.fecha-mes {
  font-family: var(--display);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.fecha-nav {
  width: 24px;
  height: 24px;
  border: 1px solid transparent;
  border-radius: var(--radio-chico);
  background: transparent;
  color: var(--texto-medio);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
}
.fecha-nav:hover { color: var(--senal); border-color: var(--borde-vivo); }

.fecha-rejilla {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
}
.fecha-dow {
  font-family: var(--mono);
  font-size: 9px;
  text-align: center;
  padding-bottom: 6px;
  color: var(--texto-tenue);
}
.fecha-dia {
  position: relative;
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid transparent;
  border-radius: var(--radio-chico);
  background: transparent;
  color: var(--texto);
  font-family: var(--mono);
  font-size: 11.5px;
  cursor: pointer;
  transition: background-color .14s ease, border-color .14s ease;
}
/* Un día sin datos no se puede elegir y se ve que no se puede: no hay nada
   que mirar ahí, y dejar pulsarlo produciría una pantalla vacía engañosa. */
.fecha-dia:disabled { color: var(--texto-tenue); opacity: .35; cursor: default; }
.fecha-dia:not(:disabled):hover { background: var(--senal-suave); }
.fecha-dia[data-hoy="true"] { border-color: var(--borde-vivo); }
.fecha-dia[data-elegida="true"] {
  background: var(--senal);
  color: var(--fondo);
  font-weight: 700;
}
.fecha-punto {
  position: absolute;
  bottom: 3px;
  left: 50%;
  transform: translateX(-50%);
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--senal);
}
.fecha-dia[data-elegida="true"] .fecha-punto { background: var(--fondo); opacity: 1 !important; }

.fecha-pie {
  display: flex;
  gap: 8px;
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--linea);
}
.fecha-pie .boton { flex: 1; justify-content: center; padding: 6px 10px; font-size: 11.5px; }

/* Buscador: es el único campo de la fila donde se escribe, así que ocupa dos
   columnas de la rejilla en vez de pedir un ancho mínimo propio —eso último
   rompía la uniformidad de las columnas y descuadraba toda la hilera. El
   aspecto del control ya lo fija `.filtros input`. */
.campo-buscador { grid-column: span 2; }
.campo-buscador input::placeholder { color: var(--texto-tenue); }

@media (max-width: 620px) {
  .campo-buscador { grid-column: 1 / -1; }
}

/* --- Parámetros de generación de voz --------------------------------------
   Deslizador con los dos extremos rotulados. «Estabilidad 0,5» no le dice
   nada a quien va a locutar un informe; «más expresiva ← → más monótona», sí. */
.parametros-voz {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 16px;
}
.parametro-cabecera {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 6px;
}
/* Cada parámetro es un `<label>`, igual que los demás campos: el rótulo
   hereda la tipografía del formulario en vez de imitarla, así que si mañana
   cambia el estilo de las etiquetas, cambian todas a la vez. */
.parametro { display: block; }
.parametro-nombre { font: inherit; color: inherit; }
.parametro-valor {
  font-family: var(--mono);
  font-size: 12.5px;
  color: var(--texto);
  font-variant-numeric: tabular-nums;
}

.parametro-rango {
  width: 100%;
  height: 3px;
  appearance: none;
  border-radius: 2px;
  background: var(--borde-vivo);
  cursor: pointer;
}
.parametro-rango::-webkit-slider-thumb {
  appearance: none;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: var(--senal);
  border: 2px solid var(--fondo-elevado);
  box-shadow: 0 0 0 1px var(--senal);
  cursor: grab;
}
.parametro-rango::-moz-range-thumb {
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: var(--senal);
  border: 2px solid var(--fondo-elevado);
  cursor: grab;
}
.parametro-rango:focus-visible { outline: 2px solid var(--senal); outline-offset: 4px; }

.parametro-extremos {
  display: flex;
  justify-content: space-between;
  margin-top: 5px;
  font-size: 9.5px;
  letter-spacing: .02em;
  color: var(--texto-tenue);
}

/* La especificidad importa: `.formulario input { width: 100% }` ganaba a
   `.parametro-interruptor { width: 36px }` y el interruptor se estiraba a
   440 px, con aspecto de barra ámbar. Se sube el selector al mismo nivel. */
.formulario .parametro-interruptor,
.parametro-interruptor {
  appearance: none;
  /* `flex: none` es imprescindible: dentro de la cabecera flexible el
     interruptor se estiraba a todo el ancho y se veía como una barra ámbar. */
  flex: none;
  width: 36px;
  height: 20px;
  border-radius: 20px;
  background: var(--borde-vivo);
  position: relative;
  cursor: pointer;
  transition: background-color .2s ease;
}
.parametro-interruptor::after {
  content: '';
  position: absolute;
  top: 3px;
  left: 3px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--fondo-elevado);
  transition: transform .2s ease;
}
.parametro-interruptor:checked { background: var(--senal); }
.parametro-interruptor:checked::after { transform: translateX(16px); }

/* Saldo de generación de voz. El color avisa cuando queda poco; en gris
   cuando el proveedor no deja leer el saldo, para no simular una medición
   que no tenemos. */
.cuota { font-variant-numeric: tabular-nums; cursor: help; }
.cuota strong { color: var(--texto); font-family: var(--mono); }
.cuota[data-nivel="bajo"] strong    { color: var(--estado-warning); }
.cuota[data-nivel="critico"] strong { color: var(--estado-error); }
.cuota[data-nivel="desconocido"]    { color: var(--texto-tenue); font-style: italic; }

/* --- Taller de voz ---------------------------------------------------------
   Texto a la izquierda, ajustes a la derecha y a la misma altura: apilados,
   había que desplazarse para ver el efecto de un control sobre lo que se
   está escribiendo. */
.voz-taller {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(280px, 1fr);
  gap: 22px;
  align-items: stretch;
}
/* Las dos columnas se igualan solas: la de ajustes marca el alto —depende de
   cuántos controles declare el motor— y el área de texto se estira hasta ahí.
   Fijar un alto a mano obliga a corregirlo cada vez que cambia un parámetro. */
.voz-texto { display: grid; grid-template-rows: auto 1fr; gap: 14px; }
/* El selector lleva `.formulario` a propósito. La regla global
   `.formulario label { align-content: start }` —que alinea los campos arriba
   de su fila— tiene más especificidad que una clase suelta, y ganaba: el área
   de texto se quedaba en su alto mínimo y dejaba 240 px de vacío bajo ella
   mientras la columna de ajustes seguía bajando. Aquí sí debe estirarse. */
.formulario .voz-campo-texto {
  display: grid;
  grid-template-rows: auto 1fr;
  gap: 6px;
  align-content: stretch;
}
.voz-campo-texto textarea { height: 100%; min-height: 220px; resize: none; }

.voz-ajustes {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 16px 18px;
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  background: var(--fondo-hundido);
}
/* Los cinco controles caben sin desplazarse: la explicación de cada uno vive
   en su tooltip, no en un párrafo debajo. */
.voz-ajustes .parametros-voz { grid-template-columns: 1fr; gap: 15px; }
.voz-ajustes label { font-size: 10.5px; letter-spacing: .07em; }
.voz-ajustes select { padding: 7px 10px; font-size: 12.5px; }
/* El interruptor comparte fila con su nombre, no ocupa una propia. */
.voz-ajustes .parametro:last-child .parametro-cabecera { margin-bottom: 0; }

@media (max-width: 860px) {
  .voz-taller { grid-template-columns: 1fr; }
  .voz-ajustes { max-height: none; }
  .voz-texto { min-height: 0; }
}

/* Pie del formulario: la ayuda a la izquierda y la acción a la derecha, en
   una fila que cierra el bloque en vez de dejar el botón suelto bajo el
   vacío. */
.voz-pie {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  margin-top: 4px;
  padding-top: 16px;
  border-top: 1px solid var(--linea);
}
.voz-pie .ayuda { margin: 0; max-width: 52ch; }
.voz-pie .boton { flex: none; }

/* --- Historial de audios --------------------------------------------------- */
.audio-cabecera {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 6px;
}
.audio-titulo { font-size: 14px; color: var(--texto); }
.audio-texto {
  margin: 10px 0 0;
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--texto-tenue);
}

/* --- Reproductor propio ----------------------------------------------------
   El control nativo cambia de aspecto en cada sistema y no encaja con nada de
   esta interfaz. */
.reproductor {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 10px;
  padding: 9px 13px;
  border: 1px solid var(--borde);
  border-radius: 999px;
  background: var(--fondo-hundido);
  max-width: 460px;
}
.repro-boton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  flex: none;
  border: 1px solid var(--senal);
  border-radius: 50%;
  background: var(--senal-suave);
  color: var(--senal);
  cursor: pointer;
  transition: background-color .18s ease;
}
.repro-boton:hover { background: var(--senal); color: var(--fondo); }
.reproductor[data-sonando="true"] .repro-boton {
  background: var(--senal);
  color: var(--fondo);
}

.repro-barra {
  position: relative;
  flex: 1;
  height: 4px;
  border-radius: 2px;
  background: var(--borde-vivo);
  cursor: pointer;
}
/* Zona de clic más alta que la barra: acertarle a 4 px con el ratón es
   imposible y saltar a un punto es lo que más se usa al revisar una toma. */
.repro-barra::before { content: ''; position: absolute; inset: -9px 0; }
.repro-avance {
  height: 100%;
  width: 0;
  border-radius: 2px;
  background: var(--senal);
  transition: width .1s linear;
}
.repro-tiempo {
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--texto-tenue);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.repro-descarga {
  display: inline-flex;
  color: var(--texto-tenue);
  transition: color .18s ease;
}
.repro-descarga:hover { color: var(--senal); }

/* Rótulo del grupo de controles. Habla con la misma voz que las etiquetas de
   campo, con una línea que separa el bloque de la selección de voz. */
.grupo-parametros { display: grid; gap: 14px; }
.grupo-titulo {
  display: block;
  padding-top: 12px;
  border-top: 1px solid var(--borde);
  font-size: 10.5px;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--texto-tenue);
}

/* --- Mandos del diagrama de operación ------------------------------------- */
/* Actividad y Capacidades eran dos tarjetas al final del panel: se leían como
   contenido y en realidad son instrumentos del pipeline que dibuja el
   diagrama. Aquí van como mandos suyos, cada uno con su lectura a la vista
   para que abrirlos sea una decisión y no un trámite. */
.diagrama-mandos {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
}

.mando {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.mando-cifra {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  padding: 1px 6px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--texto) 8%, transparent);
  color: var(--texto-medio);
}
.mando-cifra[data-tono="bien"]  { color: var(--estado-success); }
.mando-cifra[data-tono="aviso"] { color: var(--estado-warning); }

/* Punto de latido: gris cuando no pasa nada, encendido y pulsando durante
   unos segundos tras cada evento del planificador. */
.mando-pulso {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--texto) 25%, transparent);
  transition: background 180ms ease;
}
.mando-pulso[data-vivo="true"] {
  background: var(--senal);
  animation: mando-latido 1.2s ease-out infinite;
}

@keyframes mando-latido {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--senal) 55%, transparent); }
  100% { box-shadow: 0 0 0 7px transparent; }
}

@media (prefers-reduced-motion: reduce) {
  .mando-pulso[data-vivo="true"] { animation: none; }
}

/* El registro de actividad es una tabla de cuatro columnas: necesita más
   ancho que una ficha de lectura. */
.modal-caja--ancha { width: min(980px, 100%); }

@media (max-width: 720px) {
  /* Tres mandos no caben en una línea de móvil: sin envolver, el tercero
     —«Ocultar diagrama», el rótulo más largo— se salía del panel. Los dos
     medidores comparten la primera fila y el interruptor ocupa la segunda. */
  .diagrama-mandos { margin-left: 0; width: 100%; flex-wrap: wrap; }
  .diagrama-mandos .mando { flex: 1; justify-content: center; }
  .diagrama-mandos #toggle-diagrama { flex: 1 0 100%; justify-content: center; }
}

/* --- Subpestañas de configuración ---------------------------------------- */
/* Fuentes, alertas y bot comparten una entrada de menú y se reparten aquí. No
   son secciones distintas de la aplicación: son tres pantallas del mismo
   trabajo, que es dejar el sistema montado. */
.subpestanas {
  display: flex;
  gap: 4px;
  margin-bottom: 18px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--linea);
  overflow-x: auto;
}
.subpestanas a {
  padding: 7px 14px;
  border-radius: var(--radio-chico);
  font-size: 13px;
  color: var(--texto-medio);
  text-decoration: none;
  white-space: nowrap;
  transition: background-color .15s ease, color .15s ease;
}
.subpestanas a:hover { color: var(--texto); background: var(--fondo-hundido); }
.subpestanas a[aria-current] {
  color: var(--senal);
  background: var(--senal-suave);
}

/* --- Encuestas ------------------------------------------------------------
   Cada entrega trae ficha metodológica, aviso de proyectabilidad, nota y una
   lista de mediciones. Separadas solo por una línea, la lista de una se leía
   como parte de la ficha de la siguiente. Mismo tratamiento que las fichas de
   vídeo: piezas sobre fondo hundido, separadas por aire y contraste. */
#lista-encuestas {
  display: grid;
  gap: 12px;
  padding: 14px 16px;
  background: var(--fondo-hundido);
}
.encuesta {
  padding: 16px 18px;
  border-bottom: none;
  border-radius: var(--radio);
  background: var(--fondo-elevado);
  box-shadow: var(--sombra-pieza);
}
/* El nombre de la encuestadora y la edición mandan sobre el resto de la
   ficha: es lo que se busca al recorrer la lista. */
.encuesta > .informe-cabecera strong { font-size: 14.5px; }

/* Dos acciones en la cabecera de un panel: van juntas y pegadas a la derecha,
   no repartidas por la fila. */
.botonera--cabecera { margin-left: auto; gap: 8px; }
@media (max-width: 620px) {
  .botonera--cabecera { margin-left: 0; width: 100%; }
  .botonera--cabecera .boton { flex: 1; justify-content: center; }
}

/* --- Paginador ------------------------------------------------------------
   Aparece solo cuando el filtro devuelve más de una página. La cuenta va en
   medio y en cifras: «11–20 de 34» dice a la vez dónde estás y cuánto queda,
   que es lo que hace falta para decidir si merece la pena seguir hojeando. */
.paginador {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 14px 18px;
  border-top: 1px solid var(--linea);
}
.paginador[hidden] { display: none; }
.paginador-cuenta {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--texto-medio);
  font-variant-numeric: tabular-nums;
}
.paginador .boton[disabled] { opacity: .35; cursor: not-allowed; }


/* La aplicación ya no necesita esconderse: `index.html` solo se entrega a
   quien tiene sesión —lo comprueba el proxy contra /api/auth/check— así que
   no hay nada que tapar. La cortina de CSS que había aquí daba una impresión
   de seguridad que no era tal: el marcado ya estaba en el cliente. */

/* --- Acceso ---------------------------------------------------------------
   Cubre la aplicación entera. Nada del sistema se pinta detrás mientras se
   decide si hay sesión: las cifras y los marcos con los que la atacan son el
   producto, y enseñar el esqueleto ya adelanta su forma. */
/* Toda regla `display` de esta hoja lleva su compañera `[hidden]` al lado.
   Sin ella el atributo no hace nada —solo aporta `display: none` desde la
   hoja del navegador, que cualquier regla del documento pisa— y el elemento
   se pinta igual. Aquí eso dejaba los dos velos visibles a la vez y el último
   del marcado tapando al otro: salía la pantalla de cambio de contraseña sin
   haber entrado. Ya había pasado con el velo del menú en móvil. */
.acceso {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-items: center;
  padding: 24px;
  background: var(--fondo);
  overflow-y: auto;
}
.acceso[hidden] { display: none; }

/* Mientras no hay sesión, el fondo no se desplaza ni se ve a través. */
body.sin-sesion { overflow: hidden; }

.acceso-caja {
  width: min(400px, 100%);
  display: grid;
  gap: 16px;
  padding: 32px 28px 28px;
  border-radius: var(--radio);
  background: var(--fondo-elevado);
  box-shadow: var(--sombra-pieza);
}
.acceso-sello { width: 56px; height: 56px; justify-self: center; color: var(--senal); }
.acceso-titulo {
  margin: 0;
  text-align: center;
  font-size: 21px;
  letter-spacing: -0.01em;
}
.acceso-nota {
  margin: 0 0 6px;
  text-align: center;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--texto-tenue);
}

.acceso-campo {
  display: grid;
  gap: 6px;
  font-size: 11px;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--texto-tenue);
}
.acceso-campo input {
  width: 100%;
  height: 40px;
  box-sizing: border-box;
  padding: 0 12px;
  font: inherit;
  font-size: 14px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--texto);
  background: var(--fondo-hundido);
  border: 1px solid var(--borde-vivo);
  border-radius: var(--radio-chico);
}
.acceso-campo input:focus-visible { outline: 2px solid var(--senal); outline-offset: 1px; }
.acceso-campo .ayuda { text-transform: none; letter-spacing: 0; }

.acceso-error {
  margin: 0;
  padding: 9px 12px;
  border-radius: var(--radio-chico);
  background: var(--tinte-error, rgba(200, 80, 80, .12));
  color: var(--estado-error);
  font-size: 12.5px;
  line-height: 1.5;
}
.acceso-boton { justify-self: stretch; justify-content: center; height: 42px; }

/* --- Identidad en la cabecera --------------------------------------------- */

.quien { position: relative; }
.quien-boton {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 4px 10px 4px 4px;
  border: 1px solid var(--borde);
  border-radius: 999px;
  background: transparent;
  color: var(--texto-medio);
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}
.quien-boton:hover { border-color: var(--borde-vivo); color: var(--texto); }
.quien-inicial {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--senal-suave);
  color: var(--senal);
  font-family: var(--mono);
  font-size: 11px;
}
.quien-correo { max-width: 170px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.quien-menu {
  position: absolute;
  right: 0;
  top: calc(100% + 8px);
  z-index: 60;
  min-width: 210px;
  display: grid;
  padding: 6px;
  border: 1px solid var(--borde-vivo);
  border-radius: var(--radio-chico);
  background: var(--fondo-elevado);
  box-shadow: 0 20px 44px -24px #000;
}
.quien-menu[hidden] { display: none; }
.quien-rol {
  margin: 0;
  padding: 6px 10px 8px;
  font-size: 10.5px;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--texto-tenue);
  border-bottom: 1px solid var(--linea);
}
.quien-opcion {
  padding: 9px 10px;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--texto-medio);
  font: inherit;
  font-size: 13px;
  text-align: left;
  cursor: pointer;
}
.quien-opcion:hover { background: var(--fondo-hundido); color: var(--texto); }
.quien-opcion--salir { color: var(--estado-error); }

@media (max-width: 720px) {
  /* En móvil la cabecera ya va apretada: queda la inicial y se va el correo. */
  .quien-correo { display: none; }
}

/* --- Sesiones abiertas ----------------------------------------------------- */

.sesion-fila {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 12px;
  padding: 12px 18px;
  font-size: 13px;
}
.sesion-fila + .sesion-fila { border-top: 1px solid var(--linea); }
.sesion-quien { color: var(--texto); font-weight: 500; }
.sesion-ip { font-family: var(--mono); font-size: 12px; color: var(--texto-medio); }
.sesion-cuando { color: var(--texto-tenue); font-size: 12px; }
.sesion-agente {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--texto-tenue);
  font-size: 11.5px;
}

/* --- Portada de entrada ----------------------------------------------------
   Se muestra solo cuando la aplicación se abre instalada, no en el navegador.

   El encargo era que diera cuenta del nivel de seguridad «de manera elegante
   pero no obvia». Por eso no hay candados ni escudos: lo que dice que esto es
   cerrado es la secuencia —canal cifrado, identidad, sesión— escrita en
   monoespaciada, en gris, tachándose sola como una comprobación que ya estaba
   hecha antes de que llegaras. Un icono de seguridad tranquiliza a quien no
   sabe; una comprobación explícita informa a quien sí. */
.portada {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: grid;
  grid-template-rows: 1fr auto;
  place-items: center;
  padding: 40px 24px 32px;
  background: var(--fondo);
  /* La salida es lenta y sin escala: un desvanecimiento tranquilo se lee como
     un sistema que estaba listo, y un rebote como una animación de carga. */
  transition: opacity .5s ease .1s, visibility 0s linear .6s;
}
.portada[hidden] { display: none; }
.portada[data-fuera="true"] { opacity: 0; visibility: hidden; pointer-events: none; }

.portada-centro { display: grid; justify-items: center; gap: 18px; align-content: center; }

.portada-sello {
  width: 76px;
  height: 76px;
  color: var(--senal);
  /* El punteado entra desde el centro hacia fuera: es lo que cuenta el propio
     logotipo —la señal densa que se dispersa en ruido— y por eso la entrada
     correcta es una emergencia, no un giro. */
  animation: portada-emerge 1.5s cubic-bezier(.16, 1, .3, 1) both;
}

@keyframes portada-emerge {
  from { transform: scale(.86); opacity: 0; }
  to   { transform: scale(1);   opacity: 1; }
}

.portada-marca {
  margin: 0;
  font-size: 17px;
  font-weight: 600;
  letter-spacing: .01em;
  color: var(--texto);
}

/* La secuencia. Cada paso se enciende con retraso y queda en tono menor: es
   una constatación, no un anuncio. */
.portada-pasos {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 7px;
  /* Centrado como bloque y no elemento a elemento: con `justify-items:
     center` cada línea se centraba por su cuenta y los tres puntos quedaban
     escalonados, que en una lista de comprobación se lee como descuido. */
  justify-items: start;
  width: max-content;
  margin: 0 auto;
}
.portada-pasos li {
  position: relative;
  padding-left: 18px;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--texto-tenue);
  opacity: 0;
  animation: portada-paso .5s ease forwards;
}
.portada-pasos li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  width: 9px;
  height: 9px;
  margin-top: -4.5px;
  border-radius: 50%;
  border: 1px solid var(--senal);
  opacity: .35;
}
/* El punto se rellena cuando el paso ya pasó: el detalle que convierte una
   lista en una comprobación. */
.portada-pasos li::after {
  content: '';
  position: absolute;
  left: 3px;
  top: 50%;
  width: 3px;
  height: 3px;
  margin-top: -1.5px;
  border-radius: 50%;
  background: var(--senal);
  opacity: 0;
  animation: portada-punto .4s ease forwards;
}
.portada-pasos li:nth-child(1) { animation-delay: .15s; }
.portada-pasos li:nth-child(2) { animation-delay: .45s; }
.portada-pasos li:nth-child(3) { animation-delay: .75s; }
.portada-pasos li:nth-child(1)::after { animation-delay: .55s; }
.portada-pasos li:nth-child(2)::after { animation-delay: .85s; }
.portada-pasos li:nth-child(3)::after { animation-delay: 1.15s; }

@keyframes portada-paso { to { opacity: 1; } }
@keyframes portada-punto { to { opacity: 1; } }

.portada-pie {
  margin: 0;
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--texto-tenue);
  opacity: .55;
}

@media (prefers-reduced-motion: reduce) {
  .portada-sello { animation: none; }
  .portada-pasos li, .portada-pasos li::after { animation-duration: .01s; }
}

/* El sello de la cabecera. `currentColor` en el SVG y el color aquí: en claro
   y en oscuro es el mismo fichero. */

/* --- Rótulo de la marca ----------------------------------------------------
   La romana del logotipo, en los tres sitios donde aparece el nombre:
   cabecera, caja de acceso y portada. El resto de la interfaz sigue en
   grotesca; esto es lo único que es marca y no dato.

   Va sin negrita —la romana ya tiene el contraste que la grotesca conseguía
   engordando— y con el interletrado ligeramente abierto, que es lo que le da
   el aire de placa y no de titular. */
.cabecera h1,
.acceso-titulo,
.portada-marca {
  font-family: 'Cortex Logotipo', 'Space Grotesk', system-ui, sans-serif;
  /* Bruno Ace trae un solo peso y ya es ancha de por sí: forzar negrita la
     emborrona y añadir interletrado la desarma. */
  font-weight: 400;
  /* Caja mixta, no versales: en mayúsculas la romana se lee como cabecera de
     periódico y desaparece el contraste de astiles y remates que es lo que
     hace juego con los puntos del logotipo. */
  text-transform: none;
  letter-spacing: 0;
}

/* «CORTEX» en versales dentro del rótulo. La distinción la hace la caja
   tipográfica y el interletrado, no un cambio de peso: engordar además la
   segunda palabra la separaría de la primera en vez de componerla con ella. */
.marca-cortex { color: var(--senal); }

.cabecera h1 { font-size: 21px; }
.acceso-titulo { font-size: 25px; }
.portada-marca { font-size: 21px; }

@media (max-width: 620px) {
  .cabecera h1 { font-size: 17px; }
}
