/* Interfaz de la PWA de JARVIS.
 *
 * Mismo sistema visual que el escritorio: chasis de grafito con seis escalones
 * de luminosidad y un acento que **codifica procedencia** —ámbar si el trabajo
 * se resolvió en tu máquina, acero si salió a la red—. Antes esto iba por libre
 * con un cian sobre azul marino y el producto tenía dos identidades distintas.
 *
 * La jerarquía la lleva la luz, no el filete de un píxel: una superficie se
 * distingue de la de debajo por ser más clara. Aquí, además, se puede hacer de
 * verdad lo que en Qt hubo que fingir, porque el navegador sí tiene sombras y
 * desenfoque de fondo.
 *
 * Los nombres de clase son contrato con `app.js`, que conmuta `oculta`,
 * `copiada` y `con-texto`, entre otras. No se renombra ninguno.
 */

/* Las mismas familias que el escritorio, servidas desde el propio PC. Sin
   esto la PWA caía en la tipografía del sistema —Roboto en Android, San
   Francisco en iOS— y el mismo producto tenía dos letras según el aparato desde
   el que se mirara. No hay CDN de por medio a propósito: el cliente es
   zero-trust y los archivos ya estaban en `gui/fonts/` para la ventana de Qt.

   `swap` para que el texto se lea desde el primer fotograma con la tipografía
   del sistema y cambie cuando la buena llegue; en una red doméstica es un
   parpadeo, y es mejor que una pantalla en blanco.

   Bricolage y Hanken son variables: un solo archivo cubre todos los pesos, y
   los corchetes de su nombre van escapados en la URL. */
@font-face {
  font-family: 'Hanken Grotesk';
  src: url('/fonts/HankenGrotesk%5Bwght%5D.ttf') format('truetype');
  font-weight: 100 900;
  font-display: swap;
}
@font-face {
  font-family: 'Bricolage Grotesque';
  src: url('/fonts/BricolageGrotesque%5Bopsz,wdth,wght%5D.ttf') format('truetype');
  font-weight: 200 800;
  font-display: swap;
}
@font-face {
  font-family: 'JetBrains Mono';
  src: url('/fonts/JetBrainsMono-Regular.ttf') format('truetype');
  font-weight: 400;
  font-display: swap;
}

/* ── Tokens ──────────────────────────────────────────────────────────────
   Copia a mano de `gui/theme.py` (el navegador no puede importar una
   dataclass). La vigila `tests/test_diseno_compartido.py`: cada valor tiene
   que ser el del tema, y fuera de estos dos bloques no puede haber ni un
   color escrito (ni `#…` ni `rgba(…)`). Los velos de los colores semánticos se
   derivan con `color-mix`, que resuelve sobre el valor del tema activo.

   El bloque claro solo redefine lo que cambia. El **escenario** (`--escenario-*`)
   y el acento no se redefinen nunca: la esfera es luz aditiva y sobre un fondo
   claro se lava, así que el escenario es oscuro en los dos temas, como en el
   escritorio. */
:root {
  color-scheme: dark;

  /* Chasis · PLASMA_OSCURO */
  --void: #050408;
  --pozo: #07060a;
  --chasis: #0a0910;
  --rail: #0d0b13;
  --alzado: #15121d;
  --alzado-alto: #1d1928;
  --linea: #1f1b29;
  --canto: #2e2939;

  /* Procedencia: naranja = se resolvió en tu PC, azul = tocó la red */
  --local: #ff8a3d;
  --local-suave: #ffb27f;
  --local-velo: rgba(255, 138, 61, 0.160);
  --local-hondo: rgba(255, 138, 61, 0.220);
  --red: #5fa8ff;
  --red-suave: #8ec2ff;
  --red-velo: rgba(95, 168, 255, 0.180);

  /* Estados */
  --ok: #5bd39a;
  --aviso: #efc05a;
  --fallo: #f0655c;

  /* Texto */
  --texto: #f4f1f8;
  --texto-tenue: #c9c3d6;
  --texto-sutil: #a59fb3;
  --sobre-acento: #050408;

  /* Escenario de la esfera: oscuro en los dos temas */
  --escenario-fondo: #09070d;
  --escenario-resplandor: #1a0f2a;
  --escenario-texto: #f4f1f8;
  --escenario-suave: #c9c3d6;
  --escenario-tenue: #a59fb3;
  --escenario-linea: #2a2433;
  --escenario-campo: #110e18;
  --escenario-local: #ffb27f;
  --escenario-red: #8ec2ff;

  /* Degradado de acento: el botón de hablar */
  --acento-a: #ff2d6f;
  --acento-b: #ff8a3d;
  --acento-c: #6a5cff;

  /* Solo de la PWA: el navegador sí tiene sombras, Qt no. */
  --sombra: rgba(0, 0, 0, 0.550);

  /* Velos derivados: se recalculan solos en el tema claro. */
  --fallo-velo: color-mix(in srgb, var(--fallo) 12%, transparent);
  --ok-velo: color-mix(in srgb, var(--ok) 18%, transparent);
  --aviso-velo: color-mix(in srgb, var(--aviso) 18%, transparent);
  --fallo-halo: color-mix(in srgb, var(--fallo) 18%, transparent);

  /* Tipografía: mono = dato, Hanken = lenguaje, Bricolage = titulares y estado */
  --letra: 'Hanken Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, system-ui, sans-serif;
  --letra-titular: 'Bricolage Grotesque', 'Hanken Grotesk', system-ui, sans-serif;
  --letra-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Consolas, monospace;

  /* Radios. Los mismos escalones que el escritorio: cápsula › panel ›
     tarjeta › cola de burbuja, cada uno menor que el que lo contiene. */
  --radio: 20px;
  --radio-tarjeta: 16px;
  --radio-chico: 13px;
  --radio-minimo: 4px;
  --radio-pastilla: 999px;

  /* Movimiento, en los mismos tiempos que `MotionTokens`. */
  --rapido: 120ms;
  --normal: 200ms;

  --seguro-abajo: env(safe-area-inset-bottom, 0px);
  --seguro-arriba: env(safe-area-inset-top, 0px);
}

