/* ==================================================================================================
   F18 — /app/: LA FORMA. Rediseño visual completo, pedido por el operador con las capturas de
   ChatGPT y Codex delante: «simple, con animaciones sutiles pero rápidas y de calidad, robusto».

   LO QUE CAMBIA Y LO QUE NO. Cambia la geometría entera —el armazón, la barra, el hilo, el
   cajón— y las superficies. NO cambian: los colores de ESTADO (bien/alerta/mal/desconocido) ni
   el acento, que se midieron en F16 contra WCAG y siguen siendo la parte que dice la verdad; ni
   un solo contrato con los módulos (los ids y clases son los mismos); ni la regla de que el
   diseño no suaviza la verdad.

   LA IDEA VISUAL AHORA: «UNA SUPERFICIE, UNA COLUMNA». Como en las apps de referencia:
     - la pantalla es PLANA: barra a la izquierda sobre --fondo, lectura a la derecha sobre
       --superficie, separadas por un filete. Sin panel redondeado flotando dentro de un marco:
       eso leía como «widget», no como aplicación.
     - TODO lo que se lee vive en una columna central de 780px: el hilo, el hero y el cajón
       comparten el mismo eje, y por eso la pantalla se ve «acomodada» — un solo eje vertical.
     - lo humano va en BURBUJA a la derecha; lo del agente va en prosa plena a la izquierda,
       sin burbuja: la respuesta es el documento, el mensaje propio es la nota al margen.
     - el sistema habla en líneas pequeñas centradas: presente, pero nunca compitiendo.

   Las escalas (espacio --e*, tipografía --t-*, radios --r-*) se HEREDAN de F16: el sistema
   métrico funcionaba; lo que estaba mal era la composición.
   ================================================================================================== */

:root {
  color-scheme: light;

  /* ---- espacio (F16, intacta) ---- */
  --e1: 4px; --e2: 8px; --e3: 12px; --e4: 16px; --e5: 24px; --e6: 32px; --e7: 48px;

  /* ---- tipografía (F16, intacta) ---- */
  --t-2xs: 10.5px; --lh-2xs: 1.35;
  --t-xs: 11.5px;  --lh-xs: 1.4;
  --t-sm: 12.5px;  --lh-sm: 1.5;
  --t-md: 13.5px;  --lh-md: 1.55;
  --t-lg: 15.5px;  --lh-lg: 1.65;
  --t-xl: 19px;    --lh-xl: 1.35;
  --t-2xl: 23px;   --lh-2xl: 1.25;
  --t-3xl: 28px;   --lh-3xl: 1.15;
  --track-titulo: -.02em;
  --track-micro: .09em;
  --prosa: ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI Variable Text', 'Segoe UI',
    Roboto, 'Helvetica Neue', Arial, sans-serif;
  --mono: ui-monospace, 'SF Mono', SFMono-Regular, 'JetBrains Mono', 'Cascadia Mono', Menlo,
    Consolas, monospace;

  /* ---- radios (F16, intacta) ---- */
  --r-menudo: 7px; --r-control: 10px; --r-tarjeta: 14px; --r-burbuja: 18px; --r-chip: 999px;

  /* ---- medidas ---- */
  --ancho-lateral: 268px;
  --ancho-hilo: 780px;          /* la columna única: hilo, hero y cajón comparten este eje */
  --ancho-prosa: 68ch;
  --alto-control: 34px;
  --alto-tactil: 44px;
  --carril: 3px; --carril-fino: 2px; --filete: 1px;
  --punto: 6px; --punto-aqui: 8px; --trazo-punto: 1.5px;
  --icono-aviso: 16px; --icono-vacio: 32px;
  --esq-alto-lista: 116px; --esq-alto-hilo: 120px;

  /* ---- superficies: PLANAS, dos tonos ----
     --fondo es la barra; --superficie la zona de lectura; --elevada lo que flota (cajón,
     tarjetas, hero-tarjetas); --hundida lo que se marca (hover, burbuja humana, entrada de
     búsqueda). Contraste de texto: medido abajo, en PARES-MEDIDOS. */
  --fondo: #f2f3f5;
  --superficie: #ffffff;
  --elevada: #ffffff;
  --hundida: #ececef;
  --linea: rgba(16, 18, 22, .08);
  --borde: #767e8b;
  --borde-flotante: rgba(16, 18, 22, .13);
  --espina: rgba(16, 18, 22, .20);

  /* ---- tinta ---- */
  --tinta: #14171b;
  --tinta-suave: #4c535e;
  --tinta-apagada: #626c7a;

  /* ---- acento y estados (F16, MEDIDOS — no se tocan) ---- */
  --acento: #0f5fa6;
  --acento-tinta: #ffffff;
  --acento-tenue: #e2ecf7;
  --halo: 0 0 0 4px rgba(15, 95, 166, .16);
  --bien: #0e6b36;
  --alerta: #7a5200;
  --mal: #b3211b;
  --mal-fondo: #fbeceb;
  --desconocido: #6a2fbe;

  --sombra-1: 0 1px 2px rgba(16, 20, 26, .05), 0 2px 8px rgba(16, 20, 26, .05);
  --sombra-2: 0 2px 6px rgba(16, 20, 26, .06), 0 18px 44px rgba(16, 20, 26, .13);
  --esq-barra: #dcdfe4;
  --esq-brillo: #e9ebef;
  --marca: var(--borde);
}

@media (prefers-color-scheme: dark) {
  :root {
    color-scheme: dark;
    /* Oscuro a la manera de las referencias: barra un paso más honda que la lectura, y lo que
       flota un paso más claro. Sin sombras negras: en oscuro eleva el color. */
    --fondo: #16181b;
    --superficie: #212429;
    --elevada: #2a2d33;
    --hundida: #2c3036;
    --linea: rgba(233, 237, 243, .07);
    --borde: #8b93a1;
    --borde-flotante: rgba(233, 237, 243, .12);
    --espina: rgba(233, 237, 243, .16);
    --tinta: #e9edf3;
    --tinta-suave: #b7bdc7;
    --tinta-apagada: #8f97a3;
    --acento: #6bb4f5;
    --acento-tinta: #06121d;
    --acento-tenue: #16283a;
    --halo: 0 0 0 4px rgba(107, 180, 245, .18);
    --bien: #4fd48a;
    --alerta: #e9b368;
    --mal: #fb8497;
    --mal-fondo: #2e1a1e;
    --desconocido: #c3a5f6;
    --sombra-1: none;
    --sombra-2: none;
    --esq-barra: #23272d;
    --esq-brillo: #2f343b;
  }
}

/* PARES-MEDIDOS con el script de luminancia WCAG (corrido, no estimado). La primera versión de
   la apagada clara (#66707e) daba 4.3 sobre --hundida — bajo AA — y se oscureció a #626c7a:
   claro : tinta/superficie 18.0 · suave/superficie 7.8 · apagada/superficie 5.3 ·
           apagada/fondo 4.8 · apagada/hundida 4.5 · tinta/hundida 15.2 · acento/superficie 6.5
   oscuro: tinta/superficie 13.2 · suave/superficie 8.2 · apagada/superficie 5.3 ·
           apagada/fondo 6.0 · apagada/hundida 4.5 · tinta/hundida 11.3 · acento/superficie 7.0 */

/* ===== BASE (F16, portada) ===== */
/* ==================================================================================================
   BASE
   ================================================================================================== */

* { box-sizing: border-box; }
html, body { height: 100%; }

body {
  margin: 0;
  background: var(--fondo);
  color: var(--tinta);
  font: 400 var(--t-lg)/var(--lh-lg) var(--prosa);
  display: flex;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

.mono, code, pre, kbd { font-family: var(--mono); }
a { color: var(--acento); text-underline-offset: 2px; }

/* La selección lleva el acento tenue con tinta medida encima (12.8:1 oscuro, 15.0:1 claro): un
   detalle que se ve cien veces al día y que con el azul del sistema delataría que aquí no se
   eligió nada. */
::selection { background: var(--acento-tenue); color: var(--tinta); }

/* Barras de scroll finas y del color de la retícula. Sin esto, en oscuro Chrome pinta una barra
   clara que es lo más ruidoso de la pantalla. */
* { scrollbar-width: thin; scrollbar-color: var(--borde) transparent; }

:focus-visible {
  outline: var(--carril-fino) solid var(--acento);
  outline-offset: 2px;
  border-radius: var(--r-menudo);
}

/* Los controles nativos (casillas del inventario, selects, scrollbars) heredan el tema por
   color-scheme; el acento de la casilla marcada, por accent-color. Sin esto, una casilla de
   capacidad concedida se pinta con el azul del sistema y no con el de esta app. */
input, select, textarea, button {
  font-family: inherit;
  accent-color: var(--acento);
}


/* ===== MARKDOWN (F16, portada) ===== */
/* ==================================================================================================
   MARKDOWN DEL AGENTE (.cuerpo.md, árbol de markdown.js)
   white-space normal: dentro de un párrafo el salto simple colapsa a espacio, que es el markdown
   estándar; el pre-wrap del turno queda para el texto plano (humano/sistema), que sí conserva lo
   tecleado tal cual.
   NINGÚN título del agente se pasa a versalitas: las mayúsculas son de las etiquetas que escribe
   esta interfaz, nunca del texto que escribió otro.
   ================================================================================================== */

.turno .cuerpo.md { white-space: normal; }
.turno .cuerpo.md > :first-child { margin-top: 0; }
.turno .cuerpo.md > :last-child { margin-bottom: 0; }

.md-parrafo { margin: var(--e3) 0; }

.md-titulo1, .md-titulo2, .md-titulo3 {
  margin: var(--e5) 0 var(--e2);
  color: var(--tinta);
}
.md-titulo1 { font-size: var(--t-xl); line-height: var(--lh-xl); font-weight: 700; letter-spacing: var(--track-titulo); }
.md-titulo2 { font-size: var(--t-lg); line-height: var(--lh-md); font-weight: 700; letter-spacing: -.01em; }
.md-titulo3 { font-size: var(--t-md); line-height: var(--lh-md); font-weight: 700; letter-spacing: 0; }

.md-codigo {
  background: var(--hundida); color: var(--tinta);
  border-radius: var(--r-menudo);
  padding: .1em var(--e1);
  font-size: .92em;                 /* relativo: el código en línea no puede desalinear la prosa */
}
.turno.humano .cuerpo .md-codigo { background: var(--fondo); }   /* dentro de la burbuja, que se note */

.md-lista { margin: var(--e3) 0; padding-left: var(--e5); }
.md-lista li { margin: var(--e1) 0; }

/* La cita usa la MISMA raya de margen que el resto del sistema: coherencia por encima de adorno. */
.md-cita {
  margin: var(--e3) 0;
  padding: 0 0 0 var(--e4);
  border-left: var(--carril) solid var(--borde);
  color: var(--tinta-suave);
}

.md-enlace { color: var(--acento); }

/* Bloque de código: cabecera (lenguaje en versalitas + copiar) y pre con scroll-x PROPIO — un
   comando largo no fuerza scroll lateral en toda la línea de tiempo. */
.md-bloque {
  margin: var(--e4) 0;
  border: var(--filete) solid var(--linea); border-radius: var(--r-tarjeta);
  background: var(--hundida); overflow: hidden;
}
.md-bloque-cabecera {
  display: flex; align-items: center; gap: var(--e2);
  padding: var(--e1) var(--e1) var(--e1) var(--e3);
  border-bottom: var(--filete) solid var(--linea);
}
.md-lang {
  font: 600 var(--t-2xs)/var(--lh-2xs) var(--prosa);
  text-transform: uppercase; letter-spacing: var(--track-micro);
  color: var(--tinta-apagada);
}
.md-lang:empty { display: none; }      /* sin lenguaje declarado no se enseña un hueco */
.md-copiar {
  margin-left: auto;                   /* a la derecha aunque .md-lang esté vacía */
  border: 0; background: transparent; color: var(--tinta-suave); cursor: pointer;
  font: 400 var(--t-xs)/var(--lh-xs) var(--prosa);
  padding: var(--e1) var(--e2); border-radius: var(--r-menudo);
  white-space: nowrap;
}
.md-copiar:hover { background: var(--elevada); color: var(--tinta); }
.md-copiar.copiado { color: var(--bien); }
.md-bloque pre {
  margin: 0; padding: var(--e3);
  overflow-x: auto; white-space: pre;
  font-size: var(--t-sm); line-height: var(--lh-md);
  color: var(--tinta);
}


/* ===== TARJETAS DE ACCIÓN (F16, portada) ===== */
/* ==================================================================================================
   TARJETAS DE ACCIÓN — la raya del margen
   Cada tarjeta lleva un CARRIL de 3px pegado a su borde izquierdo, separado del alto por --e3 y
   redondeado por fuera: una marca trazada al margen, no un borde de color que envuelve la caja.
   Existe SIEMPRE (gris cuando no hay nada que decir), así que al cambiar de estado cambia el
   color y no se mueve ni un píxel del contenido.
   Lo caducado y lo rechazado NO se rebajan con opacity — eso rebajaría su contraste medido. Se
   apagan con el gris del carril y con su propia palabra.
   ================================================================================================== */

.tarjeta {
  position: relative;
  background: var(--elevada);
  border: var(--filete) solid var(--linea);
  border-radius: var(--r-tarjeta);
  box-shadow: var(--sombra-1);
  padding: var(--e4);
}
.tarjeta::after {
  content: '';
  position: absolute; left: 0; top: var(--e3); bottom: var(--e3);
  width: var(--carril);
  border-radius: 0 var(--r-chip) var(--r-chip) 0;
  background: var(--marca);
}
.tarjeta[data-estado="pendiente"]   { --marca: var(--alerta); }
.tarjeta[data-estado="ejecutada"]   { --marca: var(--bien); }
.tarjeta[data-estado="fallida"]     { --marca: var(--mal); }
.tarjeta[data-estado="rechazada"],
.tarjeta[data-estado="expirada"]    { --marca: var(--tinta-apagada); }
.tarjeta[data-estado="desconocido"] { --marca: var(--desconocido); }

/* La pendiente es lo único que pide algo AHORA, y aun así NO se le pone el borde entero de su
   color: eso es teñir la caja, no marcarla, y en claro un borde de --alerta (#7a5200) se lee
   marrón alrededor de una tarjeta blanca. Lo que la distingue ya es suficiente y todo es
   contenido: nace ABIERTA (tarjetas.js), su carril es ámbar, su insignia es ámbar y dentro lleva
   la caja de confirmación. El estado se marca; la caja no se pinta. */

.tarjeta > summary, .tarjeta .cab {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--e2) var(--e3);
  cursor: pointer; list-style: none;
}
.tarjeta > summary::-webkit-details-marker { display: none; }
.tarjeta .titulo {
  font-size: var(--t-lg); line-height: var(--lh-md); font-weight: 600;
  letter-spacing: -.01em;
  overflow-wrap: anywhere;
}
/* El orden del DOM es título · insignia · cadena · sello, y la cadena ocupa línea entera; sin
   `order` el sello caía a una TERCERA línea, solo, a la derecha, como una errata. Con esto la
   cabecera es: [título · insignia ······ fecha] y debajo la cadena, que es como se lee. */
.tarjeta > summary .sello { order: 1; margin-left: auto; }
.tarjeta > summary .cadena { order: 2; }

.tarjeta .detalle {
  display: flex; flex-direction: column; gap: var(--e1);
  margin-top: var(--e4); padding-top: var(--e3);
  border-top: var(--filete) solid var(--linea);
  font-size: var(--t-sm); line-height: var(--lh-sm);
  color: var(--tinta-suave);
}
/* El hueco de aviso de la tarjeta solo separa cuando tiene algo dentro. */
.tarjeta .detalle > [data-hueco-aviso]:not(:empty) { margin-top: var(--e2); }

/* -- la cadena de estados: un escalímetro, no una frase ---------------------------------------
   Cada peldaño lleva su punto: hueco = no alcanzado, relleno = alcanzado, relleno y GRANDE =
   aquí estás. Se lee de un vistazo hasta dónde llegó de verdad, que es justo lo que este
   proyecto no permite esconder. Salta a su propia línea (flex-basis 100%) para no pelearse con
   el título. Lo NO alcanzado va en apagado, jamás en verde. */
.cadena {
  flex: 1 0 100%;
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--e2);
  margin-top: var(--e1);
  font-size: var(--t-xs); line-height: var(--lh-xs);
  color: var(--tinta-apagada);
}
.peldano { display: inline-flex; align-items: center; gap: var(--e1); }
.peldano::before {
  content: '';
  width: var(--punto); height: var(--punto); border-radius: var(--r-chip);
  border: var(--trazo-punto) solid currentColor;
}
.peldano.hecho { color: var(--bien); }
.peldano.hecho::before { background: currentColor; }
.peldano.aqui { color: var(--tinta); font-weight: 700; }
.peldano.aqui::before { width: var(--punto-aqui); height: var(--punto-aqui); background: currentColor; }
.sep-cadena { color: var(--tinta-apagada); font-size: var(--t-2xs); }

/* -- insignia: la palabra del estado con su marca de 2px, sin marco y sin relleno --------------
   Versalitas porque es vocabulario de ESTA interfaz (ESTADO_ACCION), no texto de nadie. */
.insignia {
  display: inline-flex; align-items: center; gap: var(--e2);
  font: 700 var(--t-2xs)/var(--lh-2xs) var(--prosa);
  text-transform: uppercase; letter-spacing: var(--track-micro);
  color: var(--tinta-suave);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.insignia::after {
  content: '';
  order: -1;                                  /* la marca va DELANTE de la palabra */
  flex: 0 0 auto;
  width: var(--carril-fino); height: 1.1em;
  border-radius: var(--r-chip);
  background: currentColor;
}
.insignia.pendiente   { color: var(--alerta); }
.insignia.ejecutada   { color: var(--bien); }
.insignia.fallida     { color: var(--mal); }
.insignia.expirada,
.insignia.rechazada   { color: var(--tinta-apagada); }
/* REGLA 4 — el color nunca es la única señal. `.dato.desconocido` llevaba su subrayado punteado
   desde F16 y la insignia NO: el mismo estado se distinguía en la franja y no en la tarjeta.
   Lo cazó el generador del design system al aplicar las reglas escritas a una pieza que yo no
   había revisado contra ellas — que es exactamente para lo que sirve escribirlas. */
.insignia.desconocido {
  color: var(--desconocido);
  text-decoration: underline dotted; text-underline-offset: 3px;
}

/* -- confirmación inline: el control más serio de la app ---------------------------------------
   Se hunde en su propia caja para que se lea como «aquí se teclea algo que ejecuta», y la frase
   se enseña en mono sobre superficie elevada. La frase se TECLEA: sin autocompletar, a propósito
   (regla del proyecto, no decisión de diseño). */
.confirmar {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--e2);
  margin-top: var(--e3);
  padding: var(--e3);
  background: var(--hundida);
  border: var(--filete) solid var(--linea);
  border-radius: var(--r-tarjeta);
}
.confirmar input {
  flex: 1; min-width: 220px;
  background: var(--elevada); color: var(--tinta);
  border: var(--filete) solid var(--borde); border-radius: var(--r-control);
  padding: var(--e2) var(--e3);
  font: 400 var(--t-md)/var(--lh-md) var(--mono);
  outline: none;
}
/* F18 — en el teléfono, EJECUTAR ocupa su fila entera a 44px: es el gesto más serio de la app y
   las cuatro maquetas lo dibujan a ancho completo. En pantalla grande vuelve a su tamaño (abajo,
   en la capa de 48em): las dos mitades existen porque esta hoja es mobile-first. */