/* Tema claro · PLASMA_CLARO. Lo pone `escena.js::aplicarTema` en el `<head>`. */
:root[data-tema="claro"] {
  color-scheme: light;

  --void: #e9e6ef;
  --pozo: #efecf4;
  --chasis: #f3f1f6;
  --rail: #edeaf1;
  --alzado: #ffffff;
  --alzado-alto: #f7f5fa;
  --linea: #ddd8e5;
  --canto: #cfc9da;

  --local: #d9661f;
  --local-suave: #94430c;
  --local-velo: rgba(217, 102, 31, 0.160);
  --local-hondo: rgba(217, 102, 31, 0.220);
  --red: #2e66b8;
  --red-suave: #24559c;
  --red-velo: rgba(46, 102, 184, 0.180);

  --ok: #23784f;
  --aviso: #8a630d;
  --fallo: #c51d12;

  --texto: #15121c;
  --texto-tenue: #3e3949;
  --texto-sutil: #5d5869;

  --sombra: rgba(21, 18, 28, 0.160);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--void);
  color: var(--texto);
  font-family: var(--letra);
  font-size: 16px;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: none;
}

body { display: flex; flex-direction: column; }

/* Los controles no heredan la letra: sin esto, «Emparejar» y los botones de
   Ajustes salían en la del sistema (Arial en Chromium), visto en las capturas
   de la Task 10. Solo la familia: cada control fija su cuerpo y su peso. */
button, input, textarea, select { font-family: inherit; }

.vista { display: flex; flex-direction: column; flex: 1; min-height: 0; position: relative; }
.oculta { display: none !important; }

/* ── Marca ──────────────────────────────────────────────────────────────── */
/* Compacta y pesada. La versión anterior la separaba casi tres décimas de eme
   por letra, que es el tic tipográfico del panel de control de película. */
.marca { display: flex; align-items: center; gap: 9px; }
.marca h1 {
  font-size: 21px;
  font-weight: 800;
  letter-spacing: -0.02em;
  margin: 0;
}
.marca.compacta { font-size: 15px; font-weight: 800; letter-spacing: -0.01em; color: var(--texto); }

/* El logo es la marca de la esfera (`/static/marca.svg`, copia que genera
   `tools/generar_iconos.py` desde `gui/marca/marca.svg`). Antes era un punto
   naranja dibujado en CSS, y por eso el logo nuevo no llegaba aquí. El estado
   del enlace no es esto: es `#punto-estado`. */
.logo { display: block; flex: none; width: 24px; height: 24px; }
.marca:not(.compacta) .logo { width: 34px; height: 34px; }

/* ── Emparejamiento ─────────────────────────────────────────────────────── */
#vista-pareo { justify-content: center; padding: 28px 22px; }
.pareo-caja { max-width: 400px; margin: 0 auto; width: 100%; }
.pareo-texto { color: var(--texto-tenue); margin: 22px 0 26px; line-height: 1.5; }

.campo-codigo {
  width: 100%;
  background: var(--alzado);
  border: none;
  border-radius: var(--radio);
  color: var(--texto);
  font-size: 30px;
  font-weight: 700;
  letter-spacing: 0.34em;
  /* El tracking añade su hueco **después** de cada carácter, el último
     incluido, así que un texto centrado queda empujado media eme a la derecha.
     La sangría lo devuelve al centro óptico. */
  text-indent: 0.34em;
  text-align: center;
  padding: 18px 12px;
  font-family: var(--letra-mono);
  font-variant-numeric: tabular-nums;
}
.campo-codigo::placeholder { color: var(--texto-sutil); }
.campo-codigo:focus { outline: 2px solid var(--local); outline-offset: 2px; }