.confirmar button.primario { flex: 1 1 auto; min-height: var(--alto-tactil); }

.frase-esperada {
  flex-basis: 100%;
  color: var(--tinta-suave);
  font-size: var(--t-sm); line-height: var(--lh-lg);
}
.frase-esperada code {
  background: var(--elevada); color: var(--tinta);
  border: var(--filete) solid var(--linea);
  padding: var(--e1) var(--e2); border-radius: var(--r-menudo);
  font-size: var(--t-md); letter-spacing: .01em;
  user-select: text;
  /* La frase es larga y en un teléfono parte en dos líneas. Sin esto, el trozo de abajo pierde
     el padding y el radio y la frase se lee como dos rectángulos rotos — justo el texto que hay
     que copiar con los ojos y teclear sin un carácter de más. */
  -webkit-box-decoration-break: clone;
          box-decoration-break: clone;
}

/* F18 — la CABECERA del hilo (sesion.js, compartido: su gemelo vive en ui.css). Título grande y
   una línea de meta. Va DENTRO de .tiempo, así hereda la columna de 780px y no exige nodo nuevo
   en ningún armazón. El respiro de abajo la separa del primer turno sin regla dibujada: en el
   rediseño las zonas se separan por aire. */
.hilo-cab { margin-bottom: var(--e4); }
.hilo-titulo {
  margin: 0 0 var(--e1);
  font: 600 var(--t-2xl)/var(--lh-2xl) var(--prosa); color: var(--tinta);
  letter-spacing: var(--track-titulo);
  overflow-wrap: anywhere;
}
.hilo-meta {
  font: 400 var(--t-sm)/var(--lh-sm) var(--prosa); color: var(--tinta-apagada);
}

/* F18 — la anatomía de la tarjeta de acción, del rediseño. El QUÉ es el protagonista y va en
   mono grande: la persona confirma un comando, no un párrafo. El riesgo es un chip con su PALABRA
   —el color solo acompaña— y los metadatos una línea apagada. Las clases nacen en tarjetas.js,
   que es compartido: su gemelo de /ui/ vive en ui.css, y la suite exige que existan en las dos. */
.accion-cab {
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--e3);
  margin: var(--e2) 0 var(--e1);
}
.accion-que {
  min-width: 0;
  font: 600 var(--t-xl)/var(--lh-xl) var(--mono); color: var(--tinta);
  letter-spacing: -.01em;
  overflow-wrap: anywhere;
}
.accion-riesgo {
  flex: none;
  padding: 3px var(--e2);
  border: var(--filete) solid currentColor; border-radius: var(--r-menudo);
  font: 700 var(--t-2xs)/1 var(--prosa);
  text-transform: uppercase; letter-spacing: var(--track-micro);
  color: var(--tinta-apagada);
}
/* Los niveles que cortan algo llevan el tono de alerta; la PALABRA es la señal, el color acompaña. */
.accion-riesgo.r-destructive, .accion-riesgo.r-production { color: var(--alerta); }
.accion-riesgo.r-read { border-style: dotted; }   /* lectura: chip de trazo suave, no pide nada */
.accion-meta {
  margin-bottom: var(--e2);
  font: 400 var(--t-sm)/var(--lh-sm) var(--prosa); color: var(--tinta-apagada);
}

/* El JSON crudo: scroll PROPIO. Sin esto, un query_ref largo desborda la tarjeta y fuerza scroll
   lateral en toda la línea de tiempo. */
.tarjeta .crudo {
  overflow-x: auto; max-width: 100%;
  background: var(--hundida); color: var(--tinta-suave);
  border: var(--filete) solid var(--linea);
  border-radius: var(--r-control);
  padding: var(--e2) var(--e3);
  margin: var(--e2) 0 0;
  font-size: var(--t-xs); line-height: var(--lh-md);
}


/* ===== AVISOS (F16, portada) ===== */
/* ==================================================================================================
   AVISOS — error ≠ vacío ≠ trabajando. Tres cosas distintas, tres trajes distintos.
   El error es la ÚNICA pieza de la hoja con fondo tintado: la sobriedad que se aplica a todo lo
   demás, aquí haría daño. Lleva además carril, icono y su palabra.
   ================================================================================================== */

.aviso {
  position: relative;
  margin: 0;
  background: var(--elevada);
  border: var(--filete) solid var(--linea); border-radius: var(--r-tarjeta);
  padding: var(--e3) var(--e4);
  font-size: var(--t-sm); line-height: var(--lh-sm);
  color: var(--tinta-suave);
}
.aviso.mal {
  /* NO es flex, y la razón la encontró el generador del design system al escribir un aviso con
     texto y `<code>` mezclados: cada nodo suelto se convertía en un flex item y la frase salía
     partida y recortada. El flex no aportaba nada —el carril del margen es un `::after`
     posicionado, que no participa del flujo— así que era riesgo puro a cambio de cero. Hoy todos
     los avisos se construyen con `textContent` y por eso no se veía; el día que uno lleve un
     enlace o una negrita, se habría visto. */
  background: var(--mal-fondo);
  border-color: var(--mal);
  color: var(--mal);
  overflow-wrap: anywhere;      /* un motivo del servidor trae rutas e ids larguísimos */
}
.aviso.mal::after {
  content: '';
  position: absolute; left: 0; top: var(--e2); bottom: var(--e2);
  width: var(--carril);
  border-radius: 0 var(--r-chip) var(--r-chip) 0;
  background: var(--mal);
}
/* «Trabajando» no es ni bien ni mal: caja de trazo discontinuo, sin fondo. Provisional a la
   vista, sin fingir que ya hay un resultado. */
.aviso.trabajando {
  background: transparent;
  border-style: dashed;
  border-color: var(--borde);
  color: var(--tinta-suave);
}

.vacio {
  color: var(--tinta-apagada);
  font-size: var(--t-md); line-height: var(--lh-md);
}


/* ===== VACÍOS Y CARGANDO (F16, portada) ===== */
/* ==================================================================================================
   VACÍOS Y CARGANDO — los estados que más se miran en una app de administración.
   Un vacío bien hecho explica POR QUÉ está vacío y qué haría que dejara de estarlo; esos textos
   ya los escriben los módulos. Aquí va el traje: aire, línea corta, centrado y un icono dibujado
   a mano y embebido (data-uri + máscara, para que herede el color medido del texto).
   .inv-resumen queda FUERA: ahí `.vacio` no es un estado vacío, es la línea «0 de 12».
   ================================================================================================== */

.vacio:not(.inv-resumen) {
  display: flex; flex-direction: column; align-items: center;
  gap: var(--e3);
  max-width: 44ch;                 /* mucho más corta que la prosa: es un cartel, no un texto */
  margin-inline: auto;
  padding: var(--e7) var(--e4);
  text-align: center;
  text-wrap: pretty;               /* sin líneas huérfanas de una palabra */
  line-height: var(--lh-lg);
}
/* En la barra, el mismo traje con menos aire: 280px no dan para --e7 arriba y abajo. */
.conversaciones .vacio {
  padding: var(--e5) var(--e2);
  font-size: var(--t-sm);
  line-height: var(--lh-md);
}

.cargando {
  display: flex; flex-direction: column; gap: var(--e3);
  color: var(--tinta-apagada);
  font-size: var(--t-sm); line-height: var(--lh-sm);
}
.conversaciones > .cargando { padding: var(--e3) var(--e3) var(--e4); }
.principal > .cargando {
  max-width: var(--ancho-hilo); margin: 0 auto;
  padding: var(--e5) 0 var(--e6);
}

/* ---- iconos y esqueletos, dibujados aquí y embebidos ------------------------------------------
   Van de MÁSCARA y no de background-image por una razón concreta: dentro de un data-uri
   `currentColor` no resuelve (el SVG es otro documento y allí es negro), así que un
   background-image obligaría a mantener dos copias por icono, una por tema. Con máscara el color
   lo pone `background-color: currentColor` y el icono hereda EXACTAMENTE el color ya medido del
   texto al que acompaña. Una sola copia, imposible que se desincronice del tema. */
:root {
  /* Burbuja vacía con la línea de dentro punteada: «aquí se escribe, y todavía no hay nada». */
  --mask-vacio: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'><rect x='3' y='4.5' width='18' height='12' rx='3.5'/><path d='M7.6 16.5v3.7l4.4-3.7'/><path d='M7.8 10.5h8.4' stroke-dasharray='1.6 3.2'/></svg>");

  /* Exclamación en círculo. NO una equis: una equis en la esquina de una caja de color se lee
     como «cerrar esto», y este icono no cierra nada, avisa. Tampoco el triángulo, que en esta
     app ya significa --alerta («necesita atención»), no --mal («ha fallado»). */
  --mask-mal: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round'><circle cx='12' cy='12' r='9'/><path d='M12 7.4v5.4'/><path d='M12 16.3h.01'/></svg>");

  /* Esqueleto de la lista lateral: 4 filas de título + fecha, con anchos distintos (una pila de
     barras idénticas parece una tabla, no una lista que está llegando). */
  --mask-esq-lista: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 116' preserveAspectRatio='none' fill='%23000'><rect y='0' width='168' height='10' rx='5'/><rect y='15' width='56' height='7' rx='3.5'/><rect y='30' width='204' height='10' rx='5'/><rect y='45' width='56' height='7' rx='3.5'/><rect y='60' width='140' height='10' rx='5'/><rect y='75' width='56' height='7' rx='3.5'/><rect y='90' width='186' height='10' rx='5'/><rect y='105' width='56' height='7' rx='3.5'/></svg>");

  /* Esqueleto del hilo: cinco líneas de prosa, todas a la izquierda y neutras. Un esqueleto NO
     dibuja contenido reconocible: uno con forma de burbuja de humano, pintado antes de saber si
     hay sesión, insinuaría mensajes que quizá no existen. Esperar no es tener. */
  --mask-esq-hilo: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 640 120' preserveAspectRatio='none' fill='%23000'><rect y='0' width='596' height='12' rx='6'/><rect y='26' width='632' height='12' rx='6'/><rect y='52' width='540' height='12' rx='6'/><rect y='78' width='616' height='12' rx='6'/><rect y='104' width='408' height='12' rx='6'/></svg>");
}

@supports (mask-image: linear-gradient(#000, #000)) or (-webkit-mask-image: linear-gradient(#000, #000)) {
  .vacio:not(.inv-resumen)::before {
    content: '';
    flex: 0 0 auto;
    width: var(--icono-vacio); height: var(--icono-vacio);
    background-color: currentColor;      /* = --tinta-apagada, medido en ambos temas */
    -webkit-mask-image: var(--mask-vacio);
            mask-image: var(--mask-vacio);
    -webkit-mask-repeat: no-repeat;  mask-repeat: no-repeat;
    -webkit-mask-size: 100% 100%;    mask-size: 100% 100%;
  }
  .aviso.mal::before {
    content: '';
    flex: 0 0 auto;
    /* La caja del icono ES la caja de línea del texto (--t-sm × --lh-sm) y el dibujo va centrado
       dentro: así queda alineado con la primera línea sin un margen mágico de corrección, y
       sigue alineado si un día cambia el tamaño de texto del aviso. */
    width: var(--icono-aviso);
    height: calc(var(--t-sm) * var(--lh-sm));
    background-color: currentColor;       /* = --mal, medido sobre --mal-fondo */
    -webkit-mask-image: var(--mask-mal);
            mask-image: var(--mask-mal);
    -webkit-mask-repeat: no-repeat;  mask-repeat: no-repeat;
    -webkit-mask-position: center;   mask-position: center;
    -webkit-mask-size: var(--icono-aviso); mask-size: var(--icono-aviso);
  }
  /* Fuera de @supports no se genera esqueleto: sin máscaras, .cargando se queda con su texto,
     que es quien de verdad informa. Un bloque de color sin recortar no sería un esqueleto,
     sería una mancha. */
  .conversaciones > .cargando::after,
  .principal > .cargando::after {
    content: '';
    display: block;
    background: linear-gradient(100deg, var(--esq-barra) 0%, var(--esq-brillo) 45%, var(--esq-barra) 80%);
    -webkit-mask-repeat: no-repeat;  mask-repeat: no-repeat;
    -webkit-mask-size: 100% 100%;    mask-size: 100% 100%;
    animation: esqueleto-latido 1.8s ease-in-out infinite;
  }
  .conversaciones > .cargando::after {
    height: var(--esq-alto-lista);
    -webkit-mask-image: var(--mask-esq-lista);
            mask-image: var(--mask-esq-lista);
  }
  .principal > .cargando::after {
    height: var(--esq-alto-hilo);
    -webkit-mask-image: var(--mask-esq-hilo);
            mask-image: var(--mask-esq-hilo);
  }
}

/* El movimiento del esqueleto es un LATIDO de opacidad y no el barrido diagonal de costumbre: el
   barrido exige animar background-position (repintado por fotograma) y esto solo anima lo que
   resuelve el compositor. Quien informa de que se espera es el TEXTO, que no parpadea nunca. */
@keyframes esqueleto-latido {
  0%, 100% { opacity: 1; }
  50%      { opacity: .5; }
}
.esqueleto {
  min-height: var(--e3);
  border-radius: var(--r-menudo);
  background: linear-gradient(100deg, var(--esq-barra) 0%, var(--esq-brillo) 45%, var(--esq-barra) 80%);
  animation: esqueleto-latido 1.8s ease-in-out infinite;
}
.esqueleto.bloque { min-height: var(--e7); border-radius: var(--r-tarjeta); }


/* ===== SUGERENCIAS DE «/» (F16, portada) ===== */
/* ==================================================================================================
   POPOVER DE SUGERENCIAS DE «/»
   Flota sobre el cajón, alineado con la línea de composición (mismo clamp, mismo ancho).
   comandos.js lo crea como primer hijo de #cajon.
   ================================================================================================== */

.sugerencias {
  position: absolute; bottom: 100%; z-index: 20;
  left: clamp(var(--e4), 6vw, var(--e6)); right: clamp(var(--e4), 6vw, var(--e6));
  max-width: var(--ancho-hilo); margin: 0 auto var(--e2);
  padding: var(--e1);
  max-height: 40vh; overflow-y: auto;
  background: var(--elevada);
  border: var(--filete) solid var(--borde-flotante); border-radius: var(--r-burbuja);
  box-shadow: var(--sombra-2);
}
.sugerencias[hidden] { display: none; }
.sug {
  position: relative;
  display: flex; align-items: baseline; gap: var(--e3);
  width: 100%; text-align: left;
  border: 0; background: transparent; color: var(--tinta); cursor: pointer;
  padding: var(--e2) var(--e3); border-radius: var(--r-control);
  font: 400 var(--t-md)/var(--lh-md) var(--prosa);
}
.sug-nombre { font: 600 var(--t-md)/var(--lh-md) var(--mono); }   /* el comando es dato: mono */
.sug-desc {
  flex: 1; min-width: 0;
  color: var(--tinta-suave);
  font-size: var(--t-sm); line-height: var(--lh-sm);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  /* A la derecha, como en las apps de referencia: el ojo recorre la columna de comandos sin que
     las descripciones —que empiezan donde acaba cada nombre— lo hagan zigzaguear. */
  text-align: right;
}
/* el icono del comando: pequeño, apagado, centrado a la fila; el activo toma el acento. */
.sug-ic { width: 16px; height: 16px; flex: none; align-self: center; color: var(--tinta-apagada); }
.sug.activa .sug-ic, .paleta li.activo .sug-ic { color: var(--acento); }
.paleta li .sug-ic { width: 15px; height: 15px; align-self: center; }
.sug:hover { background: var(--hundida); }
/* La activa: fondo tenue + la misma marca fina de 2px de toda la app. */
.sug.activa { background: var(--acento-tenue); }
.sug.activa::after {
  content: '';
  position: absolute; left: 0; top: var(--e1); bottom: var(--e1);
  width: var(--carril-fino); border-radius: var(--r-chip);
  background: var(--acento);
}


/* ===== ADJUNTOS (F17, portada) ===== */
/* ==================================================================================================
   ADJUNTOS (F17 2B) — el «+» del cajón, su menú, el velo de soltar, el panel de subida y el
   índice de un .zip. Lo crea todo adjuntos.js; aquí solo vive el traje.

   Tres decisiones que salen del sistema y no de un gusto:

     1. EL ESTADO ES UNA MARCA. Cada fila de subida y cada fila del índice llevan su carril fino
        de 2px del color de su estado, y su PALABRA al lado. Ni una fila tintada.
     2. «SE VE RARA» NO ES «ESTÁ MAL». Una ruta que esta pantalla no sabe juzgar —un `..`, una
        marca bidireccional— va en --desconocido, que es exactamente el color de «no se pudo
        comprobar». Quien decide es el servidor, y el color lo dice antes que el texto. Pintarla
        en --mal sería que el cliente dictara un veredicto que no le corresponde.
     3. LAS RUTAS SON DATO: van en mono, como los comandos del popover y el código del inventario.
        Un nombre que viene de fuera no se viste igual que la prosa de esta interfaz.
   ================================================================================================== */

.adj { position: relative; flex: 0 0 auto; align-self: flex-end; }

/* Quieto hasta que se le pasa por encima: en el cajón manda el texto, y un segundo botón con
   marco compitiendo con «enviar» convierte la pieza más mirada de la app en una barra de
   herramientas. El borde nace transparente para que al aparecer no mueva nada de sitio. */
.adjuntar {
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--alto-control); height: var(--alto-control);
  background: transparent; color: var(--tinta-suave);
  border: var(--filete) solid transparent; border-radius: var(--r-control);
  cursor: pointer;
  font: 400 var(--t-xl)/1 var(--prosa);
  transition: color .12s ease, background-color .12s ease, border-color .12s ease;
}
.adjuntar:hover { background: var(--hundida); color: var(--tinta); border-color: var(--linea); }
.adjuntar[aria-expanded="true"] { background: var(--hundida); color: var(--acento); border-color: var(--acento); }

/* El menú nace del botón y por eso cuelga de él (mismo gesto que el popover de «/»). */
.adj-menu {
  position: absolute; bottom: calc(100% + var(--e2)); left: 0; z-index: 21;
  min-width: 248px; padding: var(--e1);
  background: var(--elevada);
  border: var(--filete) solid var(--borde-flotante); border-radius: var(--r-tarjeta);
  box-shadow: var(--sombra-2);
}
.adj-menu[hidden] { display: none; }
.adj-opcion {
  display: flex; align-items: baseline; gap: var(--e3);
  width: 100%; text-align: left;
  border: 0; background: transparent; color: var(--tinta); cursor: pointer;
  padding: var(--e2) var(--e3); border-radius: var(--r-control);
  font: 400 var(--t-md)/var(--lh-md) var(--prosa);
}
.adj-op-nombre { font-weight: 600; white-space: nowrap; }
.adj-op-desc { flex: 1; min-width: 0; color: var(--tinta-suave); font-size: var(--t-sm); line-height: var(--lh-sm); }
.adj-opcion:hover:not(:disabled) { background: var(--hundida); }
/* Una opción que este navegador no puede cumplir se apaga con COLOR y conserva su explicación al
   lado; el fondo hundido del `button:disabled` global la convertiría en un bloque gris dentro de
   un menú flotante, que se lee como un error de pintado y no como «aquí no se puede». */
.adj-opcion:disabled {
  background: transparent; border-color: transparent;
  color: var(--tinta-apagada); cursor: default;
}
.adj-opcion:disabled .adj-op-desc { color: var(--tinta-apagada); }

/* Los <input type=file> viven fuera de la vista pero DENTRO del documento: un display:none no
   siempre se puede pulsar por programa, y sacarlos del árbol los dejaría sin evento. */
.adj-entradas { display: contents; }
.adj-oculto {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); border: 0; opacity: 0; pointer-events: none;
}

/* -- el velo de soltar ---------------------------------------------------------------------------
   Excepción declarada al «nada se tiñe»: esto no es un ESTADO de un dato, es la respuesta a un
   gesto que está ocurriendo ahora mismo y dura lo que dura el arrastre. Va en --acento-tenue
   (donde hay acción) con texto medido encima, y el marco discontinuo dice «zona de soltar» sin
   pedirle el color a la semántica. `pointer-events: none` es funcional, no estético: el drop lo
   escucha el documento, y un velo que capturara el puntero se lo comería. */
.soltar {
  position: fixed; inset: 0; z-index: 40;
  display: flex; align-items: center; justify-content: center;
  padding: var(--e5);
  pointer-events: none;
  background: var(--acento-tenue);
}
.soltar[hidden] { display: none; }
.soltar-caja {
  max-width: 48ch; padding: var(--e5) var(--e6);
  text-align: center; text-wrap: pretty;
  background: var(--elevada); color: var(--tinta);
  border: var(--carril-fino) dashed var(--acento); border-radius: var(--r-tarjeta);
  box-shadow: var(--sombra-2);
  font: 600 var(--t-md)/var(--lh-lg) var(--prosa);
}
/* El panel de lectura marca que va a recibir. Es el mismo filete de siempre, en acento. */
body.arrastrando .columna { border-color: var(--acento); }

/* -- el panel de subida --------------------------------------------------------------------------
   Vive dentro de un turno de sistema, así que hereda su tinta y su tamaño; lo que se declara aquí
   es lo que tiene que DEJAR de heredar. */
.subida, .indice {
  display: flex; flex-direction: column; gap: var(--e2);
  margin-top: var(--e1);
}
.subida-cab, .indice-cab {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--e2) var(--e3);
  color: var(--tinta);
  font-size: var(--t-md); line-height: var(--lh-md); font-weight: 700;
  letter-spacing: -.01em;
  overflow-wrap: anywhere;
}
/* El título lleva el nombre de la carpeta, que es DATO ajeno: puede ser larguísimo y tiene que
   partir por donde haga falta antes que empujar la cuenta fuera de la caja. */
.subida-titulo { min-width: 0; overflow-wrap: anywhere; }
.subida-cuenta {
  margin-left: auto;
  color: var(--tinta-apagada);
  font: 400 var(--t-xs)/var(--lh-xs) var(--prosa);
  font-variant-numeric: tabular-nums;   /* cambia por cada archivo: no puede bailar al hacerlo */
  white-space: nowrap;
}
.subida-sub {
  color: var(--tinta-suave);
  font-size: var(--t-sm); line-height: var(--lh-sm); font-weight: 600;
  margin-top: var(--e2);
}
/* El corte de PANTALLA: se dice siempre, y se dice apagado — no es un problema, es una elección
   de esta vista. Truncar de verdad (dejar de mirar) va en .aviso.mal, que es otra cosa. */
.subida-mas {
  color: var(--tinta-apagada);
  font-size: var(--t-xs); line-height: var(--lh-md);
}
.subida-rechazos { display: flex; flex-direction: column; gap: var(--e2); }

.subida-lista, .indice-lista {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: var(--e1);
}

.subida-fila {
  position: relative;
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--e1) var(--e3);
  padding: var(--e2) var(--e3);
  background: var(--elevada);
  border: var(--filete) solid var(--linea); border-radius: var(--r-control);
  font-size: var(--t-sm); line-height: var(--lh-sm);
  --marca: var(--borde);
}
.subida-fila::after {
  content: '';
  position: absolute; left: 0; top: var(--e2); bottom: var(--e2);
  width: var(--carril-fino);
  border-radius: 0 var(--r-chip) var(--r-chip) 0;
  background: var(--marca);
}
.subida-fila[data-estado="cola"]      { --marca: var(--borde); }
.subida-fila[data-estado="leyendo"],
.subida-fila[data-estado="subiendo"]  { --marca: var(--acento); }
.subida-fila[data-estado="hecho"]     { --marca: var(--bien); }
.subida-fila[data-estado="fallo"]     { --marca: var(--mal); }
.subida-fila[data-estado="rechazado"] { --marca: var(--tinta-apagada); }
/* «Se ve rara» = esta pantalla NO puede juzgarla, y ese es el color de «no se pudo comprobar».
   Va en su propio atributo y DESPUÉS de los estados a propósito: la ruta sigue siendo sospechosa
   después de subirse, así que la marca no puede borrarla el «subido» de un segundo más tarde. */
.subida-fila[data-rara="si"]          { --marca: var(--desconocido); }

/* La ruta es DATO ajeno: mono, y que parta por donde haga falta antes que desbordar la fila. */
.subida-ruta, .indice-ruta {
  flex: 1 1 55%; min-width: 0;
  font-family: var(--mono); font-size: var(--t-xs); line-height: var(--lh-md);
  color: var(--tinta);
  overflow-wrap: anywhere;
}
.subida-peso, .indice-peso {
  color: var(--tinta-apagada);
  font-size: var(--t-xs); line-height: var(--lh-md);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* La palabra del estado, en versalitas porque la escribe ESTA interfaz. El color acompaña al
   carril; nunca va solo — la palabra está siempre escrita. */
.subida-estado {
  margin-left: auto;
  font: 700 var(--t-2xs)/var(--lh-2xs) var(--prosa);
  text-transform: uppercase; letter-spacing: var(--track-micro);
  color: var(--tinta-apagada);
  white-space: nowrap;
}
.subida-fila[data-estado="leyendo"] .subida-estado,
.subida-fila[data-estado="subiendo"] .subida-estado { color: var(--acento); }
.subida-fila[data-estado="hecho"] .subida-estado    { color: var(--bien); }
.subida-fila[data-estado="fallo"] .subida-estado    { color: var(--mal); }
/* El motivo ocupa línea entera: un error del servidor trae ids y rutas larguísimos y en una
   columna estrecha se leería a dos letras por renglón. */
.subida-motivo, .indice-nota {
  flex-basis: 100%;
  color: var(--tinta-suave);
  font-size: var(--t-xs); line-height: var(--lh-md);
  overflow-wrap: anywhere;
}

/* -- el índice de un .zip ------------------------------------------------------------------------
   Filas más ligeras que las de subida: son muchas y solo se leen. Sin caja; el carril basta. */
.indice-cifras {
  color: var(--tinta-suave);
  font-size: var(--t-sm); line-height: var(--lh-sm);
  font-variant-numeric: tabular-nums;
}
.indice-fila {
  position: relative;
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--e1) var(--e3);
  padding: var(--e1) 0 var(--e1) var(--e3);
  --marca: var(--espina);
}
.indice-fila::after {
  content: '';
  position: absolute; left: 0; top: .15em; bottom: .15em;
  width: var(--carril-fino);
  border-radius: var(--r-chip);
  background: var(--marca);
}
.indice-fila[data-tipo="directorio"] { --marca: var(--borde); }
/* Un contenedor dentro del archivo pide atención humana: se lista y NO se abre. */
.indice-fila[data-tipo="anidado"]    { --marca: var(--alerta); }
.indice-fila[data-tipo="rechazada"]  { --marca: var(--mal); }
.indice-fila[data-tipo="rechazada"] .indice-peso { color: var(--mal); font-weight: 600; }


/* ===== PALETA ⌘K (F16, portada) ===== */
/* ==================================================================================================
   PALETA (⌘K)
   ================================================================================================== */

.paleta {
  width: min(560px, 92vw); padding: 0;
  background: var(--elevada); color: var(--tinta);
  border: var(--filete) solid var(--borde-flotante); border-radius: var(--r-burbuja);
  box-shadow: var(--sombra-2);
}
/* El velo desenfoca lo de detrás en vez de solo oscurecerlo: dice «esto de aquí manda ahora».
   Quien no entienda backdrop-filter se queda con el velo a secas, que ya cumple. */
.paleta::backdrop {
  background: rgba(10, 12, 16, .48);
  -webkit-backdrop-filter: blur(3px);
          backdrop-filter: blur(3px);
}
.paleta input {
  width: 100%;
  border: 0; border-bottom: var(--filete) solid var(--borde);   /* delimita un campo: se mide a 3:1 */
  background: transparent; color: var(--tinta);
  padding: var(--e4);
  font: 400 var(--t-lg)/var(--lh-md) var(--prosa);
  outline: none;
}
.paleta input::placeholder { color: var(--tinta-apagada); }
.paleta ul { list-style: none; margin: 0; padding: var(--e2); max-height: 50vh; overflow-y: auto; }
.paleta li {
  position: relative;
  display: flex; gap: var(--e3);
  padding: var(--e2) var(--e3); border-radius: var(--r-control);
  cursor: pointer;
  font-size: var(--t-md); line-height: var(--lh-md);
}
.paleta li .desc { flex: 1; text-align: right; color: var(--tinta-apagada); }
.paleta li:hover { background: var(--hundida); }
.paleta li.activo { background: var(--acento-tenue); }
.paleta li.activo::after {
  content: '';
  position: absolute; left: 0; top: var(--e1); bottom: var(--e1);
  width: var(--carril-fino); border-radius: var(--r-chip);
  background: var(--acento);
}


/* ===== BOTONES Y CONTROLES (F16, portada) ===== */
/* ==================================================================================================
   BOTONES Y CONTROLES COMPARTIDOS
   El primario es el único con acento de relleno: es LA acción. El secundario es un control
   delimitado por --borde (medido a 3:1) — un botón que solo se distingue por el texto no se
   distingue.
   ================================================================================================== */

button.primario {
  background: var(--acento); color: var(--acento-tinta);
  border: var(--filete) solid var(--acento); border-radius: var(--r-control);
  padding: var(--e2) var(--e4);
  font: 600 var(--t-md)/var(--lh-md) var(--prosa);
  cursor: pointer;
  transition: filter .12s ease;
}
button.primario:hover:not(:disabled) { filter: brightness(1.08); }

button.secundario {
  background: var(--elevada); color: var(--tinta);
  border: var(--filete) solid var(--borde); border-radius: var(--r-control);
  padding: var(--e2) var(--e3);
  font: 500 var(--t-md)/var(--lh-md) var(--prosa);
  cursor: pointer;
  transition: border-color .12s ease, color .12s ease;
}
button.secundario:hover:not(:disabled) { border-color: var(--acento); color: var(--acento); }

/* Deshabilitado = «hay algo en vuelo». Se dice con color y cursor, no bajando la opacidad del
   texto por debajo de lo medido. */
button:disabled {
  cursor: default;
  color: var(--tinta-apagada);
  background: var(--hundida);
  border-color: var(--borde);
  filter: none;
}
button.primario:disabled { background: var(--hundida); border-color: var(--borde); }


/* ===== VISTA DE ACCIONES (F16, portada) ===== */
/* ==================================================================================================
   VISTA DE ACCIONES DEL SISTEMA (vistaAcciones.js)
   Ninguna de estas clases tenía UNA SOLA regla en ninguna hoja: la pantalla que la barra abre con
   más frecuencia salía en Times New Roman de navegador. Aquí se viste entera.
   ================================================================================================== */

.vista {
  display: flex; flex-direction: column; gap: var(--e5);
  max-width: var(--ancho-hilo); margin: 0 auto;
}
.vista-cab {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--e4);
  padding-bottom: var(--e4);
  border-bottom: var(--filete) solid var(--linea);
}
.vista-titulos { min-width: 0; }
/* El único texto grande de la app: 28px, peso 700 y tracking negativo. La jerarquía empieza aquí
   — sin un título con peso de verdad, toda la pantalla parece la misma cosa repetida. */
.vista-titulo {
  margin: 0;
  font-size: var(--t-3xl); line-height: var(--lh-3xl); font-weight: 700;
  letter-spacing: var(--track-titulo);
}
.vista-sub {
  margin: var(--e2) 0 0;
  max-width: 64ch;
  color: var(--tinta-suave);
  font-size: var(--t-md); line-height: var(--lh-lg);
  text-wrap: pretty;
}
.vista-cab .secundario { flex: 0 0 auto; }
.vista-cuerpo { display: flex; flex-direction: column; gap: var(--e3); }

/* Los filtros son un CONTROL SEGMENTADO hundido: un grupo, no cuatro botones sueltos. El activo
   se levanta a superficie elevada y lleva la marca de 2px en acento. */
.filtros {
  display: inline-flex; flex-wrap: wrap; align-self: flex-start;
  gap: var(--e1); padding: var(--e1);
  background: var(--hundida);
  border: var(--filete) solid var(--linea); border-radius: var(--r-control);
}
.filtro {
  display: inline-flex; align-items: center; gap: var(--e2);
  border: 0; background: transparent; color: var(--tinta-suave); cursor: pointer;
  padding: var(--e2) var(--e3); border-radius: var(--r-menudo);
  font: 500 var(--t-md)/var(--lh-md) var(--prosa);
}
.filtro:hover { color: var(--tinta); }
.filtro.activo {
  background: var(--elevada); color: var(--tinta); font-weight: 600;
  box-shadow: var(--sombra-1);
}
.filtro.activo::before {
  content: '';
  flex: 0 0 auto;
  width: var(--carril-fino); height: 1.1em;
  border-radius: var(--r-chip);
  background: var(--acento);
}
/* La cuenta se queda VACÍA cuando no hay lista fiable (vistaAcciones.js no escribe un cero que
   nadie comprobó): sin :empty aquí saldría un hueco con separación pidiendo un número. */
.filtro-cuenta {
  font-size: var(--t-xs); color: var(--tinta-apagada);
  font-variant-numeric: tabular-nums;
}
.filtro-cuenta:empty { display: none; }
.filtro.activo .filtro-cuenta { color: var(--tinta-suave); }


/* ===== INVENTARIO (F16, portada) ===== */
/* ==================================================================================================
   INVENTARIO (/inventario dentro del hilo — inventario.js)
   Mismo idioma visual que las tarjetas de acción: carril al margen, versalitas para lo que es
   vocabulario de la interfaz, y color SOLO en los dos estados que significan algo — «necesita
   revisión» y «no reconocido». Pintar «Gestionado» de verde diría «esto está bien», y gestionado
   no significa comprobado.
   ================================================================================================== */

.inventario { display: flex; flex-direction: column; gap: var(--e4); }

/* La caja de la regla: lo primero que se lee, porque evita el malentendido caro. No lleva acento
   —no es una acción— sino el carril de --alerta: es una advertencia estructural. */
.regla {
  position: relative;
  background: var(--elevada);
  border: var(--filete) solid var(--linea);
  border-radius: var(--r-tarjeta);
  box-shadow: var(--sombra-1);
  padding: var(--e4);
}
.regla::after {
  content: '';
  position: absolute; left: 0; top: var(--e3); bottom: var(--e3);
  width: var(--carril); border-radius: 0 var(--r-chip) var(--r-chip) 0;
  background: var(--alerta);
}
.regla-titulo {
  font-size: var(--t-lg); line-height: var(--lh-md); font-weight: 700;
  letter-spacing: -.01em;
}
.regla-pasos {
  margin: var(--e2) 0 0; padding-left: var(--e5);
  font-size: var(--t-sm); line-height: var(--lh-lg);
  color: var(--tinta-suave);
}
.regla-pasos li { margin: var(--e1) 0; }
.regla-cola {
  margin: var(--e3) 0 0;
  font-size: var(--t-sm); line-height: var(--lh-lg);
  color: var(--tinta-suave);
}

.inv-controles { display: flex; flex-wrap: wrap; gap: var(--e2); }
.inv-controles[hidden] { display: none; }
.inv-filtro, .cand-entorno {
  background: var(--elevada); color: var(--tinta);
  border: var(--filete) solid var(--borde); border-radius: var(--r-control);
  padding: var(--e2) var(--e3);
  font: 500 var(--t-md)/var(--lh-md) var(--prosa);
  cursor: pointer;
}
.inv-buscar {
  flex: 1; min-width: 180px;
  background: var(--elevada); color: var(--tinta);
  border: var(--filete) solid var(--borde); border-radius: var(--r-control);
  padding: var(--e2) var(--e3);
  font: 400 var(--t-md)/var(--lh-md) var(--prosa);
  outline: none;
}
.inv-buscar::placeholder { color: var(--tinta-apagada); }

.inv-estado { display: flex; flex-direction: column; gap: var(--e2); }
.inv-resumen {
  color: var(--tinta-suave);
  font-size: var(--t-sm); line-height: var(--lh-sm);
  font-variant-numeric: tabular-nums;         /* «3 de 12 · 40 en total» no puede bailar */
}
.inv-resumen.vacio { color: var(--tinta-apagada); padding: 0; }