.pareo-error { color: var(--fallo); min-height: 22px; margin: 14px 0 0; font-size: 14px; }

.btn-primario {
  width: 100%;
  margin-top: 18px;
  padding: 16px;
  border: none;
  border-radius: var(--radio);
  background: var(--local);
  color: var(--sobre-acento);
  font-size: 16px;
  font-weight: 700;
  box-shadow: 0 10px 26px -12px var(--local-hondo);
}
.btn-primario:disabled { opacity: 0.45; box-shadow: none; }

.btn-secundario {
  width: 100%;
  margin-top: 14px;
  padding: 14px;
  border: none;
  border-radius: var(--radio-chico);
  background: transparent;
  color: var(--fallo);
  font-size: 14px;
  font-weight: 600;
}

/* ── Cabecera ───────────────────────────────────────────────────────────── */
/* JARVIS a la izquierda y el destino a la derecha, como en el lienzo. Sigue al
   tema; el escenario de debajo no. */
.cabecera {
  flex: none;
  display: flex; align-items: center; gap: 10px;
  min-height: 64px;
  padding: calc(var(--seguro-arriba) + 10px) 16px 6px 22px;
  background: var(--chasis);
  position: relative; z-index: 5;
}
.marca.compacta span {
  font-family: var(--letra-titular);
  font-size: 16px; font-weight: 600; letter-spacing: 0.3em;
}

/* La conexión con el PC: siempre el punto; el texto solo si no hay línea. Con
   línea se oculta a la vista pero no al lector de pantalla. */
.estado { margin-left: auto; display: flex; align-items: center; gap: 7px; font-size: 12px; color: var(--texto-tenue); }
.estado[data-conexion="viva"] #texto-estado {
  position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%);
}
.punto { width: 7px; height: 7px; border-radius: 50%; background: var(--texto-sutil); flex: none; }
.punto.viva { background: var(--ok); box-shadow: 0 0 0 3px var(--ok-velo); }
.punto.pensando { background: var(--red); animation: latido 1.1s ease-in-out infinite; }
.punto.caida { background: var(--fallo); box-shadow: 0 0 0 3px var(--fallo-halo); }

@keyframes latido { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }

/* Destino: una pastilla con su menú. El color es la procedencia —azul el PC,
   naranja el teléfono—, igual que en la ventana y en Android. */
.destino { position: relative; flex: none; }
.destino-actual {
  display: flex; align-items: center; gap: 7px;
  min-height: 36px; padding: 0 12px;
  border: 1px solid var(--canto); border-radius: var(--radio-pastilla);
  background: transparent; color: var(--texto);
  font: inherit; font-size: 13px;
}
.destino-actual .flecha { color: var(--texto-sutil); }
.destino-actual[data-destino="pc"] { color: var(--red-suave); }
.destino-actual[data-destino="movil"] { color: var(--local-suave); }
.ics { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; flex: none; }
.ic { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; flex: none; }

.destino-menu {
  position: absolute; right: 0; top: calc(100% + 6px);
  display: flex; flex-direction: column; gap: 3px;
  min-width: 190px; padding: 6px;
  background: var(--alzado); border-radius: var(--radio-chico);
  box-shadow: 0 18px 40px -20px var(--sombra);
}
.destino-opcion {
  display: flex; align-items: center; gap: 8px;
  min-height: 44px; padding: 0 12px;
  border: none; border-radius: var(--radio-minimo);
  background: transparent; color: var(--texto-sutil);
  font: inherit; font-size: 14px; font-weight: 600; text-align: left;
}
#destino-pc.activa { color: var(--red-suave); background: var(--red-velo); }
#destino-movil.activa { color: var(--local-suave); background: var(--local-hondo); }
/* «En el teléfono» necesita la app de Android conectada (ver `fijarDestino`):
   la nota lo dice bajo las dos opciones. */
.destino-nota { margin: 2px 12px 6px; max-width: 200px; color: var(--texto-sutil); font-size: 12px; line-height: 1.4; }

/* ── Movimiento ─────────────────────────────────────────────────────────── */
/* Un solo momento animado, y es el que dice que la conversación avanzó: la
   entrada de un turno, de una fila de registro o de un sello de tiempo. Lo que
   se toca cien veces al día —el compositor, el selector de aparato, el botón de
   enviar— no se anima nunca: cualquier duración ahí se lee como lentitud.

   La curva arranca rápida (`cubic-bezier(.23,1,.32,1)`, un ease-out fuerte):
   `ease-in` retrasa el movimiento justo en el instante que el ojo está mirando.
   Y no sale de `scale(0)` ni de `translateY` grande: nada en el mundo real
   aparece de la nada, así que seis píxeles bastan para que se lea como llegada.

   El bloque de `prefers-reduced-motion` de más abajo lo desactiva entero, junto
   con el latido del punto de estado. */