.inv-lista { display: flex; flex-direction: column; gap: var(--e3); }

.cand {
  position: relative;
  background: var(--elevada);
  border: var(--filete) solid var(--linea);
  border-radius: var(--r-tarjeta);
  box-shadow: var(--sombra-1);
  padding: var(--e4);
}
.cand::after {
  content: '';
  position: absolute; left: 0; top: var(--e3); bottom: var(--e3);
  width: var(--carril); border-radius: 0 var(--r-chip) var(--r-chip) 0;
  background: var(--marca);
}
.cand[hidden] { display: none; }
.cand[data-estado="est-revision"]    { --marca: var(--alerta); }
.cand[data-estado="est-desconocido"] { --marca: var(--desconocido); }

.cand-cab { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--e2) var(--e3); }
.cand-tipo {
  font: 600 var(--t-2xs)/var(--lh-2xs) var(--prosa);
  text-transform: uppercase; letter-spacing: var(--track-micro);
  color: var(--tinta-apagada);
}
.cand-nombre {
  font-size: var(--t-lg); line-height: var(--lh-md); font-weight: 600;
  letter-spacing: -.01em; overflow-wrap: anywhere;
}
/* «compartido» avisa de que tocarlo afecta a varias aplicaciones: es una advertencia, y por eso
   lleva marca de --alerta como las demás advertencias, no una etiqueta neutra. */
.cand-compartido {
  display: inline-flex; align-items: center; gap: var(--e2);
  font: 700 var(--t-2xs)/var(--lh-2xs) var(--prosa);
  text-transform: uppercase; letter-spacing: var(--track-micro);
  color: var(--alerta);
}
.cand-compartido::before {
  content: '';
  width: var(--carril-fino); height: 1.1em; border-radius: var(--r-chip);
  background: currentColor;
}
.cand-conf {
  margin-left: auto;
  font-size: var(--t-xs); line-height: var(--lh-xs);
  color: var(--tinta-apagada);
  font-variant-numeric: tabular-nums;
}

/* Las insignias del inventario comparten la base .insignia (marca de 2px + versalitas); solo
   cambian el color las que dicen algo que no es neutro. */
.insignia.est-revision    { color: var(--alerta); }
.insignia.est-desconocido { color: var(--desconocido); }
.insignia.est-detectado,
.insignia.est-incorporado,
.insignia.est-gestionado  { color: var(--tinta-suave); }
.insignia.est-apartado    { color: var(--tinta-apagada); }

.cand-aviso:not(:empty) { margin-top: var(--e3); }
.cand-acciones {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--e2);
  margin-top: var(--e3);
}
.cand-nota {
  display: block;
  color: var(--tinta-suave);
  font-size: var(--t-sm); line-height: var(--lh-sm);
}
.cand-acciones .cand-nota { flex-basis: 100%; }
.cand > .cand-nota { margin-top: var(--e2); }

/* Las capacidades cuelgan de un <details> y se cargan al abrirlo. */
.cand-caps { margin-top: var(--e3); border-top: var(--filete) solid var(--linea); padding-top: var(--e3); }
.cand-caps-titulo {
  list-style: none; cursor: pointer;
  font: 600 var(--t-md)/var(--lh-md) var(--prosa);
  color: var(--acento);                       /* lo único pulsable de una ficha ya incorporada */
}
.cand-caps-titulo::-webkit-details-marker { display: none; }
.cand-caps-cuerpo { display: flex; flex-direction: column; gap: var(--e3); margin-top: var(--e3); }

.cap-grupo {
  display: flex; flex-direction: column; gap: var(--e1);
  background: var(--superficie);
  border: var(--filete) solid var(--linea); border-radius: var(--r-control);
  padding: var(--e3);
}
.cap-grupo-titulo { font-size: var(--t-md); line-height: var(--lh-md); font-weight: 700; }
.cap-grupo-ex {
  color: var(--tinta-suave);
  font-size: var(--t-sm); line-height: var(--lh-sm);
  margin-bottom: var(--e2);
}
/* Cada capacidad es una casilla con su código y su explicación. Altura táctil desde ya: esto se
   marca con el dedo tanto como con el ratón, y una casilla que se falla concede lo que no era. */
.cap {
  display: flex; align-items: flex-start; gap: var(--e2);
  min-height: var(--alto-tactil);
  padding: var(--e2);
  border-radius: var(--r-menudo);
  cursor: pointer;
}
.cap:hover { background: var(--elevada); }
.cap-texto { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--e2); min-width: 0; }
.cap-codigo {
  font: 500 var(--t-sm)/var(--lh-sm) var(--mono);
  background: var(--hundida); color: var(--tinta);
  border-radius: var(--r-menudo); padding: .1em var(--e1);
}
.cap-desc { color: var(--tinta-suave); font-size: var(--t-sm); line-height: var(--lh-sm); }
.cap-aviso:not(:empty) { margin-top: var(--e2); }


/* ===== ENTRADA (F16, portada) ===== */
/* ==================================================================================================
   ENTRADA (sin sesión)
   La app entera es esto cuando no hay sesión, así que lleva la firma de la casa: la misma raya de
   acento de la marca, arriba del formulario.
   ================================================================================================== */

.entrada {
  display: flex; flex-direction: column; gap: var(--e3);
  max-width: 380px; margin: 12vh auto 0;
  padding: var(--e6);
  background: var(--elevada);
  border: var(--filete) solid var(--linea); border-radius: var(--r-burbuja);
  box-shadow: var(--sombra-2);
}
.entrada::before {
  content: '';
  width: var(--e5); height: var(--carril);
  border-radius: var(--r-chip);
  background: var(--acento);
  margin-bottom: var(--e1);
}
.entrada label {
  font: 600 var(--t-xs)/var(--lh-xs) var(--prosa);
  text-transform: uppercase; letter-spacing: var(--track-micro);
  color: var(--tinta-suave);
}
.entrada input {
  width: 100%;
  background: var(--hundida); color: var(--tinta);
  border: var(--filete) solid var(--borde); border-radius: var(--r-control);
  padding: var(--e3);
  font: 400 var(--t-md)/var(--lh-md) var(--prosa);
  outline: none;
}
.entrada .error {
  min-height: 1.2em;
  color: var(--mal);
  font-size: var(--t-sm); line-height: var(--lh-sm);
}
.entrada a { font-size: var(--t-sm); }


/* ===== FOCO (F16, portada) ===== */
/* ==================================================================================================
   FOCO — nada pierde su indicador.
   El anillo global vive en la base; aquí se corrige lo que ese anillo no puede saber solo:
     a) el radio — el anillo abraza la forma REAL de la pieza, no le impone la suya;
     b) el recorte — dentro de un contenedor con scroll (lista, popover, bloque de código) un
        anillo con offset positivo se corta o empuja el scroll: ahí va POR DENTRO;
     c) los campos sin outline propio: su indicador sube a 2px, porque 1px no es un anillo.
   El anillo va siempre en --acento, medido a ≥3:1 contra TODAS las superficies de ambos temas.
   ================================================================================================== */

button.primario:focus-visible, button.secundario:focus-visible, button.nueva:focus-visible,
.adjuntar:focus-visible, .adj-opcion:focus-visible,
.conv-abrir:focus-visible, .acciones-titulo:focus-visible, .sug:focus-visible,
.filtro:focus-visible, .tarjeta > summary:focus-visible, .cand-caps-titulo:focus-visible,
.buscar-conv:focus-visible, .inv-buscar:focus-visible, .inv-filtro:focus-visible,
.cand-entorno:focus-visible, .confirmar input:focus-visible,
.entrada input:focus-visible { border-radius: var(--r-control); }

.conv-btn:focus-visible, .turno-copiar:focus-visible,
.md-copiar:focus-visible, .conv-editar:focus-visible { border-radius: var(--r-menudo); }

.btn-lateral:focus-visible { border-radius: var(--r-control); }

/* b) anillo por dentro donde el de fuera se cortaría. */
.conversaciones .conv-abrir:focus-visible,
.conversaciones .conv-btn:focus-visible,
.conversaciones .conv-editar:focus-visible,
.sugerencias .sug:focus-visible,
.adj-menu .adj-opcion:focus-visible,
.md-bloque .md-copiar:focus-visible { outline-offset: -2px; }

/* c) campos con borde propio: el foco añade acento + halo. El campo de la frase de confirmación
      es el control más serio de la app —quien lo tiene enfocado está a punto de teclear una
      autorización— y no puede quedarse con un borde de 1px como único indicador. */
.confirmar input:focus-visible, .entrada input:focus-visible,
.buscar-conv:focus-visible, .inv-buscar:focus-visible, .conv-editar:focus-visible {
  border-color: var(--acento);
  box-shadow: var(--halo);
}
/* La paleta se abre con el campo ya enfocado; el subrayado de acento de 2px es su anillo. */
.paleta input:focus-visible {
  border-bottom-color: var(--acento);
  box-shadow: inset 0 -2px 0 var(--acento);
}

/* Lo que solo aparece al pasar el ratón tiene que aparecer también al llegar con Tab. */
.turno-copiar:focus-visible, .conv-btn:focus-visible { opacity: 1; }

/* Casillas de capacidad: el anillo nativo sigue siendo el indicador; la fila entera se ilumina
   para que se vea CUÁL de las doce está enfocada. Dos anillos concéntricos serían ruido. */
.cap:has(input:focus-visible) { background: var(--elevada); }


/* ===== MOVIMIENTO Y SU REDUCCIÓN (F16, portada) ===== */
/* ==================================================================================================
   MOVIMIENTO
   Cada animación contesta una sola pregunta: ¿qué le explica esto a quien mira? La que no la
   contesta, no está.

     1. Un turno NUEVO sube 8px con fundido (180 ms)  → «esto es nuevo, no estaba hace un segundo».
     2. Al pintar el hilo, las piezas se escalonan 30 ms, TOPE de 6 → «se lee en este orden». De la
        séptima en adelante entran ya puestas: escalonar cuarenta piezas no explica un orden, lo
        esconde detrás de una cortina que tarda un segundo en levantarse.
     3. Una tarjeta que se despliega crece con su ALTURA MEDIDA (200 ms) → «esto sale de aquí».
     4. El popover de «/» y la paleta escalan 0.98→1 con fundido (120 ms) → «esto viene de aquí».
     5. La barra lateral desliza con curva de SALIDA, no lineal → tiene inercia, es un objeto.
     6. La insignia de una acción que CAMBIÓ de estado da un pulso corto, una vez → «esto es lo
        que cambió». Un repintado seco esconde justo eso: la tarjeta se vuelve a dibujar idéntica
        salvo una palabra, y esa palabra es toda la noticia.

   LO QUE NO ESTÁ, Y POR QUÉ
     · Ningún rebote elástico: los dos puntos de control de --curva-salida tienen y ≤ 1. Esto
       administra servidores; un botón que rebota promete un juguete.
     · Ningún bucle salvo el latido del esqueleto, que solo existe mientras se espera y desaparece
       con el dato. Nada compite por la atención con lo que hay que leer.
     · Nada retrasa una respuesta: un turno nuevo entra con retraso CERO.
     · No se anima NI UN COLOR DE ESTADO. Ni la cadena, ni el carril, ni la insignia cruzan de un
       color a otro: un fotograma a medio camino entre --alerta y --bien es literalmente un estado
       que no existe, pintado como si existiera. Cambian de golpe; lo que se anima es la ATENCIÓN
       sobre el cambio (el pulso), no el cambio.

   RENDIMIENTO HONESTO — solo transform y opacity, con dos excepciones declaradas: la ALTURA del
   despliegue (que es el punto entero de la animación 3 y no se puede falsear con transform sin
   deformar el texto) y el margin-left de la barra en escritorio (la columna tiene que RECUPERAR
   el sitio; con transform la barra se iría y el hueco se quedaría).

   CLASES QUE PONE animacion.js Y NINGÚN MÓDULO EMITE: .nace, .ya-puesta, .pulso, .desplegando.
   Si ese fichero no se carga, todo esto degrada al escalonado por :nth-child y al plegado nativo.
   ================================================================================================== */

:root {
  /* Duraciones: son TIEMPOS, no espacio (por eso no salen de --e1..--e7). Cada una atada a lo que
     explica, no a un gusto. */
  --dur-pop: 120ms;         /* aparecer pegado a su origen: popover de «/» y paleta */
  --dur-entrada: 180ms;     /* un turno que llega */
  --dur-lateral: 180ms;     /* el mismo tiempo que un turno: la app tiene un solo pulso */
  --dur-despliegue: 200ms;  /* recorrer una altura medida pide algo más que aparecer */
  --dur-pulso: 320ms;       /* señalar un cambio: se tiene que ver entero y acabarse */
  --escalon: 30ms;          /* entre pieza y pieza al pintar el hilo */

  /* La curva de SALIDA: arranca a velocidad y frena al llegar. Imposible que rebote. */
  --curva-salida: cubic-bezier(.2, .8, .3, 1);

  /* Las dos magnitudes que la transformación mueve. En movimiento reducido se anulan abajo, así
     que ningún sitio tiene que acordarse de apagarlas por su cuenta. */
  --sube: 8px;   /* desde dónde entra un turno */
  --pop: .98;    /* desde qué escala nace un popover */
}

@keyframes entra-pieza {
  from { opacity: 0; transform: translateY(var(--sube)); }
  to   { opacity: 1; transform: translateY(0); }
}
/* La misma entrada sin transformación, para movimiento reducido. Es un fundido de LLEGADA: acaba
   en opacidad 1 y no atenúa nada que ya se estuviera leyendo. */
@keyframes entra-pieza-plana {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes nace-popover {
  from { opacity: 0; transform: scale(var(--pop)); }
  to   { opacity: 1; transform: scale(1); }
}
@keyframes nace-popover-plano {
  from { opacity: 0; }
  to   { opacity: 1; }
}
/* El pulso de la insignia se declara UNA vez, más abajo, junto al resto del movimiento.
   Aquí hubo una segunda declaración cuyo comentario prometía «sin pasarse de 1.06» mientras el
   navegador aplicaba la de abajo (1.12): dos @keyframes con el mismo nombre no conviven, gana el
   último, y el comentario del perdedor describía algo que no ocurría en ninguna pantalla. */

  100% { transform: scale(1); }
}

/* ---- 1 y 2 · la entrada de las piezas del hilo -------------------------------------------------
   `both` es obligatorio: sin él, una pieza con retraso se vería un instante YA PUESTA y luego
   saltaría atrás para empezar su entrada.
   El escalonado va por :nth-child y no por JavaScript a propósito: así funciona igual cuando
   animacion.js no está cargado. */
.tiempo > * { animation: entra-pieza var(--dur-entrada) var(--curva-salida) both; }
.tiempo > :nth-child(2) { animation-delay: calc(var(--escalon) * 1); }
.tiempo > :nth-child(3) { animation-delay: calc(var(--escalon) * 2); }
.tiempo > :nth-child(4) { animation-delay: calc(var(--escalon) * 3); }
.tiempo > :nth-child(5) { animation-delay: calc(var(--escalon) * 4); }
.tiempo > :nth-child(6) { animation-delay: calc(var(--escalon) * 5); }
.tiempo > :nth-child(n+7) { animation: none; }

/* Van DESPUÉS de las de :nth-child a propósito: misma especificidad (0,2,0), gana la última.
     .ya-puesta  pieza de un repintado en bloque más allá del tope → entra ya puesta.
     .nace       pieza que ha llegado sola a un hilo ya pintado → entra SIEMPRE, esté donde esté,
                 y sin retraso: una respuesta no espera a nadie. */
.tiempo > .ya-puesta { animation: none; }
.tiempo > .nace {
  animation: entra-pieza var(--dur-entrada) var(--curva-salida) both;
  animation-delay: 0s;
}

/* ---- 3 · el despliegue de una tarjeta ----------------------------------------------------------
   La clase la pone y la quita animacion.js alrededor de la transición. Aquí vive lo que es de la
   hoja: tiempo, curva, recorte mientras crece y el will-change ACOTADO — se va con la clase, que
   es lo que impide dejar una capa de composición viva en cada tarjeta de la pantalla. */
.tarjeta.desplegando {
  overflow: hidden;
  will-change: height;
  transition: height var(--dur-despliegue) var(--curva-salida);
}

/* ---- 4 · popover de «/» y paleta ---------------------------------------------------------------
   Los dos nacen al pasar de oculto a visible, así que basta con declarar la animación. No hay
   animación de SALIDA y es deliberado: cerrar tiene que ser instantáneo, o el siguiente gesto
   pelea con una caja que todavía se está yendo. */
.sugerencias:not([hidden]) {
  animation: nace-popover var(--dur-pop) var(--curva-salida) both;
  transform-origin: bottom center;   /* nace del borde del cajón, que es de donde sale de verdad */
}
.paleta[open] {
  animation: nace-popover var(--dur-pop) var(--curva-salida) both;
  transform-origin: center;          /* no cuelga de ningún control: nace de sí misma */
}
.paleta[open]::backdrop { animation: entra-pieza-plana var(--dur-pop) linear both; }
/* El menú del «+» nace igual que el popover de «/» y del mismo sitio: del borde del cajón. */
.adj-menu:not([hidden]) {
  animation: nace-popover var(--dur-pop) var(--curva-salida) both;
  transform-origin: bottom left;
}
/* El velo de soltar es un fundido de LLEGADA a secas (0→1): sin transformación, así que vale
   igual con movimiento reducido y no hace falta apagarlo abajo. */
.soltar:not([hidden]) { animation: entra-pieza-plana var(--dur-pop) linear both; }

/* ---- 6 · la insignia que cambió de estado ------------------------------------------------------
   La clase la pone animacion.js SOLO cuando el estado de esa acción es distinto del que enseñaba
   la última vez, y se la quita al acabar: es un aviso de una vez, no un adorno permanente. */
.insignia.pulso { animation: pulso-insignia var(--dur-pulso) var(--curva-salida) 1; }

/* ==================================================================================================
   MOVIMIENTO REDUCIDO — requisito, no extra.
   Se apaga TODO lo de transformación y quedan los fundidos de LLEGADA. Una excepción que no es
   estética sino MEDIDA: el pulso de la insignia NO se convierte en un parpadeo de opacidad. Se
   calculó qué contraste tiene la insignia compuesta con alfa sobre el fondo de la tarjeta y
   cualquier atenuación la saca de AA en el tema claro (a 0.90 de alfa ya baja de 4.5:1). Así que
   en movimiento reducido la insignia no se mueve y no se apaga — y no se pierde información,
   porque la información nunca estuvo en el pulso: está escrita en la propia insignia y en la
   cadena de peldaños.
   La regla general que sale de ese cálculo y vale para toda la hoja: un fundido de LLEGADA (0→1)
   es legítimo, porque no le quita legibilidad a nada que ya estuviera; una atenuación de lo que
   ya se lee (1→0.x) no lo es.
   ================================================================================================== */