@keyframes entrar-turno {
  from { opacity: 0; transform: translateY(6px); }
}
.burbuja, .registro, .sello-hora {
  animation: entrar-turno 200ms cubic-bezier(0.23, 1, 0.32, 1) both;
}

@media (prefers-reduced-motion: reduce) {
  * {
    transition: none !important;
    animation: none !important;
  }
}

/* Cuarenta y cuatro píxeles: el mínimo por debajo del cual un objetivo táctil
   deja de acertarse con el pulgar. Medía 38. */
.btn-icono {
  width: 44px; height: 44px;
  border: none; border-radius: 50%;
  background: var(--alzado);
  color: var(--texto-tenue);
  font-size: 18px;
  flex: none;
}
.btn-icono:active { background: var(--alzado-alto); }

/* ── Ajustes ────────────────────────────────────────────────────────────── */
.panel-ajustes .fila-ajuste:first-of-type { padding-top: 6px; }
.fila-ajuste {
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 0;
  border-bottom: 1px solid var(--linea);
  font-size: 15px;
}
.fila-ajuste:last-of-type { border-bottom: none; }
/* Interruptor dibujado, no la casilla del sistema. La nativa se pintaba con
   `accent-color`: un cuadrado ámbar de esquinas vivas con una marca blanca
   dentro, que en una interfaz cuyos radios van de 4 a 20 px era la única
   esquina recta de la pantalla y, siendo el elemento más saturado del panel,
   la primera cosa que se miraba al abrir los ajustes.

   Es una pastilla con un pomo, del tamaño mínimo táctil, y el estado se lee por
   la posición del pomo antes que por el color: quien no distinga el ámbar del
   gris sigue viendo a qué lado está. */
.fila-ajuste input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  margin: 0;
  flex: none;
  width: 46px;
  height: 28px;
  border-radius: var(--radio-pastilla);
  background: var(--alzado-alto);
  position: relative;
  cursor: pointer;
  transition: background var(--normal) ease;
}
.fila-ajuste input[type="checkbox"]::after {
  content: '';
  position: absolute;
  top: 3px; left: 3px;
  width: 22px; height: 22px;
  border-radius: 50%;
  background: var(--texto-tenue);
  transition: transform var(--normal) cubic-bezier(0.2, 0.8, 0.2, 1),
              background var(--normal) ease;
}
.fila-ajuste input[type="checkbox"]:checked { background: var(--local-hondo); }
.fila-ajuste input[type="checkbox"]:checked::after {
  background: var(--local);
  transform: translateX(18px);
}

.fila-botones { display: flex; gap: 10px; margin-top: 12px; }

.btn-menor {
  flex: 1;
  padding: 13px 12px;
  border: none;
  border-radius: var(--radio-chico);
  background: var(--alzado);
  color: var(--texto);
  font-size: 14px;
  font-weight: 600;
}
.btn-menor:active { background: var(--alzado-alto); }
.btn-menor.ancho { width: 100%; margin-top: 10px; display: block; }
.btn-menor[disabled] { color: var(--texto-sutil); }

.nota-apk { margin: 8px 0 0; color: var(--texto-tenue); font-size: 12.5px; line-height: 1.45; }
.nota-apk:empty { display: none; }

/* ── Conversación ───────────────────────────────────────────────────────── */
.chat {
  flex: 1;
  overflow-y: auto;
  padding: 16px 14px 8px;
  display: flex; flex-direction: column; gap: 12px;
  -webkit-overflow-scrolling: touch;
}

/* La conversación crece **desde abajo**: el último mensaje queda pegado al
   compositor y el hueco cae arriba, donde no se cruza con el pulgar. Antes se
   anclaba arriba y con cuatro turnos quedaban trescientos píxeles de vacío
   entre lo último dicho y el sitio donde se escribe.

   El margen automático en el primer hijo y no `justify-content: flex-end` en el
   contenedor: con desbordamiento, el segundo recorta el principio de la lista y
   deja los mensajes viejos inalcanzables. */
.chat > :first-child { margin-top: auto; }

/* Chat de dos lados: tú a la derecha, JARVIS a la izquierda. Las dos burbujas
   se distinguen solo por su luminosidad — **ninguna** usa el ámbar, que queda
   reservado a la procedencia. Teñir de acento la burbuja del usuario, que es lo
   que hace medio mundo, rompería esa reserva. */