@media (prefers-reduced-motion: reduce) {
  :root { --sube: 0px; --pop: 1; }

  .lateral { transition: none; }
  .principal { scroll-behavior: auto; }
  .conv-acciones, .turno-copiar, .cajon-linea,
  button.primario, button.secundario { transition: none; }

  .tiempo > *, .tiempo > .nace { animation-name: entra-pieza-plana; }
  .sugerencias:not([hidden]), .paleta[open],
  .adj-menu:not([hidden]) { animation-name: nace-popover-plano; }
  .adjuntar { transition: none; }
  .tarjeta.desplegando { transition: none; }
  .insignia.pulso { animation: none; }

  /* Los selectores del esqueleto repiten la especificidad de los de arriba a propósito: con
     `.cargando::after` a secas, la regla de `.conversaciones > .cargando::after` (más específica)
     seguiría ganando y el latido no se apagaría. Un «respeto» a reduced-motion que pierde en la
     cascada es peor que no haberlo escrito, porque parece cumplido. */
  .esqueleto,
  .conversaciones > .cargando::after,
  .principal > .cargando::after { animation: none; opacity: 1; }
}


/* ===== F16 P2B — VISTAS VESTIDAS (portada) ===== */
/* ==================================================================================================
   F16 P2B — LAS VISTAS NUEVAS, VESTIDAS
   --------------------------------------------------------------------------------------------------
   Este bloque va AL FINAL a propósito: P1 dejó la identidad puesta y aquí se termina de vestir lo
   que P1 dibujó a grandes rasgos —la vista de Acciones, el panel de inventario y el markdown del
   agente— sin volver a escribir la hoja. Cuando una regla de abajo pisa una de arriba se dice
   dónde y por qué; son cuatro, están todas marcadas «PISA:» y se listaron en los riesgos de la
   fase, porque una hoja que se contradice consigo misma a 400 líneas de distancia es una trampa
   para el siguiente.

   LO QUE ESTE BLOQUE AÑADE Y NO ESTABA
     1. Los ESTADOS NOMBRADOS del inventario (.est-*) dejan de ser seis palabras con dos colores y
        pasan a tener seis marcas distinguibles — SIN inventarles color semántico. Ver el cuadro.
     2. El <summary> de capacidades recupera un indicio de que se pliega: se le quitó el marcador
        nativo y no se puso nada en su sitio.
     3. El markdown: ligaduras APAGADAS en todo lo mono (razón de seguridad, no de gusto),
        subrayado en los enlaces (el color no puede ser la única señal) y scroll contenido.
     4. «Trabajando» sube al nivel de «mal»: carril propio, punteado, con el latido que ya usan los
        esqueletos. Provisional a la vista, sin fingir resultado.
     5. Densidad y objetivos táctiles de lo que P1 no llegó a tocar en el teléfono.

   CONTRASTES, CALCULADOS (sRGB, fórmula WCAG 2.x) sobre --elevada, que es el fondo de las fichas
   y las tarjetas — claro / oscuro:
     --tinta 17.98 / 13.92 · --tinta-suave 7.76 / 7.47 · --tinta-apagada 5.79 / 5.34
     --alerta 6.92 / 8.65 · --desconocido 7.60 / 7.81 · --acento 6.55 / 7.38
     --borde 4.10 / 3.56  ← el único que no es texto: es carril y borde informativo, mínimo 3:1
   Y sobre --superficie (el panel, fondo del markdown y de la vista):
     --acento 6.16 / 8.03 (enlaces) · --tinta-apagada 5.45 / 5.81 (marcadores de lista)
   Sobre --hundida (código): --tinta 14.47 / 16.67.
   Ninguna pieza de este bloque baja la opacidad de un texto que se esté leyendo.
   ================================================================================================== */

/* -- iconos nuevos, dibujados aquí y embebidos (misma técnica que P1: máscara, no imagen, para que
      el color lo ponga currentColor y no haya dos copias por tema) ------------------------------- */
:root {
  /* Galón de despliegue. Apunta a la derecha cerrado y gira 90° abierto: una sola forma para los
     dos estados, que es como se lee «esto se abre» sin dos iconos que puedan desincronizarse. */
  --mask-galon: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'><path d='M9.5 5 16.5 12 9.5 19'/></svg>");

  /* Tablilla EN BLANCO, con las líneas punteadas: el vacío de la vista de Acciones dice «nada
     espera tu frase», y una tablilla sin nada escrito es exactamente eso. La burbuja de --mask-vacio
     («aquí se escribe») es del hilo y allí se queda; aquí significaría otra cosa. */
  --mask-vacio-acciones: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'><rect x='4' y='4.6' width='16' height='16.4' rx='3'/><rect x='8.2' y='2.6' width='7.6' height='4' rx='1.6'/><path d='M8.4 12.4h7.2' stroke-dasharray='1.7 3.4'/><path d='M8.4 16.4h4.6' stroke-dasharray='1.7 3.4'/></svg>");

  /* La equis de los campos de búsqueda. AQUÍ una equis sí es «borrar»: es un control que borra lo
     tecleado. (En .aviso.mal no lo era, y por eso allí hay una exclamación.) */
  --mask-borrar: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round'><path d='M6.5 6.5 17.5 17.5'/><path d='M17.5 6.5 6.5 17.5'/></svg>");

  /* EL CARRIL PUNTEADO — trazo de --e1, hueco de --e1 — significa una cosa y solo una en toda la
     app: SIN RESOLVER. Se usa en «trabajando», en «detectado» y en «apartado», que son los tres
     sitios donde el registro todavía no ha cerrado nada.

     Y SE ESCRIBE ENTERO EN CADA SITIO, aunque duela repetirlo tres veces. La versión bonita
     —guardar la trama en un token `--trama-punteada: repeating-linear-gradient(… var(--marca) …)`
     y que cada ficha pusiera su --marca— NO FUNCIONA, y no falla de forma ruidosa sino pintando
     un color equivocado sin avisar: el valor computado de una propiedad personalizada sustituye
     sus `var()` DONDE SE DECLARA, no donde se usa. Declarada en :root, `var(--marca)` se congela
     ahí con el gris por defecto y baja ya resuelto a todos los descendientes; la ficha puede
     poner el --marca que quiera, que llega tarde.
     Cazado ejecutando: la ficha «apartado» pedía --tinta-apagada (#5e6673) y se pintaba con
     --borde (#767e8b), que era el valor que :root había horneado dentro del token. Si alguien
     vuelve a factorizar esto en una variable compartida, vuelve el mismo fallo mudo. */
}

/* ==================================================================================================
   1 · «TRABAJANDO» — al nivel de «mal», sin prometer resultado
   P1 le dio caja de trazo discontinuo y nada más, mientras «mal» llevaba carril e icono. Un aviso
   sin carril, rodeado de piezas que sí lo tienen, se lee como una pieza sin estado — y esperar SÍ
   es un estado. Lleva el carril punteado (sin resolver) y el latido que ya existe para los
   esqueletos: es el único bucle admitido de la hoja, porque solo vive mientras se espera y
   desaparece con el dato. Ni un spinner: un spinner gira igual cuando la red ya murió.
   ================================================================================================== */

/* El canal ya existe: `.aviso` trae `padding: var(--e3) var(--e4)`, así que el carril de 3px cae
   en los 16px de la izquierda sin tocar la primera letra. */
.aviso.trabajando::after {
  content: '';
  position: absolute; left: 0; top: var(--e2); bottom: var(--e2);
  width: var(--carril);
  border-radius: 0 var(--r-chip) var(--r-chip) 0;
  /* --borde: 4.10:1 claro / 3.56:1 oscuro sobre --elevada. Es marca de estado, mínimo 3:1. */
  background: repeating-linear-gradient(to bottom,
    var(--borde) 0 var(--e1), transparent var(--e1) calc(var(--e1) * 2));
  animation: esqueleto-latido 1.8s ease-in-out infinite;
}

/* ==================================================================================================
   2 · LA VISTA DE ACCIONES (vistaAcciones.js)
   ================================================================================================== */

/* La firma de la casa arriba del título. Es el mismo gesto de `.marca::before` y de `.entrada::before`
   —una raya de acento de --e5 × --carril— y es lo que ata las pantallas entre sí: se entra por la
   misma marca por la que se abre la app. Único uso de acento aquí; el resto de la cabecera es gris. */
.vista-titulos { display: block; }
.vista-titulos::before {
  content: '';
  display: block;
  width: var(--e5); height: var(--carril);
  border-radius: var(--r-chip);
  background: var(--acento);
  margin-bottom: var(--e3);
}

/* PISA: `.vista-cuerpo { gap: var(--e3) }` (sección VISTA DE ACCIONES).
   12px entre tarjetas de 16px de padding las apelmaza: se leen como filas de una tabla y no como
   piezas separadas. --e4 es además el gap del hilo, así que la app respira a un solo ritmo en las
   dos pantallas que enseñan tarjetas. */
.vista-cuerpo { gap: var(--e4); }

/* El vacío de esta vista cambia de icono (ver --mask-vacio-acciones). Redefinir la variable EN EL
   ELEMENTO basta: el ::before de P1 la lee de ahí, así que no hay que repetir ni una línea de la
   maquinaria de máscaras. */
.vista-cuerpo > .vacio { --mask-vacio: var(--mask-vacio-acciones); }

/* -- los filtros: control segmentado de verdad --------------------------------------------------
   P1 dejó el contenedor hundido y la píldora activa elevada, que es la forma correcta. Falta lo
   que hace que no baile: la cuenta con ancho reservado y alineada a la derecha, para que pasar de
   9 a 10 no ensanche la píldora y mueva a las otras tres. El :empty de P1 sigue mandando — sin
   lista fiable no se reserva sitio para un número que nadie ha comprobado. */
.filtro-cuenta {
  min-width: 2ch;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
/* La píldora activa se levanta también con un filete, no solo con la sombra: en el tema oscuro
   --sombra-1 es `none` a propósito (allí la elevación la dan luminancia y borde), así que sin esta
   línea el filtro activo se distinguía únicamente por el peso de la letra — la misma razón por la
   que P1 obliga a declarar SIEMPRE borde y sombra juntos en todo lo que flota.
   El filete lo llevan los CUATRO, transparente en los inactivos: si solo lo tuviera el activo,
   cambiar de filtro ensancharía una píldora 2px y desplazaría a las otras tres. */
/* PISA: `.filtro { border: 0 }` de más arriba. El borde transparente reserva el sitio para
   que la marca del activo no desplace a los demás. */
.filtro { border: var(--filete) solid transparent; }
.filtro.activo { border-color: var(--linea); }

/* ==================================================================================================
   3 · EL PANEL DE INVENTARIO (inventario.js)
   --------------------------------------------------------------------------------------------------
   LOS SEIS ESTADOS NOMBRADOS, Y POR QUÉ SE DISTINGUEN ASÍ
   El encargo pide que cada .est-* tenga marca clara y distinta. La tentación es darle un color a
   cada uno; es exactamente lo que no se puede hacer: «Gestionado» en verde diría «esto está bien»
   y gestionado NO significa comprobado — lo dice el propio inventario.js. Seis colores en un
   carril de 3px tampoco sobreviven a un ojo real.
   Así que la distinción se reparte en tres señales que sí se leen, y ninguna promete nada:

     estado         carril de la ficha            marca de la insignia      color
     -------------- ----------------------------- ------------------------- -----------------
     detectado      3px PUNTEADO  --borde         corta (.6em)              --tinta-suave
     revisión       3px sólido    --alerta        entera                    --alerta
     incorporado    3px sólido    --borde         entera                    --tinta-suave
     gestionado     3px sólido    --tinta-suave   entera                    --tinta  (plena)
     apartado       2px PUNTEADO  --tinta-apagada punteada                  --tinta-apagada
     desconocido    3px sólido    --desconocido   entera                    --desconocido

   Se lee: punteado = SIN RESOLVER · sólido = resuelto · más tinta = más decisión tuya · 2px =
   fuera del recorrido. El violeta de «desconocido» ni promete ni acusa, y es el único color que
   no comparte familia con nada más de la hoja. Y encima de todo esto sigue estando la PALABRA,
   que es la que de verdad informa: el color nunca va solo.
   ================================================================================================== */

/* PISA (amplía): `.cand[data-estado=…]` de la sección INVENTARIO, que solo conocía dos estados.
   Los otros cuatro caían al gris por defecto de :root y salían idénticos entre sí. */
.cand[data-estado="est-detectado"]   { --marca: var(--borde); }
.cand[data-estado="est-incorporado"] { --marca: var(--borde); }
.cand[data-estado="est-gestionado"]  { --marca: var(--tinta-suave); }
.cand[data-estado="est-apartado"]    { --marca: var(--tinta-apagada); }

/* Punteado = sin resolver. No se toca el `background: var(--marca)` de P1 para los sólidos: solo
   se sustituye en los dos estados que lo necesitan, y con el color ESCRITO (ver el aviso del
   bloque de tokens: una trama compartida en :root se lleva el gris de :root a todas partes). */
.cand[data-estado="est-detectado"]::after {
  background: repeating-linear-gradient(to bottom,
    var(--borde) 0 var(--e1), transparent var(--e1) calc(var(--e1) * 2));
}
/* Apartado además adelgaza: menos presencia para lo que está fuera del recorrido. No se apaga con
   opacidad —eso rebajaría su contraste medido—, se estrecha. --tinta-apagada: 5.79:1 / 5.34:1. */
.cand[data-estado="est-apartado"]::after {
  width: var(--carril-fino);
  background: repeating-linear-gradient(to bottom,
    var(--tinta-apagada) 0 var(--e1), transparent var(--e1) calc(var(--e1) * 2));
}

/* PISA: `.insignia.est-gestionado { color: var(--tinta-suave) }`.
   Gestionado es el único de los seis que exige una decisión explícita de la persona («marcado como
   gestionado por ti»), y por eso va en tinta plena. Sigue sin ser verde: más peso, no aprobación. */
.insignia.est-gestionado { color: var(--tinta); }

/* Marca corta = va por el primer peldaño. Es la misma idea de `.peldano` de las tarjetas: cuánto
   se ha recorrido se dice con la forma, no con el color. */
.insignia.est-detectado::after { height: .6em; }
/* Marca punteada = apartado, fuera del recorrido. La trama va en currentColor y no en --marca
   porque la insignia se pinta siempre con su propio color. */
.insignia.est-apartado::after {
  background: repeating-linear-gradient(to bottom,
    currentColor 0 var(--carril-fino), transparent var(--carril-fino) calc(var(--carril-fino) * 2));
}

/* -- la caja de la regla ------------------------------------------------------------------------
   Los tres pasos son la espina del panel entero: detectar ≠ autorizar ≠ poder hacer. Sus números
   llevan peso y tinta plena porque son la estructura del argumento, no una viñeta decorativa. */
.regla-pasos > li::marker {
  color: var(--tinta);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* -- controles del inventario -------------------------------------------------------------------
   La equis nativa de <input type="search"> es un blob gris que ignora el tema: en oscuro es la
   pieza más clara de la barra. Se reemplaza por la nuestra, con el color ya medido del texto
   apagado. Se estilan los DOS buscadores de la app, porque dejar uno con la equis del navegador y
   el otro con la nuestra sería peor que no tocar ninguno. */
.inv-buscar::-webkit-search-cancel-button,
.buscar-conv::-webkit-search-cancel-button {
  -webkit-appearance: none; appearance: none;
  width: var(--e3); height: var(--e3);
  cursor: pointer;
  background-color: var(--tinta-apagada);
  -webkit-mask-image: var(--mask-borrar);  mask-image: var(--mask-borrar);
  -webkit-mask-repeat: no-repeat;          mask-repeat: no-repeat;
  -webkit-mask-size: 100% 100%;            mask-size: 100% 100%;
}

/* -- el desplegable de capacidades ---------------------------------------------------------------
   P1 quitó el marcador nativo del <summary> y no puso nada en su sitio: «Qué le dejas hacer a
   Hermes» era un texto azul que no anunciaba que se abriera. El galón lo dice, y gira al abrirse
   para que el estado del pliegue se vea sin leer. */
.cand-caps-titulo {
  display: flex; align-items: center; gap: var(--e2);
}
.cand-caps-titulo::before {
  content: '';
  flex: 0 0 auto;
  width: .8em; height: .8em;
  background-color: currentColor;          /* = --acento, 6.55:1 claro / 7.38:1 oscuro sobre --elevada */
  -webkit-mask-image: var(--mask-galon);   mask-image: var(--mask-galon);
  -webkit-mask-repeat: no-repeat;          mask-repeat: no-repeat;
  -webkit-mask-size: 100% 100%;            mask-size: 100% 100%;
  transition: transform var(--dur-pop) var(--curva-salida);
}
.cand-caps[open] > .cand-caps-titulo::before { transform: rotate(90deg); }

/* -- las casillas de capacidad ------------------------------------------------------------------
   Una casilla nativa mide 13px. Es el control que CONCEDE PERMISOS y se marca con el dedo tanto
   como con el ratón; fallar una concede lo que no era. Se sube a --e5 y se alinea con la primera
   línea del texto en vez de con la caja, que es donde el ojo la busca. */
.cap > input[type="checkbox"] {
  flex: 0 0 auto;
  width: var(--e5); height: var(--e5);
  margin: 0;
  /* Centrada respecto a la línea de --t-sm del código, no respecto al bloque entero: una casilla
     que flota a media altura de un texto de tres líneas parece de otro sitio. */
  align-self: flex-start;
  margin-top: calc((var(--t-sm) * var(--lh-sm) - var(--e5)) / 2);
}
/* El grupo «Preparar» y el grupo «Leer» no se tiñen: leer y preparar son ambos inocuos hasta que
   alguien teclea una frase. Lo que sí se marca es el título del grupo contra su explicación. */
.cap-grupo-titulo { letter-spacing: -.01em; }

/* ==================================================================================================
   4 · MARKDOWN (markdown.js) — lo que más se mira
   ================================================================================================== */

/* LIGADURAS APAGADAS EN TODO LO MONO. No es gusto tipográfico: es la misma regla del proyecto.
   Las fuentes de código modernas (SF Mono, JetBrains Mono, Cascadia) componen `!=` como `≠`,
   `->` como `→` y `==` como `⩵`. Aquí se COPIAN comandos y se TECLEA la frase de confirmación
   carácter a carácter: un glifo que no es el carácter que hay debajo es una mentira tipográfica,
   y en esta app eso significa un comando mal copiado o una frase que no valida. Alcanza a TODO lo
   mono, no solo al markdown. */
.mono, code, pre, kbd,
.md-codigo, .md-bloque pre, .md-bloque code,
.cap-codigo, .sug-nombre, .tarjeta .crudo,
.confirmar input, .frase-esperada code {
  font-variant-ligatures: none;
  font-feature-settings: 'liga' 0, 'clig' 0, 'calt' 0;
}

/* La tabulación de un bloque pegado desde un YAML o un Dockerfile: 8 columnas por defecto parten
   la línea y obligan a scroll lateral desde el primer nivel de sangría. */
.md-bloque pre { tab-size: 2; -moz-tab-size: 2; }

/* El scroll del bloque se queda EN el bloque. Sin esto, llegar al final de un comando largo
   arrastra la línea de tiempo entera hacia el lado en trackpad y en táctil. */
.md-bloque pre, .tarjeta .crudo { overscroll-behavior-x: contain; }

/* Los enlaces del agente llevaban SOLO color. El color no puede ser la única señal —vale para
   quien no distingue el azul del gris, y vale para cualquiera sobre --superficie, donde --acento
   está a 6.16:1 pero un ojo distraído no separa «enlace» de «negrita»—. Subrayado fino, separado
   de las descendentes, y más grueso al pasar por encima. */
.md-enlace {
  text-decoration: underline;
  text-decoration-thickness: from-font;
  text-underline-offset: 2px;
}
.md-enlace:hover { text-decoration-thickness: 2px; }

/* Los marcadores de lista son andamiaje, no contenido: van apagados (5.45:1 claro / 5.81:1 oscuro
   sobre --superficie, AA de sobra) y con cifras tabulares, porque markdown.js respeta la
   numeración de origen y una lista puede empezar en 8 y llegar a 12. */
.md-lista > li::marker {
  color: var(--tinta-apagada);
  font-variant-numeric: tabular-nums;
}

/* Una cita que contiene un párrafo heredaba el margen del párrafo DENTRO de su propio padding y
   se descolgaba de la raya del margen. */
.md-cita > :first-child { margin-top: 0; }
.md-cita > :last-child  { margin-bottom: 0; }

/* La cabecera del bloque no se desploma cuando el mensaje no declaró lenguaje (.md-lang:empty se
   oculta y solo queda el botón): altura fija de control pequeño, y el botón centrado en ella. */
.md-bloque-cabecera { min-height: var(--alto-control); }

/* Dos bloques seguidos (salida de un comando y su explicación en otro bloque) no se pegan. */
.md-bloque + .md-bloque { margin-top: var(--e4); }

/* ==================================================================================================
   5 · FOCO — lo que quedó fuera de la ronda de P1
   ================================================================================================== */

/* El galón y el texto del <summary> comparten anillo: el anillo abraza la fila entera, que es la
   zona pulsable de verdad. */
.cand-caps-titulo:focus-visible { outline-offset: 2px; }

/* Enlaces del agente y salida de emergencia: el anillo global los alcanza, pero un enlace que
   parte en dos líneas necesita que el anillo siga la forma real de cada trozo. */
.md-enlace:focus-visible, .escape a:focus-visible, .entrada a:focus-visible {
  border-radius: var(--r-menudo);
  outline-offset: 1px;
}

/* La casilla de capacidad ya ilumina su fila (P1, `.cap:has(...)`). Aquí solo se garantiza que el
   anillo nativo no se recorte contra el borde del grupo. */
.cap > input[type="checkbox"]:focus-visible { outline-offset: 1px; }

/* ==================================================================================================
   6 · MÓVIL — densidad y dedo
   P1 cubrió la barra, el hilo y los botones. Falta lo que solo existe en estas dos vistas.
   ================================================================================================== */

@media (max-width: 760px) {
  /* El control segmentado a dos columnas: cuatro píldoras del mismo tamaño se pulsan sin mirar,
     que es justo lo que no pasaba con cuatro anchos distintos envolviendo a media línea. */
  .filtros {
    display: grid; grid-template-columns: 1fr 1fr;
    width: 100%; align-self: stretch;
  }
  .filtro { justify-content: center; text-align: center; }

  /* Filtro y buscador del inventario a línea completa: a 375px compartir fila deja el buscador en
     100px, que no admite ni «postgres». */
  .inv-filtro, .inv-buscar { flex: 1 1 100%; min-width: 0; }

  /* Densidad: --e4 de padding por los cuatro lados en una pantalla de 375px se come el 9 % del
     ancho por lado. El aire vertical se conserva; el horizontal cede, que es el que escasea. */
  .cand, .regla, .tarjeta { padding: var(--e4) var(--e3); }

  /* La confianza deja de colgarse a la derecha (margin-left:auto) y baja a su propia línea: en una
     cabecera que ya envuelve, un dato empujado al margen acaba solo en una línea huérfana. */
  .cand-conf { margin-left: 0; flex-basis: 100%; }

  /* La equis de borrar, a tamaño de dedo. */
  .inv-buscar::-webkit-search-cancel-button,
  .buscar-conv::-webkit-search-cancel-button { width: var(--e5); height: var(--e5); }

  /* El bloque de código gana columnas por DENTRO, no saliéndose del margen: sacarlo a sangre lo
     metía en el canal de la espina (y dentro de la burbuja de la persona, casi contra su borde),
     y la espina es la estructura de la pantalla, no un margen negociable. Bajar el padding de
     --e3 a --e2 devuelve 8px de código sin tocar la retícula. */
  .md-bloque pre { padding: var(--e2); }
}

/* ==================================================================================================
   7 · MOVIMIENTO REDUCIDO — lo que añade este bloque
   El @media de P1 está más arriba y no puede conocer estos selectores. Se apaga aquí lo único que
   se mueve de los añadidos: el giro del galón y el latido del carril de «trabajando». En los dos
   casos la información NO estaba en el movimiento —el galón conserva su rotación final y el
   carril su trama punteada—, así que no se pierde nada al detenerlos.
   ================================================================================================== */

@media (prefers-reduced-motion: reduce) {
  .cand-caps-titulo::before { transition: none; }
  /* opacity: 1 explícito: sin él, el latido puede dejar el carril congelado a media atenuación. */
  .aviso.trabajando::after { animation: none; opacity: 1; }
}


/* ===== F16 P2A — CONTRATO CON MOTION (portada) ===== */
/* ==================================================================================================
   F16 P2A — MOVIMIENTO CON MOTION: lo que la hoja tiene que CEDER, y lo que sigue siendo suyo.
   --------------------------------------------------------------------------------------------------
   Todo lo de aquí abajo cuelga de una sola clase, `con-motion`, que animacion.js pone en <html>
   cuando —y solo cuando— se cumplen las dos condiciones a la vez:

       window.Motion existe   Y   prefers-reduced-motion NO es «reduce»

   Sin esas dos, la clase no está y NINGUNA de estas reglas se aplica: el movimiento de P1 vuelve a
   ser el único que hay, intacto, y la app se ve y se usa exactamente igual. Esa es la razón de que
   el bloque entero esté escrito como una CESIÓN condicional en vez de como una reescritura — que
   el motor de terceros no cargue, o que alguien pida menos movimiento, no puede dejar esta
   interfaz peor de lo que P1 la dejó.

   La clase se sincroniza EN VIVO con matchMedia: activar «reducir movimiento» a mitad de sesión
   devuelve el mando a esta hoja en el acto, sin recargar.

   POR QUÉ HAY QUE APAGAR LO DE P1 Y NO BASTA CON SUPERPONER: dos animaciones sobre la misma
   propiedad se pelean, y la que gana no es la que se eligió sino la que dice la cascada. Una pieza
   entrando dos veces —una por keyframes y otra por Motion— es un parpadeo, y un parpadeo sobre una
   tarjeta de acción es ruido justo donde hace más daño.

   LO QUE **NO** CEDE LA HOJA, y por qué:
     · El fondo del diálogo (`.paleta[open]::backdrop`). Es un fundido plano de llegada y los
       pseudo-elementos no son terreno de una librería de DOM. Se queda donde está.
     · El latido del esqueleto de carga. Es un bucle, y un bucle solo se justifica mientras se
       espera un dato; no lo conduce nadie más.
     · TODO el color. Ni una regla de aquí abajo toca un color, un borde ni un contraste: el
       movimiento no puede cambiar lo que una pieza AFIRMA, solo llamar la atención sobre ella.
       Los contrastes medidos en P1 siguen siendo los de P1, exactos.
   ================================================================================================== */

/* ---- 1 y 2 · la entrada de las piezas del hilo -------------------------------------------------
   Cede el escalonado por :nth-child entero. Motion escalona con `delay` por elemento (28 ms, seis
   como mucho) y las piezas que llegan en vivo entran con retraso CERO, esté donde esté.
   Los tres selectores repiten los de arriba a propósito: `.tiempo > .nace` es (0,2,0) igual que
   `.tiempo > *`, así que sin nombrarlo el apagado no lo alcanzaría por orden pero sí quedaría
   atado a él — y un apagado que depende del orden de dos reglas iguales se rompe el día que
   alguien mueva un bloque. Nombrados, ganan por especificidad y no por suerte. */
.con-motion .tiempo > *,
.con-motion .tiempo > .nace,
.con-motion .tiempo > .ya-puesta { animation: none; }

/* ---- 3 · el despliegue de una tarjeta ----------------------------------------------------------
   La clase `.desplegando` sigue siendo de la hoja para lo que es suyo —recorte mientras crece y el
   will-change acotado, que se va con la clase— y cede SOLO la transición. Tiene que ceder: en la
   cascada una transición en curso gana a una animación, así que dejarla puesta no sería una
   redundancia inofensiva, sería el resorte pintado por encima de una bézier. */
.con-motion .tarjeta.desplegando { transition: none; }

/* ---- 4 · popover de «/» y paleta ---------------------------------------------------------------
   Ceden la animación de entrada y, con ella, el `transform-origin`: animacion.js lo calcula para
   el popover de «/» (la columna del cursor en el cajón, que es de donde sale de verdad) y lo deja
   en manos de esta hoja para la paleta, porque a la paleta la invoca ⌘K y un atajo de teclado no
   tiene punto en la pantalla. No se le inventa un origen que no existe.
   El `::backdrop` NO cede: sigue siendo el fundido plano de arriba. */
.con-motion .sugerencias:not([hidden]),
.con-motion .paleta[open] { animation: none; }

/* ---- 5 · la barra lateral ----------------------------------------------------------------------
   Cede la transición ENTERA, las dos propiedades: el margen (o el transform en móvil) porque lo
   conduce el resorte, y `visibility` porque su tiempo lo marca ahora la animación y no un plazo
   fijo — con la transición puesta, la barra seguía visible 180 ms después de haber terminado de
   irse, y una barra fuera de pantalla que todavía es `visible` sigue siendo alcanzable con el
   tabulador. Eso no es un adorno de más: es un fallo de accesibilidad.

   `animando` la pone y la quita animacion.js alrededor del resorte, y hace UNA sola cosa: mantener
   la barra visible mientras se va. Sin ella, `visibility: hidden` entra en el primer fotograma y
   el recorrido se corta en seco. */
.con-motion .lateral { transition: none; }
.con-motion .lateral.oculta.animando { visibility: visible; }

/* ---- 6 · la insignia que cambió de estado ------------------------------------------------------
   El pulso sube de 1.06 a 1.12 por decisión del operador en P2A. Se redefinen los keyframes en vez
   de añadir una regla `.con-motion` porque el pulso tiene que ser EL MISMO por las dos vías: es
   una frase —«esto de aquí es lo que cambió»— y una frase no se dice más alto según qué ficheros
   hayan cargado. Un mismo nombre de @keyframes declarado dos veces se queda con el último, así que
   esta sustituye a la de arriba para todo el documento.
   Sigue sin pasarse: 1.12 se ve sin discusión y no llega a leerse como un juguete. Y sigue sin
   tocar ni un color — el color YA dice el estado nuevo, y cruzarlo desde el viejo pintaría medio
   segundo de un estado que no existe. */
@keyframes pulso-insignia {
  0%   { transform: scale(1); }
  30%  { transform: scale(1.12); }
  100% { transform: scale(1); }
}

/* ---- 7 · la vista de acciones, al entrar en pantalla -------------------------------------------
   No hay nada que ceder aquí (P1 no animaba esta vista), pero sí un seguro que poner.
   animacion.js atenúa a 0 las tarjetas que están FUERA de la pantalla y las destapa al llegar a
   ellas. Al imprimir no hay pantalla, no hay desplazamiento y no hay observador que se dispare:
   sin esta regla, un historial impreso saldría con las tarjetas de más abajo en blanco. Que un
   registro de acciones se imprima incompleto y con aspecto de estar completo es exactamente el
   fallo que este proyecto no se permite, así que va con !important — es el único modo de que una
   hoja gane a un estilo en línea, y aquí ganar es obligatorio. */
@media print {
  .vista-cuerpo > * { opacity: 1 !important; transform: none !important; }
}

/* ---- movimiento reducido ------------------------------------------------------------------------
   No hay bloque nuevo, y esa ausencia es la decisión: bajo `prefers-reduced-motion: reduce` la
   clase `con-motion` NO se pone, así que ninguna regla de esta sección existe, animacion.js no
   anima nada y manda entero el @media de P1 — transformaciones apagadas, fundidos de llegada
   intactos. Un solo interruptor y un solo sitio donde se apaga todo; dos habrían acabado
   discrepando, y el que se olvidara sería el que se ve.
   ================================================================================================== */

/* ---- adjuntos en el cajón (F17) --------------------------------------------------------------
   Fichas que esperan mientras escribes, como en las apps actuales. Antes la subida se contaba en
   turnos del hilo y el archivo se mandaba solo: el hilo no es un registro de subidas, y un
   adjunto que ya voló no puede acompañar al mensaje que aún no has escrito. */
.adj-cola {
  display: flex; flex-wrap: wrap; gap: var(--e2);
  max-width: var(--ancho-prosa, 820px); margin: 0 auto var(--e2);
}
.adj-cola[hidden] { display: none; }
.adj-ficha {
  position: relative; display: flex; align-items: center; gap: var(--e2);
  background: var(--elevada); border: var(--filete, 1px) solid var(--borde);
  border-radius: var(--r-control, 10px); padding: var(--e2) var(--e3);
  max-width: 320px; overflow: hidden;
}
/* El carril del margen, como el resto del sistema: el estado se marca, no se tiñe la ficha. */
.adj-ficha::after {
  content: ''; position: absolute; left: 0; top: var(--e1); bottom: var(--e1);
  width: 3px; border-radius: 0 3px 3px 0; background: var(--tinta-apagada);
}
.adj-ficha.subiendo::after { background: var(--acento); }
.adj-ficha.con-error::after { background: var(--mal); }
.adj-ficha-cuerpo { display: flex; flex-direction: column; min-width: 0; padding-left: var(--e1); }
.adj-nombre {
  font-size: var(--t-sm, 13px); font-weight: 600; color: var(--tinta);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.adj-sub {
  font-size: var(--t-xs, 11.5px); color: var(--tinta-suave);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.adj-ficha.con-error .adj-sub { color: var(--mal); white-space: normal; }
/* «Se ve rara» es de la RUTA, no del estado de la subida: una ruta sospechosa que subió bien
   sigue siendo sospechosa, y se dice en su propio sitio. */
.adj-rara { font-size: var(--t-xs, 11.5px); color: var(--alerta); white-space: normal; }
.adj-quitar {
  flex: 0 0 auto; border: 0; background: transparent; color: var(--tinta-suave);
  cursor: pointer; font-size: 13px; line-height: 1; padding: var(--e1) var(--e2);
  border-radius: var(--r-menudo, 6px); min-width: 28px; min-height: 28px;
}
.adj-quitar:hover { background: var(--hundida); color: var(--tinta); }


/* ===== RUEDA DE PROGRESO (F17, portada) ===== */
/* ---- rueda de progreso de una subida (F17) — misma pieza que en /ui/ ------------------------ */
.adj-anillo { width: 18px; height: 18px; flex: 0 0 auto; }
.adj-anillo-pista,
.adj-anillo-arco { fill: none; stroke-width: 2; }
.adj-anillo-pista { stroke: var(--linea, rgba(127,127,127,.28)); }
.adj-anillo-arco {
  stroke: var(--acento, #4f8cff);
  stroke-linecap: round;
  transform: rotate(-90deg);
  transform-origin: 50% 50%;
  transition: stroke-dashoffset .18s linear;
}
.adj-anillo[data-indeterminado] { animation: adj-girar 1s linear infinite; }
.adj-anillo[data-indeterminado] .adj-anillo-arco { transition: none; }
@keyframes adj-girar { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .adj-anillo[data-indeterminado] { animation: none; }
  .adj-anillo-arco { transition: none; }
}


/* ==================================================================================================/* ==================================================================================================
   F18 — EL ARMAZÓN, MOBILE FIRST.
   ==================================================================================================

   POR QUÉ SE REESCRIBIÓ ESTA CAPA. El operador lo dijo exacto: «que no se vea como una adaptación
   mal hecha a móvil». Y no era una impresión — era la estructura. La versión anterior era
   DESKTOP-FIRST: la base describía el escritorio y un `@media (max-width: 760px)` al final
   deshacía cosas para el teléfono. Eso produce dos defectos que ninguna revisión ve y el dedo sí:

     · lo que se olvida corregir se queda con el valor de escritorio, y en un teléfono eso es
       siempre lo apretado (una lista de 34px de alto, letra de 11.5, botones de 24);
     · las reglas del teléfono viven al final, lejos de la pieza, así que quien edita el
       componente no ve que existen y las contradice sin enterarse. Pasó: el riel se apagaba en
       una regla que perdía por especificidad contra la que lo enseñaba, cinco secciones arriba.

   AHORA la base ES el teléfono. `@media (min-width: 48em)` AÑADE lo que una pantalla grande
   permite: el eje ancho, la barra fija, la densidad. Nada se deshace; todo se suma. Si mañana se
   olvida una pieza en la capa grande, el resultado en escritorio es una app cómoda de más, no un
   teléfono ilegible — el error cae del lado bueno, que es la regla de esta casa.

   LA FRONTERA está en `em` y no en `px` a propósito: 48em respeta el tamaño de letra que la
   persona haya elegido en su sistema. Quien lee a 20px pasa a la versión ancha más tarde, cuando
   de verdad le cabe.

   LO QUE NO CAMBIA: los tokens, los contrastes medidos, los cuatro estados y todos los
   componentes portados de F16/F17 — esta capa es COMPOSICIÓN, no identidad.
   ================================================================================================== */

html, body { height: 100%; }
body {
  margin: 0;
  display: flex;
  background: var(--superficie);
  color: var(--tinta);
  font: 400 var(--t-md)/var(--lh-md) var(--prosa);
  overflow: hidden;                       /* el scroll vive en .principal, no en la página */
}

/* El atributo hidden MANDA sobre cualquier display de esta hoja. Sin esto, `.lateral{display:flex}`
   le ganaba al `hidden` que main.js pone sin sesión y la barra aparecía —con su lista esqueleto y
   su badge— al lado del formulario de entrada: una app a medias en vez de una app cerrada. */
[hidden] { display: none !important; }

/* ---- iconos del sprite --------------------------------------------------------------------------
   En el teléfono el icono acompaña al dedo, así que la base es 21 y en pantalla grande BAJA a 18:
   al revés de como estaba. Un icono grande sobra en escritorio; uno pequeño estorba en un móvil. */
.ic { width: 21px; height: 21px; flex: 0 0 auto; display: inline-block; vertical-align: -4px; }
.ic-marca { width: 24px; height: 24px; color: var(--acento); }

/* ==================================================================================================
   1 · LA BARRA — en el teléfono es un PANEL que entra desde el borde. Esa es su naturaleza aquí, no
   una excepción: por eso vive en la base y no en una corrección.
   ================================================================================================== */
.lateral {
  position: fixed; inset: 0 auto 0 0; z-index: 40;
  width: min(86vw, 340px);
  display: flex; flex-direction: column; min-height: 0;
  background: var(--fondo);
  border-right: var(--filete) solid var(--linea);
  /* La sombra gigante oscurece lo que asoma sin taparlo: el asomo sigue recibiendo el toque. */
  box-shadow: 0 0 0 100vmax rgba(0, 0, 0, .32);
  transition: transform .22s cubic-bezier(.2, .7, .3, 1);
}
.lateral.oculta { transform: translateX(-100%); box-shadow: none; }

.lateral-cab { padding: var(--e4) var(--e4) var(--e2); }
.marca {
  display: flex; align-items: center; gap: var(--e2);
  font: 600 var(--t-xl)/1 var(--prosa); letter-spacing: var(--track-titulo);
  color: var(--tinta); border: 0; padding: 0;
}

/* Un objetivo de toque mide 44. En la base es la altura de TODO lo pulsable; en pantalla grande,
   donde apunta un cursor de un píxel, baja a 36. */
.nav-item {
  display: flex; align-items: center; gap: var(--e3);
  width: auto; margin: 0 var(--e2); padding: 0 var(--e3);
  height: var(--alto-tactil);
  background: transparent; color: var(--tinta);
  border: 0; border-radius: var(--r-control);
  font: 500 var(--t-lg)/1 var(--prosa);
  cursor: pointer; text-align: left;
  transition: background .12s ease;
}
.nav-item:hover { background: var(--hundida); }
.nav-item .ic { color: var(--tinta-suave); }
.nav-item.nueva { margin-top: var(--e1); }

.buscar-envoltura {
  display: flex; align-items: center; gap: var(--e2);
  margin: var(--e2) var(--e3); padding: 0 var(--e3);
  height: var(--alto-tactil);
  background: var(--hundida);
  border: var(--filete) solid transparent; border-radius: var(--r-control);
  color: var(--tinta-apagada);
  transition: border-color .12s ease, background .12s ease;
}
.buscar-envoltura:focus-within { border-color: var(--acento); background: var(--superficie); }
.buscar-envoltura .ic { width: 18px; height: 18px; }
.buscar-envoltura .buscar-conv {
  flex: 1; margin: 0; padding: 0; height: auto;
  background: transparent; border: 0; outline: none; box-shadow: none;
  color: var(--tinta); font: 400 var(--t-lg)/1 var(--prosa);
}
.buscar-conv::placeholder { color: var(--tinta-apagada); }

.sec-acciones { margin: 0; padding: 0; border: 0; }
/* F18 M3 — Acciones es un PANEL, no un ítem de menú. Es la pieza que las cuatro direcciones del
   rediseño destacan igual: borde de acento, dos líneas y la cifra. Es lo único de la barra que
   espera a una persona, y se tiene que ver como tal. `height: auto` pisa el alto fijo de
   .nav-item: con subtítulo son dos líneas. */
.acciones-titulo {
  position: relative; width: calc(100% - 2 * var(--e2));
  height: auto; min-height: var(--alto-tactil);
  padding: var(--e2) var(--e3);
  border: var(--filete) solid var(--linea);
  border-left: var(--carril) solid var(--acento);
  border-radius: var(--r-menudo);
  background: var(--acento-tenue);
}
.acciones-titulo.activo { background: var(--hundida); }
.acciones-cuerpo { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.acciones-tit { font: 600 var(--t-md)/var(--lh-2xs) var(--prosa); color: var(--tinta); }
.acciones-sub {
  font: 400 var(--t-xs)/var(--lh-2xs) var(--prosa); color: var(--tinta-apagada);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Sin subtítulo (0 comprobado) el panel baja el volumen: borde normal, sin tinte. Cero de verdad
   no pide atención — pero el panel no desaparece, porque desaparecer es lo que hacía el ítem. */
.acciones-titulo:has(.acciones-sub[hidden]) {
  background: transparent;
  border-left-color: var(--linea);
}
.badge-pendientes {
  margin-left: auto;
  min-width: 20px; height: 20px; padding: 0 6px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--acento); color: var(--acento-tinta);
  border-radius: var(--r-chip);
  font: 600 var(--t-xs)/1 var(--prosa); font-variant-numeric: tabular-nums;
}
.badge-pendientes.desconocido { background: var(--desconocido); color: #fff; }

.lateral-aviso { margin: 0 var(--e3); }
.lateral-aviso:empty { display: none; }

/* ---- la lista de conversaciones ----------------------------------------------------------------
   Los grupos por fecha (Hoy / Ayer / Últimos 7 días / Anteriores) los pidió el operador explícito
   y se calculan con la fecha LOCAL de quien mira, no con el UTC del servidor. */
.conversaciones { flex: 1; min-height: 0; overflow-y: auto; padding: var(--e1) var(--e3) var(--e3); }
.grupo-titulo {
  padding: var(--e5) var(--e2) var(--e2);
  font: 600 var(--t-xs)/1 var(--prosa);
  letter-spacing: var(--track-micro); text-transform: uppercase;
  color: var(--tinta-apagada);
  border: 0; margin: 0;
}
.conv {
  position: relative;
  border-radius: var(--r-control);
  border: 0; margin: 0; padding: 0; background: transparent;
  transition: background .12s ease;
}
.conv:hover, .conv.activa { background: var(--hundida); }
.conv.activa .conv-titulo { font-weight: 600; }
.conv-abrir {
  /* F18 M3 — la fila del rediseño: UNA línea, título a la izquierda y hora corta a la derecha,
     como en las cuatro direcciones. El correo y la fecha completa viven en el title de la fila.
     Antes eran dos líneas y la segunda repetía el correo del operador en cada una. */
  display: flex; align-items: center; gap: var(--e2);
  width: 100%;
  min-height: var(--alto-tactil);
  /* El hueco de los dos botones de fila se RESERVA. En un teléfono están siempre visibles (no hay
     cursor con el que posarse) y sin esta reserva se montaban sobre la fecha y la tapaban: un
     botón que oculta el dato al que acompaña no ayuda, estorba. */
  padding: var(--e2) 78px var(--e2) var(--e2);
  background: transparent; border: 0; border-radius: var(--r-control);
  text-align: left; cursor: pointer; color: inherit; font: inherit;
}
.conv-titulo {
  flex: 1; min-width: 0;
  font: 400 var(--t-md)/var(--lh-md) var(--prosa); color: var(--tinta);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cuando {
  flex: none;
  font: 400 var(--t-xs)/1 var(--prosa); color: var(--tinta-apagada);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* La hora termina donde empieza la reserva de 78px de los botones de fila: conviven sin taparse,
   que era el defecto que la reserva vino a cerrar. */
.conv-acciones {
  position: absolute; right: var(--e1); top: 50%; transform: translateY(-50%);
  display: flex; gap: var(--e1);
  opacity: 1;                            /* visibles: en un teléfono no hay `hover` que revele */
}
.conv-btn {
  width: 34px; height: 34px; padding: 0;
  display: grid; place-items: center;
  background: var(--fondo); color: var(--tinta-apagada);
  border: 0; border-radius: var(--r-menudo);
  font-size: var(--t-md); cursor: pointer;
  transition: color .12s ease, background .12s ease;
}
.conv:hover .conv-btn { background: var(--hundida); }
.conv-btn:hover { color: var(--tinta); background: var(--fondo); }
.conv-editar {
  width: calc(100% - 2 * var(--e2)); margin: var(--e1) var(--e2);
  padding: var(--e2);
  background: var(--superficie); color: var(--tinta);
  border: var(--filete) solid var(--acento); border-radius: var(--r-menudo);
  font: 400 var(--t-lg)/var(--lh-sm) var(--prosa); outline: none;
}

/* ---- pie de la barra ---- */
.lateral-pie {
  padding: var(--e3) var(--e3) var(--e4);
  border-top: var(--filete) solid var(--linea);
  display: flex; flex-direction: column; gap: var(--e3);
  background: transparent;
}
.quien { font: 400 var(--t-sm)/var(--lh-xs) var(--prosa); color: var(--tinta-apagada); }
.quien a { color: var(--tinta-suave); }
.escape { display: flex; gap: var(--e4); }
.escape a {
  display: inline-flex; align-items: center; gap: var(--e1);
  font: 400 var(--t-sm)/1 var(--prosa); color: var(--tinta-apagada);
  text-decoration: none;
}
.escape a:hover { color: var(--tinta); }
.escape .ic { width: 16px; height: 16px; }

/* ==================================================================================================
   2 · EL ASOMO — la franja de chat que se ve con la barra abierta, y que al tocarla VUELVE al chat.
   Es un <button> y no un <div>: se alcanza con teclado y lo anuncia el lector de pantalla.
   Existe solo con la barra abierta; el CSS lo deriva del contenido, no de un estado en JS.
   ================================================================================================== */
.velo-lateral { display: none; }
body:has(#lateral:not(.oculta)) .velo-lateral:not([hidden]) {
  display: block;
  /* z-index 39, POR DEBAJO de la barra (40) — y este número fue un incidente, no un matiz. Con 41
     el velo cubría la pantalla entera POR ENCIMA del menú: cualquier toque —abrir una conversación,
     buscar, el selector de traje— aterrizaba en el velo, que cerraba la barra y devolvía al chat.
     El operador lo dijo exacto: «cualquier click que hago en el menú me regresa al chat, no me deja
     hacer nada». Por debajo, la barra recibe sus toques y el velo solo recibe los del asomo del
     chat, que es su único trabajo. */
  position: fixed; inset: 0; z-index: 39;
  background: transparent; border: 0; padding: 0;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}

/* ==================================================================================================
   3 · LA COLUMNA Y EL HAMBURGUESA — en el teléfono la columna ocupa TODO: la barra flota encima.
   ================================================================================================== */
.columna {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column; min-height: 0;
  position: relative;
  background: var(--superficie);
}
.btn-lateral {
  position: absolute; top: var(--e2); left: var(--e2); z-index: 45;
  width: var(--alto-tactil); height: var(--alto-tactil); padding: 0;
  display: grid; place-items: center;
  background: transparent; color: var(--tinta-apagada);
  border: 0; border-radius: var(--r-control);
  font-size: 20px; cursor: pointer;
  transition: background .12s ease, color .12s ease;
}
.btn-lateral:hover { background: var(--hundida); color: var(--tinta); }

/* EL RIEL no existe en el teléfono. No es una excepción que haya que apagar: aquí simplemente no
   se enciende. La versión anterior lo declaraba visible en la base y lo apagaba en la capa de
   móvil con una regla que PERDÍA POR ESPECIFICIDAD — un display:none escrito y sin efecto, que
   pasa cualquier prueba de existencia y sale igual en la pantalla. Mobile first lo hace
   imposible: para que el riel exista hay que ENCENDERLO, y solo se enciende arriba. */
.riel { display: none; }

.principal {
  flex: 1; min-height: 0; overflow-y: auto;
  background: transparent; border: 0; border-radius: 0; margin: 0; box-shadow: none;
}

/* ==================================================================================================
   4 · EL HILO — una columna. Lo tuyo en burbuja a la derecha, lo del agente en prosa plena a la
   izquierda, el sistema en líneas pequeñas centradas.
   ================================================================================================== */
.tiempo {
  max-width: var(--ancho-hilo); width: 100%;
  margin: 0 auto;
  padding: var(--e7) var(--e4) var(--e4);   /* el respiro de arriba deja sitio al hamburguesa */
  display: flex; flex-direction: column; gap: var(--e4);
  min-height: 100%;
  box-sizing: border-box;
}
.tiempo::before, .tiempo::after { display: none; }   /* la espina de F16 se retira */

.turno { display: flex; position: relative; margin: 0; padding: 0; border: 0; background: transparent; }
.turno .rol { display: none; }
.turno .cuerpo { min-width: 0; font: 400 var(--t-lg)/var(--lh-lg) var(--prosa); }

.turno.humano { justify-content: flex-end; }
.turno.humano .cuerpo {
  background: var(--hundida); color: var(--tinta);
  border-radius: var(--r-burbuja);
  padding: var(--e2) var(--e4);
  max-width: 88%;                        /* en un móvil, casi todo el ancho; arriba se estrecha */
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.turno.hermes { justify-content: flex-start; }
.turno.hermes .cuerpo { max-width: 100%; width: 100%; padding: 0; }
.turno.sistema { justify-content: center; }
.turno.sistema .cuerpo {
  font: 400 var(--t-xs)/var(--lh-xs) var(--prosa); color: var(--tinta-apagada);
  text-align: center; max-width: 60ch;
  white-space: pre-wrap; overflow-wrap: anywhere;
}

/* Copiar: en el teléfono no hay `hover`, así que está SIEMPRE presente y discreto. Arriba se
   esconde hasta que el cursor se posa. */
.turno-copiar {
  position: absolute; right: 0; top: calc(-1 * var(--e3));
  opacity: .55; transition: opacity .12s ease;
  background: var(--elevada); color: var(--tinta-apagada);
  border: var(--filete) solid var(--linea); border-radius: var(--r-menudo);
  padding: 2px var(--e2); font-size: var(--t-2xs); cursor: pointer;
}
.turno-copiar:hover, .turno-copiar:focus-visible { opacity: 1; }
.turno.humano .turno-copiar { right: auto; left: 0; }

/* ==================================================================================================
   5 · EL VACÍO — la pregunta manda. En el teléfono, UNA columna de tarjetas: dos de 150px en 375
   de ancho salen ilegibles.
   ================================================================================================== */
.hero {
  margin: auto;
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: var(--e2);
  max-width: 560px; width: 100%;
  padding: var(--e6) 0;
  animation: hero-entra .3s cubic-bezier(.2, .7, .3, 1) both;
}
.hero-marca { width: 34px; height: 34px; color: var(--tinta-apagada); margin-bottom: var(--e2); }
.hero-pregunta {
  font: 600 var(--t-2xl)/var(--lh-2xl) var(--prosa);
  letter-spacing: var(--track-titulo); color: var(--tinta);
}
.hero-sub { font: 400 var(--t-md)/var(--lh-md) var(--prosa); color: var(--tinta-apagada); max-width: 44ch; }
.hero-tarjetas {
  display: grid; grid-template-columns: 1fr;
  gap: var(--e2); width: 100%; margin-top: var(--e4);
}
.hero-tarjeta {
  display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
  padding: var(--e3) var(--e4);
  min-height: var(--alto-tactil);
  background: var(--elevada); color: var(--tinta);
  border: var(--filete) solid var(--borde-flotante); border-radius: var(--r-tarjeta);
  text-align: left; cursor: pointer;
  box-shadow: var(--sombra-1);
  transition: border-color .14s ease, transform .14s ease, background .14s ease;
}
.hero-tarjeta:hover { border-color: var(--borde); background: var(--hundida); }
.hero-tarjeta:active { transform: scale(.99); }
.hero-ic { color: var(--acento); width: 20px; height: 20px; margin-bottom: var(--e1); }
.hero-titulo { font: 600 var(--t-md)/var(--lh-sm) var(--prosa); }
.hero-detalle { font: 400 var(--t-sm)/var(--lh-sm) var(--prosa); color: var(--tinta-apagada); }
@keyframes hero-entra { from { opacity: 0; transform: translateY(8px); } }

.tiempo > .vacio {
  text-align: center; margin: 0 auto; max-width: 56ch;
  font-size: var(--t-xs); color: var(--tinta-apagada);
  background: transparent; border: 0; box-shadow: none; padding: var(--e2) 0;
}
.tiempo > .vacio::before { display: none; }

/* ==================================================================================================
   6 · EL TURNO EN VIVO — actividad del agente mientras trabaja.
   ================================================================================================== */
.vivo-actividad {
  display: flex; align-items: center; gap: var(--e2);
  color: var(--tinta-apagada);
  font: 400 var(--t-sm)/var(--lh-sm) var(--prosa);
  margin-bottom: var(--e2);
}
.vivo-actividad::before {
  content: ''; width: 7px; height: 7px; border-radius: 50%;
  background: var(--acento); flex: 0 0 auto;
  animation: latido 1.15s ease-in-out infinite;
}
.vivo-actividad.mal { color: var(--mal); }
.vivo-actividad.mal::before { background: var(--mal); animation: none; }
.vivo-actividad.pensamiento { font-style: italic; }
/* El nombre de una herramienta es un IDENTIFICADOR y se lee como tal. */
.vivo-actividad.herramienta { font-family: var(--mono); font-size: var(--t-xs); }
.vivo-actividad.plan { font-variant-numeric: tabular-nums; }
/* REGLA 4 también aquí. Esta la encontró la PRUEBA que generalizó el hallazgo, no el hallazgo:
   arreglar dos piezas a mano habría dejado la tercera. El punto late en su color pero un evento
   sin nombre conocido también se subraya, porque el color solo no basta. */
.vivo-actividad.desconocido { color: var(--desconocido); text-decoration: underline dotted; text-underline-offset: 3px; }
.vivo-actividad.desconocido::before { background: var(--desconocido); animation: none; }
.vivo-pie {
  color: var(--tinta-apagada);
  font: 400 var(--t-xs)/var(--lh-xs) var(--prosa);
  margin-top: var(--e3); padding-top: var(--e2);
  border-top: var(--filete) solid var(--linea);
}
@keyframes latido { 50% { opacity: .35; transform: scale(.8); } }

/* ==================================================================================================
   7 · EL COMPOSITOR — la píldora. La alineación es ARITMÉTICA: la línea de texto (25.6 de altura
   + 5+5 de padding) mide 36, igual que el «+» y que el enviar; la píldora cierra en 52.
   Se estila por CLASE y no por `#enviar`: una pieza que no se puede documentar sin su id está mal
   factorizada — el id es único por página y la pieza salió rota en su propia tarjeta.
   ================================================================================================== */
/* `position: relative` NO es decorativo: el popover de «/» (.sugerencias) es `absolute` con
   `bottom: 100%` y se ancla al ancestro posicionado más cercano. Sin esto, ese ancestro era
   .columna y el popover se pintaba ENCIMA de la columna entera — fuera de la pantalla por arriba.
   El operador tecleaba «/» y no salía nada: el menú llevaba semanas abriéndose donde nadie podía
   verlo. /ui/ lo tenía (`.cajon { position: relative }` en ui.css) y la reconstrucción de esta
   hoja lo perdió. */
.cajon { position: relative; background: transparent; border: 0; box-shadow: none; padding: var(--e1) var(--e3) var(--e3); }
.cajon-aviso { max-width: var(--ancho-hilo); margin: 0 auto var(--e1); }
.cajon-aviso:empty { display: none; }
.cajon-linea {
  max-width: var(--ancho-hilo); margin: 0 auto;
  display: flex; align-items: flex-end; gap: 6px;
  min-height: 52px;
  background: var(--elevada);
  border: var(--filete) solid var(--borde-flotante);
  border-radius: 28px;
  padding: var(--e2);
  box-shadow: var(--sombra-1);
  transition: border-color .14s ease, box-shadow .14s ease;
}
/* El foco no grita: superficie y sombra, sin halo de color. */
.cajon-linea:focus-within { border-color: var(--borde); box-shadow: var(--sombra-1); }
#entrada-texto {
  flex: 1; resize: none; max-height: 40vh;
  background: transparent; color: var(--tinta);
  border: 0; outline: none; box-shadow: none;
  padding: 5px 0; margin: 0 var(--e1);
  /* 16px como mínimo real: por debajo, iOS hace ZOOM al enfocar el campo y descoloca la página
     entera. Es el defecto de móvil más común y no se ve en ningún escritorio. */
  font: 400 max(16px, var(--t-lg))/var(--lh-md) var(--prosa);
}
#entrada-texto::placeholder { color: var(--tinta-apagada); }

.adjuntar {
  width: 36px; height: 36px; min-width: 36px; padding: 0;
  display: grid; place-items: center;
  background: transparent; color: var(--tinta-suave);
  border: 0; border-radius: 50%;
  font-size: 20px; line-height: 1; cursor: pointer;
  transition: background .12s ease, color .12s ease;
}
.adjuntar:hover { background: var(--hundida); color: var(--tinta); }

button.primario.redondo {
  width: 36px; height: 36px; min-width: 36px; padding: 0;
  display: grid; place-items: center;
  background: var(--tinta); color: var(--superficie);
  border: 0; border-radius: 50%;
  cursor: pointer;
  transition: opacity .12s ease, transform .12s ease;
}
button.primario.redondo:hover:not(:disabled) { opacity: .8; }
button.primario.redondo:active:not(:disabled) { transform: scale(.94); }
button.primario.redondo:disabled { background: var(--hundida); color: var(--tinta-apagada); }
button.primario.redondo .ic { width: 17px; height: 17px; vertical-align: 0; }
/* Un solo botón envía y DETIENE: los dos iconos están presentes y la clase decide cuál se ve, así
   no hay que reconstruir el árbol en pleno streaming. */
button.primario.redondo .ic-detener { display: none; }
button.primario.redondo.deteniendo .ic-enviar { display: none; }
button.primario.redondo.deteniendo .ic-detener { display: block; }

.cajon-pie {
  max-width: var(--ancho-hilo); margin: var(--e2) auto 0;
  text-align: center;
  color: var(--tinta-apagada);
  font-size: var(--t-2xs); line-height: var(--lh-2xs);
}
.chip-proveedor { color: var(--tinta-suave); }

/* El selector de modelo, como un elemento MÁS de la línea «a · b · c» del pie — no como un control
   suelto. Mismo tratamiento que el chip del proveedor, con un subrayado punteado que es la única
   pista de que se puede tocar.

   Esta regla está DUPLICADA en /ui/ui.css a propósito, y no por descuido: /app/ carga solo
   app.css y /ui/ carga solo ui.css, aunque las dos superficies compartan los MISMOS módulos JS. La
   primera versión estiló solo ui.css y en /app/ salió el desplegable con el borde por defecto del
   navegador. `tests/modeloRecordado.test.ts` exige que las dos hojas lo estilen. */
.cajon-pie .sel-modelo { display: inline-flex; align-items: baseline; gap: 3px; }
.cajon-pie .sel-modelo select {
  font: inherit; color: var(--tinta-suave);
  background: transparent; border: 0; border-bottom: 1px dotted currentColor;
  padding: 0 0 1px; cursor: pointer; max-width: 12em;
  -webkit-appearance: none; appearance: none;
}
.cajon-pie .sel-modelo select:hover,
.cajon-pie .sel-modelo select:focus-visible { color: var(--tinta); }
.adj-cola { max-width: var(--ancho-hilo); margin: 0 auto var(--e1); }

/* ==================================================================================================
   8 · LA FRANJA DERIVADA — cinco datos, cada uno con su marca. La idea entera en 20px de alto: el
   estado NO es el fondo del chip, es la raya de 2px del margen más el color de la cifra.
   Este bloque se perdió una vez al reensamblar la hoja y en el pie se leía «pendientes0», etiqueta
   y cifra pegadas. Lo cazó el operador pegando su HTML, no una revisión.
   ================================================================================================== */
.franja {
  display: flex; flex-wrap: wrap; gap: var(--e2);
  padding: 0 var(--e2);
  font-variant-numeric: tabular-nums;
}
.dato {
  display: inline-flex; align-items: baseline; gap: var(--e2);
  font: 400 var(--t-xs)/var(--lh-2xs) var(--prosa);
}
.dato::before {
  content: ''; align-self: center; flex: 0 0 auto;
  width: var(--carril-fino); height: 1.1em;
  border-radius: var(--r-chip); background: var(--marca);
}
.dato .etq { color: var(--tinta-apagada); text-transform: uppercase; letter-spacing: var(--track-micro); }

/* ==================================================================================================
   UNA FORMA POR ESTADO — F18
   ==================================================================================================
   Las CUATRO direcciones del rediseño coinciden en algo que esta hoja no hacía: cada estado tiene
   una FORMA propia, no solo un color. Círculo lo bueno, triángulo la alerta, cuadrado lo malo, aro
   punteado el hueco. Coincidieron sin haberlo acordado, que es la señal de que la idea es del
   dominio y no del gusto de nadie.

   Aquí los cuatro compartían la misma barrita de 2px y solo cambiaba el color. La regla 4 se
   cumplía —la palabra acompaña siempre— pero la marca en sí no distinguía nada: en una captura en
   gris, «bien» y «mal» eran la misma raya.

   Va en app.css y no en un traje: no es una decisión estética, es la diferencia entre una flota
   sana y una caída. Se dibuja con CSS —sin tocar el marcado ni añadir un sprite— para que valga
   igual en las dos presentaciones y en cualquier traje. La forma nace en el pseudo-elemento que ya
   existía, así que ningún módulo se entera.
   ================================================================================================== */

/* Base: la raya de siempre para lo que no tiene estado (el saldo, por ejemplo). */
.dato.bien::before, .dato.alerta::before, .dato.mal::before, .dato.desconocido::before,
.insignia.ejecutada::after, .insignia.pendiente::after, .insignia.fallida::after,
.insignia.desconocido::after, .insignia.est-desconocido::after {
  width: .62em; height: .62em;
  align-self: center;
}
/* BIEN — círculo macizo. Lo cerrado se dibuja cerrado. */
.dato.bien::before, .insignia.ejecutada::after { border-radius: 50%; }
/* MAL — cuadrado. La esquina viva es la forma que más «para» a la vista. */
.dato.mal::before, .insignia.fallida::after { border-radius: 0; }
/* ALERTA — triángulo, con bordes: no hay `clip-path` que herede el color de forma fiable en los
   dos pseudo-elementos, y un triángulo de bordes usa `currentColor` sin más. */
.dato.alerta::before, .insignia.pendiente::after {
  width: 0; height: 0; background: none; border-radius: 0;
  border-left: .34em solid transparent;
  border-right: .34em solid transparent;
  border-bottom: .58em solid currentColor;
}
.dato.alerta::before { border-bottom-color: var(--marca); }
/* DESCONOCIDO — aro HUECO y punteado. Un hueco tiene que verse hueco, y ese es literalmente el
   argumento con el que las cuatro direcciones lo dibujaron así. */
.dato.desconocido::before, .insignia.desconocido::after, .insignia.est-desconocido::after {
  background: none; border-radius: 50%;
  border: 1.5px dotted currentColor;
}
.dato.desconocido::before { border-color: var(--marca); }
.dato .val { font-size: var(--t-sm); font-weight: 700; color: var(--tinta); letter-spacing: -.01em; }
.dato.bien        { --marca: var(--bien); }
.dato.alerta      { --marca: var(--alerta); }
.dato.mal         { --marca: var(--mal); }
.dato.desconocido { --marca: var(--desconocido); }
.dato.bien .val        { color: var(--bien); }
.dato.alerta .val      { color: var(--alerta); }
.dato.mal .val         { color: var(--mal); }
/* El color no es la única señal: lo no comprobado se subraya de puntos y se ve igual sin
   distinguir colores. */
.dato.desconocido .val { color: var(--desconocido); text-decoration: underline dotted; text-underline-offset: 3px; }
/* El saldo trae dos cifras en una (dinero medido + tokens estimados): línea entera para él, y sin
   color de estado — nadie ha declarado qué es «poco saldo», y pintarlo de rojo sería una política
   que no existe. */
.dato:first-child { flex-basis: 100%; }
/* F18 — el motivo de un hueco, escrito. estado.js lo emite SIEMPRE (hasta ahora solo vivía en el
   `title`, y en un teléfono no hay hover: era inalcanzable). Aquí nace OCULTO porque la franja de
   F16 es una tira de cifras y una frase entera la rompe; los trajes que la componen de otra manera
   lo encienden. Que el nodo exista siempre es lo que permite encenderlo sin tocar el módulo. */
.dato-nota { display: none; }

/* ==================================================================================================
   9 · PANTALLA GRANDE (≥ 48em) — lo que se AÑADE cuando hay sitio. Nada se deshace.
   ================================================================================================== */
@media (min-width: 48em) {
  /* La barra deja de flotar y pasa a ocupar su columna. */
  .lateral {
    position: static; inset: auto;
    width: var(--ancho-lateral); flex: 0 0 var(--ancho-lateral);
    box-shadow: none;
    transition: margin-left .22s cubic-bezier(.2, .7, .3, 1);
  }
  .lateral.oculta { transform: none; margin-left: calc(-1 * var(--ancho-lateral)); }

  /* Densidad de cursor: lo pulsable baja de 44 a 36 y la letra a tamaño de interfaz. */
  .ic { width: 18px; height: 18px; vertical-align: -3px; }
  .ic-marca { width: 20px; height: 20px; }
  .marca { font-size: var(--t-lg); }
  .nav-item { height: 36px; font-size: var(--t-md); gap: var(--e2); padding: 0 var(--e2); }
  .nav-item .ic { width: 18px; height: 18px; }
  .buscar-envoltura { height: var(--alto-control); margin: var(--e2); padding: 0 var(--e2); }
  .buscar-envoltura .ic { width: 15px; height: 15px; }
  .buscar-envoltura .buscar-conv { font-size: var(--t-md); }
  .conversaciones { padding: var(--e1) var(--e2) var(--e2); }
  .grupo-titulo { padding: var(--e4) var(--e2) var(--e1); font-size: var(--t-2xs); }
  .conv-titulo { font-size: var(--t-md); line-height: var(--lh-sm); }
  .cuando { font-size: var(--t-2xs); margin-top: 1px; }
  .conv-btn { width: 24px; height: 24px; font-size: var(--t-sm); }
  .conv-editar { font-size: var(--t-md); padding: 5px var(--e2); }
  .badge-pendientes { min-width: 18px; height: 18px; padding: 0 5px; font-size: var(--t-2xs); }
  .lateral-pie { padding: var(--e3) var(--e4); gap: var(--e2); }
  .quien, .escape a { font-size: var(--t-xs); }
  .escape { gap: var(--e3); }
  .escape .ic { width: 14px; height: 14px; }
  .dato { font-size: var(--t-2xs); }
  .dato .val { font-size: var(--t-xs); }

  /* Con cursor, los botones de fila se revelan al posarse: la fila respira mientras se lee. */
  .conv-abrir { padding: 7px var(--e2); }
  .conv-acciones { opacity: 0; transition: opacity .12s ease; }
  .conv:hover .conv-acciones, .conv:focus-within .conv-acciones { opacity: 1; }

  /* EL RIEL se ENCIENDE aquí, y solo aquí. */
  body:has(#lateral.oculta) .riel:not([hidden]) {
    display: flex;
    position: absolute; inset: 0 auto 0 0;
    width: 52px; z-index: 25;
    flex-direction: column; align-items: center;
    gap: var(--e1); padding: var(--e3) 0;
  }
  .riel-btn {
    width: 36px; height: 36px; padding: 0;
    display: grid; place-items: center;
    background: transparent; color: var(--tinta-suave);
    border: 0; border-radius: var(--r-control);
    cursor: pointer;
    transition: background .12s ease, color .12s ease;
  }
  .riel-btn:hover { background: var(--hundida); color: var(--tinta); }
  .riel-btn:first-child { margin-bottom: var(--e2); }
  /* Con el riel presente el ☰ flotante sobra: el riel trae su propio botón de abrir. */
  body:has(#lateral.oculta) .btn-lateral { display: none; }
  .btn-lateral { top: var(--e3); left: var(--e3); width: 34px; height: 34px; font-size: 16px; }

  /* El asomo no existe: la barra ya no tapa nada. */
  body:has(#lateral:not(.oculta)) .velo-lateral:not([hidden]) { display: none; }

  /* El eje de lectura se ensancha y respira. */
  .tiempo { padding: var(--e6) var(--e5) var(--e5); }
  .turno.humano .cuerpo { max-width: 72%; padding: 9px var(--e4); }
  /* La mitad de arriba del botón de ejecutar: con cursor no hace falta la fila entera. */
  .confirmar button.primario { flex: 0 1 auto; min-height: 0; }
  .turno-copiar { opacity: 0; top: calc(-1 * var(--e2)); }
  .turno:hover .turno-copiar { opacity: 1; }
  .cajon { padding: var(--e1) var(--e5) var(--e3); }
  .cajon-linea { padding: var(--e2) var(--e2) var(--e2) var(--e3); }

  /* El vacío pasa a dos columnas y la pregunta crece. */
  .hero-pregunta { font: 600 var(--t-3xl)/var(--lh-3xl) var(--prosa); }
  .hero-marca { width: 36px; height: 36px; }
  .hero-tarjetas { grid-template-columns: repeat(2, minmax(0, 1fr)); margin-top: var(--e5); }
  .hero-tarjeta:hover { transform: translateY(-1px); box-shadow: var(--sombra-1); }
  .hero-ic { width: 19px; height: 19px; }
}

/* ==================================================================================================
   10 · MOVIMIENTO REDUCIDO — requisito, no extra. Quien pidió calma no recibe nada que se mueva, y
   la información NUNCA depende de la animación: el texto de al lado siempre la lleva.
   ================================================================================================== */
@media (prefers-reduced-motion: reduce) {
  .hero { animation: none; }
  .vivo-actividad::before { animation: none; }
  .lateral, .hero-tarjeta, .nav-item, .conv, .buscar-envoltura, .btn-lateral, .turno-copiar,
  .conv-acciones, button.primario.redondo, .adjuntar, .cajon-linea, .riel-btn { transition: none; }
}

/* ==================================================================================================
   11 · PIEZAS RECUPERADAS TRAS EL REENSAMBLADO A MOBILE FIRST.
   Al partir la hoja para reescribir el armazón se quedaron fuera tres bloques que vivían mezclados
   con las capas viejas: el aviso de ATENCIÓN, las tablas del markdown y la regla horizontal. No lo
   vio ninguna revisión — lo cazó el verificador del design system cruzando las clases que el
   marcado usa contra las que la hoja viste. Es la segunda vez que un reensamblado se lleva algo
   por delante (la primera fue la franja), y por eso ese verificador existe.
   ================================================================================================== */
/* ---- aviso de ATENCIÓN (F18) — degradación declarada, no fallo -------------------------------
   El modo reducido salía vestido de error (.aviso.mal, rojo) y el operador leyó «tu mensaje
   falló» donde decía «este panel tiene menos herramientas». Son cosas distintas y visten
   distinto: ámbar de --alerta, sin el fondo tintado que el error reserva para sí. */
.aviso.atencion {
  color: var(--alerta);
  border-left: var(--carril) solid var(--alerta);
  padding-left: var(--e3);
}

/* ---- tablas y regla del markdown (F18) --------------------------------------------------------
   Hermes resume archivos y estados en tablas constantemente; hasta ahora salían como sopa de
   barras. La caja pone el scroll: una tabla ancha se desplaza dentro de su caja, no rompe la
   columna del hilo. */
.md-tabla-caja { overflow-x: auto; margin: var(--e3) 0; }
.md-tabla {
  border-collapse: collapse;
  font-size: var(--t-sm); line-height: var(--lh-sm);
  min-width: 50%;
}
.md-tabla th, .md-tabla td {
  border: var(--filete) solid var(--linea);
  padding: var(--e1) var(--e3);
  text-align: left; vertical-align: top;
}
.md-tabla th {
  font-weight: 600; color: var(--tinta);
  background: var(--hundida);
}
.md-tabla td { color: var(--tinta-suave); }
.md-tabla tbody tr:nth-child(even) td { background: color-mix(in srgb, var(--hundida) 40%, transparent); }
.md-regla { border: 0; border-top: var(--filete) solid var(--linea); margin: var(--e4) 0; }