/* El radio es el de la tarjeta (`--radio-chico`), no el del panel: una burbuja
   va dentro de la conversación, no la contiene. Con `--radio` valía lo mismo
   que el panel de ajustes que la envuelve, y era además el doble que la
   burbuja del escritorio, así que el mismo mensaje tenía dos formas según
   dónde se leyera. */
.burbuja {
  max-width: 82%;
  padding: 12px 15px;
  border-radius: var(--radio-chico);
  font-size: 15.5px;
  line-height: 1.5;
  word-wrap: break-word;
  white-space: pre-wrap;
}

.burbuja.usuario {
  align-self: flex-end;
  background: var(--alzado-alto);
  border-bottom-right-radius: var(--radio-minimo);
}

.burbuja.jarvis {
  align-self: flex-start;
  background: var(--alzado);
  border-bottom-left-radius: var(--radio-minimo);
}

/* El sistema no es un interlocutor: ni burbuja ni lado. */
.burbuja.sistema {
  align-self: center;
  max-width: 92%;
  background: transparent;
  color: var(--texto-sutil);
  font-size: 13px;
  text-align: center;
  padding: 4px 8px;
}

.burbuja.fallo {
  align-self: flex-start;
  background: var(--fallo-velo);
  color: var(--fallo);
}

.burbuja.copiada { outline: 2px solid var(--local); outline-offset: 2px; }
.burbuja:not(.sistema) { cursor: pointer; }

/* La atribución la lleva el lado, igual que en el escritorio: tú a la derecha,
   JARVIS a la izquierda. Repetir el nombre encima de cada burbuja no añadía
   nada, y la de JARVIS lo escribía **en ámbar**, que en esta interfaz significa
   «se resolvió en tu máquina». Gastado en un rótulo, el color deja de poder
   afirmar eso. El fallo sí conserva su etiqueta: ahí el color es el aviso.

   El nodo se sigue emitiendo desde `app.js`; se oculta aquí para no tocar el
   contrato del marcado. */
.etiqueta {
  display: block;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--texto-sutil);
  margin-bottom: 5px;
}
.burbuja.usuario .etiqueta,
.burbuja.jarvis .etiqueta { display: none; }
.burbuja.fallo .etiqueta { color: var(--fallo); }

/* Sello de tiempo de bloque. Sustituye a la hora bajo cada burbuja, que en una
   conversación normal escribía la misma cifra cuatro o seis veces, un renglón
   cada una, compitiendo con el texto. Solo aparece cuando hay salto real —más
   de cinco minutos— o cambio de día. La hora exacta de un mensaje concreto sigue
   estando: en su atributo `title`, que es lo que enseña la pulsación larga.

   Mono, como todo lo que la máquina midió: es la frontera tipográfica del
   sistema de diseño, mono para el dato y Hanken para el lenguaje. */
.sello-hora {
  align-self: center;
  font-family: var(--letra-mono);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--texto-sutil);
  padding: 6px 0 2px;
}

/* ── Registro de acciones ───────────────────────────────────────────────── */
/* La misma anatomía que la fila del raíl de la ventana: punto de estado,
   herramienta en mono, sujeto, duración y procedencia. Aquí llegaba como una
   línea gris centrada con la etiqueta «sistema» encima y la hora debajo: tres
   renglones para decir menos que esto en dos, y sin el dato —la procedencia—
   que sostiene el argumento entero del producto.

   No lleva lado: no es un interlocutor, es lo que la máquina hizo. */
.registro {
  align-self: stretch;
  display: grid;
  grid-template-columns: 8px 1fr auto;
  align-items: center;
  gap: 10px;
  padding: 11px 13px;
  background: var(--alzado);
  border-radius: var(--radio-chico);
}

/* El punto codifica **estado**, nunca procedencia: la procedencia ya la dice la
   insignia del final, y dos codificaciones para el mismo píxel dejan al lector
   sin saber cuál está mirando. Mismo vocabulario que `JOB_STATE_TOKENS`. */
.registro-punto {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--texto-sutil);
}
.registro[data-estado="running"] .registro-punto { background: var(--local); }
.registro[data-estado="completed"] .registro-punto { background: var(--ok); }
.registro[data-estado="failed"] .registro-punto { background: var(--fallo); }
.registro[data-estado="cancelled"] .registro-punto { background: var(--aviso); }

.registro-cuerpo { min-width: 0; }
.registro-cuerpo b {
  display: block;
  font-family: var(--letra-mono);
  font-size: 12.5px;
  font-weight: 700;
  color: var(--texto);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.registro-cuerpo em {
  display: block;
  font-family: var(--letra-mono);
  font-style: normal;
  font-size: 11.5px;
  color: var(--texto-sutil);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.registro-cuerpo em:empty { display: none; }

.registro-cola { display: flex; flex-direction: column; align-items: flex-end; gap: 3px; }
.registro-metrica {
  font-family: var(--letra-mono);
  font-size: 11px;
  color: var(--texto-tenue);
  font-variant-numeric: tabular-nums;
}
/* Sin procedencia declarada no se pinta insignia: afirmar «local» sobre algo que
   quizá salió a Internet es peor que no afirmar nada. */
.registro-origen {
  font-family: var(--letra-mono);
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  padding: 2px 6px;
  border-radius: var(--radio-minimo);
}
.registro-origen:empty { display: none; }
.registro-origen[data-origen="local"] { color: var(--local-suave); background: var(--local-hondo); }
.registro-origen[data-origen="net"] { color: var(--red-suave); background: var(--red-velo); }

/* ── Estado vacío ───────────────────────────────────────────────────────── */
/* Sin conversación todavía, cuatro órdenes de ejemplo bajo la esfera, en una
   rejilla de dos columnas iguales. Ya no lleva logo: la esfera de encima es la
   presencia y la marca a la vez (spec §1.3). */
.vacio { padding: 6px 20px 10px; }
.sugerencias {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 9px;
  width: 100%;
  max-width: 420px;
  margin: 0 auto;
}
.chip {
  padding: 13px 14px;
  border: 1px solid var(--escenario-linea);
  border-radius: var(--radio-chico);
  background: var(--escenario-campo);
  color: var(--escenario-suave);
  font: inherit;
  font-size: 14px;
  min-height: 44px;
  transition: background var(--rapido) ease, color var(--rapido) ease;
}
.chip:active { color: var(--escenario-texto); }

/* ── Encendido remoto ───────────────────────────────────────────────────── */
/* Sin filete lateral de color. Un borde grueso pegado al canto izquierdo de
   una tarjeta redondeada corta la curva y delata el rectángulo de debajo, y es
   además el tic más reconocible de la interfaz generada a granel. Lo que avisa
   de que algo va mal es el punto ámbar del título, que es donde ya está
   mirando quien lee. */
.panel-encendido {
  margin: 12px 14px 0;
  padding: 16px;
  background: var(--chasis);
  border-radius: var(--radio);
}
.encendido-titulo {
  margin: 0;
  font-size: 15px;
  font-weight: 700;
  display: flex; align-items: center; gap: 9px;
}
.encendido-titulo::before {
  content: '';
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--aviso);
  box-shadow: 0 0 0 3px var(--aviso-velo);
  flex: none;
}
.encendido-texto { margin: 6px 0 0; color: var(--texto-tenue); font-size: 13.5px; line-height: 1.45; }
.encendido-aviso { margin: 10px 16px 0; color: var(--aviso); font-size: 13px; }
.encendido-aviso:empty { display: none; }
/* Con el PC caído, el panel empuja «Hablar» hacia abajo, y a 844 px la barra de
   órdenes acababa debajo de las pestañas (capturas de la Task 10). Sobran las
   sugerencias, que no hay a quién mandar, y la tarjeta «Ahora», que ya no se
   refresca y diría que suena algo en un PC apagado. Vuelven con la línea. */
.panel-encendido:not(.oculta) ~ .paneles #estado-vacio,
.panel-encendido:not(.oculta) ~ .paneles #ahora-compacta { display: none; }

/* ── Barra de órdenes ───────────────────────────────────────────────────── */
/* «›» y el campo. Sin micrófono desde el 2026-10-05: se dicta tocando la
   esfera, que es el botón de hablar. La clase sigue siendo `compositor` porque
   `app.js` la busca y le pone `con-texto`; el botón de enviar solo aparece
   entonces. */
.compositor {
  flex: none;
  display: flex; align-items: center; gap: 10px;
  height: 52px;
  margin: 0 16px 12px;
  padding: 0 5px 0 16px;
  background: var(--escenario-campo);
  border: 1px solid var(--escenario-linea);
  border-radius: 14px;
}
.prompt { font-family: var(--letra-mono); font-size: 16px; color: var(--acento-b); }
.campo-texto {
  flex: 1; min-width: 0;
  background: transparent; border: none; color: var(--escenario-texto);
  /* 16 y no los 15 del lienzo: por debajo, iOS amplía la página al enfocar. */
  font: inherit; font-size: 16px;
  padding: 12px 0;
}
.campo-texto::placeholder { color: var(--escenario-tenue); }
.campo-texto:focus { outline: none; }

/* 44 y no los 42 del lienzo: es el mínimo táctil del resto de la app. */
.btn-enviar {
  width: 44px; height: 44px; flex: none;
  border: none; border-radius: 12px;
  place-items: center;
  display: none; background: var(--escenario-linea);
}
.btn-enviar svg { width: 20px; height: 20px; fill: var(--escenario-texto); }
.compositor.con-texto .btn-enviar { display: grid; }

/* ── Accesibilidad ──────────────────────────────────────────────────────── */
:focus-visible { outline: 2px solid var(--local); outline-offset: 2px; }

/* ── Paneles y pestañas ─────────────────────────────────────────────────── */
/* Cuatro paneles, uno a la vista. `oculta` los apaga, como siempre. */
.paneles { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.panel { flex: 1; min-height: 0; display: flex; flex-direction: column; }
#panel-historial .chat { flex: 1; }
.panel-lista { overflow-y: auto; padding: 8px 16px 24px; gap: 12px; }
/* En columna nada crece: `.btn-menor` lleva `flex: 1` para repartirse una fila
   de botones, y aquí lo estiraría a lo alto hasta llenar el panel. */
.panel-lista > * { flex: none; }
.titulo-panel { margin: 4px 2px 6px; font-family: var(--letra-titular); font-size: 22px; font-weight: 600; letter-spacing: -0.01em; }
.nota { margin: 0; color: var(--texto-tenue); font-size: 13.5px; line-height: 1.45; }
.nota:empty { display: none; }

/* Pestañas: Hablar · Historial · Ahora · Ajustes. 70 px como en el lienzo,
   con el borde de la zona segura debajo. */
.pestanas {
  flex: none;
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  height: calc(70px + var(--seguro-abajo));
  padding: 0 8px var(--seguro-abajo);
  background: var(--chasis);
  border-top: 1px solid var(--linea);
}
.pestana {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  border: none; background: transparent; color: var(--texto-sutil);
  font: inherit; font-size: 11px; font-weight: 500;
}
.pestana.activa { color: var(--texto); font-weight: 600; }

/* ── Escenario ──────────────────────────────────────────────────────────── */
/* Hablar es escenario: oscuro en los dos temas, con el resplandor del lienzo
   detrás de la esfera. */
.escenario {
  background: radial-gradient(ellipse at 50% 28%, var(--escenario-resplandor) 0%, var(--escenario-fondo) 60%);
  color: var(--escenario-texto);
}
/* En el lienzo el resplandor es de la pantalla entera (al 28 % de los 844 px) y
   la cabecera va encima, sin fondo propio. Pintado solo en el panel, la
   cabecera oscura se leía como una franja: el centro saltaba de golpe de
   `--chasis` al resplandor en la línea de los 64 px (capturas de la Task 10).
   Con «Hablar» a la vista, el escenario pasa a la vista entera; en claro la
   cabecera sigue siendo chasis (decisión 17) y lo tapa. */
#vista-chat[data-pestana="hablar"] {
  background: radial-gradient(ellipse at 50% 28%, var(--escenario-resplandor) 0%, var(--escenario-fondo) 60%);
}
#vista-chat[data-pestana="hablar"] .escenario { background: none; }
:root:not([data-tema="claro"]) #vista-chat[data-pestana="hablar"] .cabecera { background: transparent; }

/* La esfera es el botón de hablar (spec §4.2): tocarla es dictar. */
.presencia { flex: none; display: flex; flex-direction: column; align-items: center; gap: 6px; padding-top: 6px; }
.esfera-boton { border: none; padding: 0; background: transparent; border-radius: 50%; cursor: pointer; }
.esfera { display: block; width: 270px; height: 270px; }
.estado-fila { display: flex; align-items: baseline; gap: 10px; }
.estado-titulo { font-family: var(--letra-titular); font-size: 28px; font-weight: 600; letter-spacing: -0.02em; }
.estado-marca { font-family: var(--letra-mono); font-size: 11.5px; color: var(--escenario-tenue); }
.estado-linea { margin: 0; padding: 0 28px; min-height: 1.4em; font-size: 16px; color: var(--escenario-suave); text-align: center; }

/* Subtítulos: tu frase a la derecha en una cápsula con borde; la respuesta de
   JARVIS a la izquierda, en Bricolage, con su procedencia debajo si consta. */
.subtitulos {
  flex: 1; min-height: 0; overflow-y: auto;
  display: flex; flex-direction: column; justify-content: flex-end; gap: 10px;
  padding: 12px 20px;
}
.sub-usuario {
  align-self: flex-end; max-width: 80%; margin: 0;
  padding: 8px 13px; border-radius: 14px;
  border: 1px solid var(--escenario-linea);
  font-size: 14.5px; color: var(--escenario-texto);
}
.sub-jarvis { align-self: flex-start; max-width: 90%; display: flex; flex-direction: column; gap: 5px; }
.sub-respuesta { margin: 0; font-family: var(--letra-titular); font-size: 19px; line-height: 1.35; }
.sub-respuesta[data-fallo="si"] { color: var(--fallo); }
.sub-procedencia {
  display: flex; align-items: center; gap: 6px;
  font-family: var(--letra-mono); font-size: 11px;
}
.sub-procedencia::before { content: ''; width: 6px; height: 6px; border-radius: 50%; flex: none; }
.sub-procedencia[data-origen="local"] { color: var(--escenario-local); }
.sub-procedencia[data-origen="local"]::before { background: var(--local); }
.sub-procedencia[data-origen="net"] { color: var(--escenario-red); }
.sub-procedencia[data-origen="net"]::before { background: var(--red); }

/* Tarjeta «Ahora» compacta: lo que suena y su botón, o el próximo aviso. */
.ahora-compacta {
  flex: none;
  display: flex; align-items: center; gap: 12px;
  margin: 0 16px 10px; padding: 10px 12px 10px 14px;
  border-radius: var(--radio-tarjeta);
  background: var(--escenario-campo);
  border: 1px solid var(--escenario-linea);
}
.compacta-texto { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.rotulo { font-family: var(--letra-mono); font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--escenario-tenue); }
.compacta-titulo { font-family: var(--letra-titular); font-size: 15px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.btn-redondo { width: 44px; height: 44px; flex: none; border: none; border-radius: 50%; display: grid; place-items: center; }
.btn-redondo.claro { background: var(--escenario-texto); color: var(--escenario-fondo); }
.btn-redondo .ics { fill: currentColor; stroke: none; }
.ahora-compacta[data-tipo="aviso"] .btn-redondo { display: none; }

/* Sin parámetros no hay esfera: queda el hueco, y el estado en palabras. */
.esfera-boton.sin-esfera .esfera { visibility: hidden; }

/* ── «Ahora» ────────────────────────────────────────────────────────────── */
/* Las tarjetas del panel del PC (`gui/widgets/panel_ahora.py`) sobre el chasis
   del tema. Una tarjeta sin datos se oculta; no se rellena con nada inventado. */
.tarjeta {
  display: flex; flex-direction: column; gap: 4px;
  padding: 14px 16px;
  background: var(--alzado);
  border-radius: var(--radio-tarjeta);
}
.tarjeta .rotulo { color: var(--texto-sutil); }
.tarjeta-titulo { font-family: var(--letra-titular); font-size: 17px; font-weight: 600; }
.tarjeta-detalle { color: var(--texto-tenue); font-size: 14px; }
.tarjeta-hora { font-family: var(--letra-mono); font-size: 12.5px; color: var(--local-suave); }
.tarjeta-cifra { font-family: var(--letra-titular); font-size: 30px; font-weight: 600; letter-spacing: -0.02em; }
.tarjeta-origen { font-family: var(--letra-mono); font-size: 11px; }
.tarjeta-origen[data-origen="net"] { color: var(--red-suave); }
.controles-musica { display: flex; gap: 10px; margin-top: 8px; }
.controles-musica .btn-icono { display: grid; place-items: center; background: var(--alzado-alto); color: var(--texto); }
.controles-musica .btn-icono.principal { background: var(--texto); color: var(--chasis); }
.lista-equipos { list-style: none; margin: 4px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; font-size: 14px; }
.lista-equipos li { display: flex; align-items: center; gap: 8px; }
.lista-equipos li::before { content: ''; width: 7px; height: 7px; border-radius: 50%; background: var(--texto-sutil); flex: none; }
.lista-equipos li[data-estado="activo"]::before { background: var(--ok); }
.lista-equipos li[data-estado="enlazado"]::before { background: var(--red); }
/* Una tecla de música que no llegó se dice, sin modal ni bloqueo. */
.aviso-musica { color: var(--fallo); padding: 0 2px; }

/* ── Historial vacío ────────────────────────────────────────────────────── */
/* Una línea centrada sobre el hueco, en vez de un panel negro que no dice si
   está vacío o roto. `#chat` crece desde abajo y ocupa el panel entero, así
   que la nota va encima, sin quitarle sitio. */
#panel-historial { position: relative; }
.historial-vacio {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  padding: 0 28px; text-align: center;
  pointer-events: none;
}

/* ── Ajustes: tema ──────────────────────────────────────────────────────── */
/* Sistema · Oscuro · Claro, en un control segmentado de 44 px. */
.segmentado { display: flex; gap: 3px; padding: 3px; border-radius: var(--radio-pastilla); background: var(--alzado); }
.segmento {
  min-height: 38px; padding: 0 12px;
  border: none; border-radius: var(--radio-pastilla);
  background: transparent; color: var(--texto-sutil);
  font: inherit; font-size: 13px; font-weight: 600;
}
/* En claro `--alzado-alto` sobre `--alzado` es casi blanco sobre blanco: el
   canto y la sombra dicen cuál está elegido también ahí. */
.segmento.activa {
  background: var(--alzado-alto); color: var(--texto);
  box-shadow: inset 0 0 0 1px var(--linea), 0 1px 3px var(--sombra);
}
