/* ============================================================
   RPH OS — estilos LOCALES de la aplicación.
   Prefijo .os- para no tocar nunca el namespace .r- del sistema.
   Regla: aquí no nace ningún color ni tamaño tipográfico nuevo;
   todo sale de tokens.css. Lo único propio son medidas de layout
   del chasis (ancho del rail, del sidebar, alto de la barra).
   ============================================================ */

/* ---------- La reserva del chasis ----------
   Aquí vivían TRES reservas —izquierda, derecha y dock inferior— porque el rail
   era una cápsula que se podía mover a cualquiera de los tres cantos. El chasis
   nuevo son ORBES sueltos anclados al canto derecho, así que hoy exactamente una
   de las tres vale algo. Las tres se quedan declaradas y ese no es un descuido:

   · Quien necesita espacio sigue sumando el lado que le toca y NUNCA pregunta
     dónde está el chasis. Hoy el único que lo lee es el CENTRO, por la vía del
     design system (`--flyout-rail`); las tres segundas páginas de los módulos
     —la nota, la hoja de tarea y la ficha de persona— dejaron de leerlo el día
     que se mudaron adentro del panel, que es lo que las hace medir sin saber
     dónde está nada. Este renglón decía lo contrario y era falso: `.wk-side` y
     `.rel-side` ya no lo tocaban, y `.os-side` lo leía justamente para pintar
     donde no debía.
   · La posición sigue siendo un ajuste y hoy tiene un solo valor (`right`).
     Arriba y abajo se prueban después, y volver a abrirlas es escribir su
     bloque `[data-rail="…"]` aquí y su opción en Ajustes. Borrar las perillas
     obligaría a reinventarlas.

   Viven en `:root` y NO en `.os-shell`, a propósito: una custom property
   hereda por el DOM y no por el `position: fixed`, así que en la raíz la puede
   leer cualquier cosa, esté montada donde esté. Es lo que costó averiguar
   cuando los paneles de módulo vivían colgados del `<body>`.

   El número NO se copia: sale de `--orb-reserve`, que tokens.css deriva del
   tamaño del orbe (44 + 20 de inset + 16 de aire = 80). Si el orbe crece, todo
   lo que le deja lugar se corre solo. */
:root {
  --os-rail-w: var(--orb-reserve);
  --os-rail-l: 0px;
  --os-rail-r: var(--os-rail-w);
  --os-rail-b: 0px;
  --os-safe-top: env(safe-area-inset-top, 0px);
  --os-safe-right: env(safe-area-inset-right, 0px);
  --os-safe-bottom: env(safe-area-inset-bottom, 0px);
  --os-safe-left: env(safe-area-inset-left, 0px);
}

/* La única posición implementada. Pesa lo mismo que el `:root` de arriba y
   viene después, así que gana sin trucos de especificidad — que es exactamente
   la prioridad que hará falta el día que haya dos posiciones. */
[data-rail="right"] {
  --os-rail-l: 0px;
  --os-rail-r: var(--os-rail-w);
  --os-rail-b: 0px;
}

/* LÁPIDA: `--os-head` y `--os-side`.

   `--os-head` (60px) era el alto de la cabecera de un panel lateral, y antes
   de eso el de la barra superior del OS. Las tres segundas páginas se mudaron
   adentro del panel flotante y sus cabeceras dejaron de tener alto fijo: son
   un renglón que mide lo que mide su contenido, como `.rel-side__top`.

   `--os-side` (520px) era el ancho de la gaveta de nota anclada a la ventana.
   Ese ancho hoy lo da el panel —y es el ancho que Robbie eligió arrastrando—,
   así que un segundo número que dijera lo mismo solo podía contradecirlo: de
   hecho lo contradecía en 80px y por eso la nota tapaba la conversación.

   Los dos se borran en vez de dejarse declarados: un token que nadie lee se
   vuelve a leer. */

/* ---------- Chasis ---------- */

/* `hidden` tiene que ganarle a cualquier display de clase: el sidebar
   y el menú de bloques declaran flex/grid y sin esto aparecerían solos. */
[hidden] { display: none !important; }

/* El OS no scrollea el documento. Un popover mal cerrado en el `<body>`
   (Intel filtros/alta) estiraba la ventana hacia abajo y WKWebView
   reventaba. `.os-shell` ya recorta; el documento también. */
html, body {
  height: 100%;
  overflow: hidden;
}

/* ---------- EL HOST DEL CHASIS ----------
   `.os-shell` es lo que el contrato del design system (§ 33) llama el host: el
   padre directo de `.r-flyout`, posicionado contra la ventana visible, donde ron.js escribe
   `data-flyout-open`, `data-flyout-resizing` y `--flyout-w`. De ahí los leen el
   panel, el campo de color y el centro.

   Mide EXACTAMENTE la ventana y no `min-height`, y eso no es cosmética: el
   panel se posiciona contra esta caja (`top`/`bottom` de --flyout-gap), así que
   un host que crece con su contenido dejaría el panel midiendo el documento en
   vez de la pantalla.

   `overflow: hidden` porque el OS no scrollea: scrollea el log de la
   conversación y scrollea el cuerpo del panel. Las segundas páginas de los
   módulos (`.os-side`, `.rel-side`, `.wk-side`) viven ADENTRO del panel, así
   que su recorte es el del flotante y este no las alcanza.

   El fondo es TRANSPARENTE, sin condición. Antes decía `var(--canvas)` y una
   regla espejo lo abría a transparente cuando había tapiz; hoy el tapiz no se
   puede apagar, así que la condición sobraba y las dos reglas eran una. */
.os-shell {
  position: fixed;
  inset-inline: 0;
  top: var(--os-vv-top, 0px);
  height: 100vh;
  height: var(--os-vv-h, 100dvh);
  overflow: hidden;
  background: transparent;
}

/* ---------- EL CENTRO ----------
   La conversación no es una vista: es el piso del OS. Ocupa todo y se recoge al
   abrir un panel.

   La geometría es la que declara el design system —se copia, no se inventa— y
   la transición va en `padding` y NO en `width`: así el log y la caja de prompt
   se remiden solos y el texto refluye MIENTRAS el panel entra, en vez de
   reacomodarse de golpe al final. */
.os-center {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  --os-libre-end: calc(var(--flyout-rail) + var(--flyout-air));
  padding-inline: var(--flyout-pad) var(--os-libre-end);
  /* --s-8 (32px) y no --s-4: con TitleBarStyle::Overlay el contenido llega al
     canto de la ventana, y los 28px de arriba son la franja por donde se
     arrastra (`.os-drag`, ver abajo). Con 16px de reserva, #chatPick, #osStat
     y #chatNew cruzaban esa frontera —medido a 1440x900— y se peleaban el
     clic con el arrastre. 32 = 28 de franja + 4 de aire. */
  padding-block: var(--s-8) var(--s-5);
  transition: padding var(--d-slow) var(--ease);
}
[data-flyout-open] .os-center {
  --os-libre-end: calc(var(--flyout-rail) + var(--flyout-w) + var(--flyout-air));
  padding-inline-end: var(--os-libre-end);
}
/* Mientras se arrastra el canto NADA interpola: el panel sigue al dedo y el
   centro con él. El sistema apaga las suyas con el mismo atributo. */
[data-flyout-resizing] .os-center { transition: none; }

/* ---------- La columna, centrada en la región libre ----------
   La región del centro es todo lo que queda a la IZQUIERDA del panel, y cambia
   de ancho cuando el panel abre, cierra o se arrastra. La columna se recentra
   con ella: sin panel queda centrada en la ventana, con panel queda centrada en
   lo que sobra.

   El techo es `--measure`, la medida de lectura del sistema, y NO `--page-max`.
   En el ultrawide de 3440 la conversación se estiraba a 2,700px de renglón
   —4.5 veces la medida— y un renglón así es ilegible por el lado contrario: el
   ojo pierde la línea al regresar.

   El mismo número es el PISO, y de eso se encarga ron.js: si el mínimo de un
   módulo no cabe junto a la medida de lectura, el que gana es el piso. El
   centro está siempre; el panel es un visitante.

   Que la columna se MUEVA al abrir es la función y no un defecto —el espacio
   cambió, el contenido se reacomoda— y por eso viaja con las curvas del
   sistema en vez de saltar.

   ── EN ULTRA ANCHO SOBRA MUCHO, Y ESO ESTÁ BIEN ─────────────────────
   Se auditó y el número asusta: a 2560px de ancho, con Workspace abierto, 976px
   (38%) de la ventana no son ni conversación ni panel; con Lever, 1,366px (53%).
   Lo que la medición dice al mirarla de cerca es que no hay nada que reparar, y
   se anota AQUÍ para que la próxima auditoría no vuelva a abrirlo:

     · La conversación mide 534px, que es `--measure` EXACTA (64ch). Crecer está
       prohibido dos párrafos más arriba, y con número: 4.5× la medida es
       ilegible por el lado contrario.
     · El panel mide su `roomy` declarado —970 en Workspace, 580 en Lever— y
       `roomy` es, por definición, «hasta dónde MÁS ancho sigue ayudando».
       Los 970 de Workspace son exactamente donde aterriza la quinta columna del
       Kanban: 1,200 no le compran una sexta, le compran desierto.
     · El hueco YA está repartido parejo. Medido en los dos temas: Workspace
       492px a la izquierda contra 484 a la derecha, Lever 687 contra 679 (los
       8px de diferencia son `--flyout-pad` 24 contra `--flyout-air` 16). La
       auditoría reportó 687/289 para Workspace y de ahí salió «pegada a la
       izquierda»; el total era correcto, el reparto no. La columna está
       centrada, y lo hace el `margin-inline: auto` de aquí abajo.

   O sea que las dos superficies están en su máximo DISEÑADO y lo que queda no
   es espacio sin asignar: es el tapiz, que además es lo que el papel translúcido
   del panel necesita para ser vidrio y no un rectángulo gris. Llenarlo porque
   hay pantalla es justo lo que `CLAUDE.md` prohíbe —«el tamaño se gana»—, así
   que la respuesta a esta auditoría fue no cambiar ni un token. */
.os-col {
  width: 100%;
  max-width: var(--measure);
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1 1 auto;
  gap: var(--s-3);
}

/* ---------- LO QUE SE FUE CON LA BARRA SUPERIOR ----------
   `.os-top` existía para una sola cosa: decir el nombre de la vista
   (`.os-where`). Hoy ese nombre lo dice la cabecera del panel, una vez, y una
   barra que cruza la pantalla para no decir nada es la definición de clutter.
   Con ella se fueron `.os-main` (la columna que empezaba después del rail) y
   el `margin-inline` negativo que la sacaba hasta el canto.

   Los tres inquilinos que tenía, y dónde quedaron:

     · `.os-where`, el nombre de la vista  → `.r-flyout__t`, la cabecera del panel.
     · `.os-stat`, el indicador de estado  → el renglón de conversación del
       centro (`.os-chats`), que es el único chrome permanente que queda. Sus
       estilos siguen aquí abajo, intactos: solo cambió de vecino.
     · `.os-gear`, el botón de Ajustes     → el último orbe de la columna,
       separado por aire. Su regla de activo se fue con él: el orbe activo lo
       resuelve el sistema (`.r-orb[aria-current]`).

   También se fueron `.os-railwrap` (la envoltura de la cápsula) y `.os-invoke`
   (el rayo del rail). La cápsula la reemplazan los orbes, que son del sistema y
   traen su propia colocación; el invocador se quedó sin trabajo el día que la
   caja de prompt pasó a estar siempre puesta. */

.os-view { flex: 1 1 auto; min-height: 0; }

/* ---------- LA PÁGINA DE UN MÓDULO, DENTRO DEL PANEL ----------
   `.os-page` era la caja de una vista a pantalla completa: 1240 de techo,
   centrada, con 32 de relleno arriba y 80 de cola. Hoy toda vista vive dentro
   del cuerpo del panel flotante, que ya pone su propio relleno y su propio
   ancho, así que la página no tiene nada que medir: es un bloque y ya.

   Se queda como clase y no se borra porque cinco módulos la escriben desde su
   JS (`intel.js`, `growth.js`, `lever.js`, `workspace.js` y el `#finRoot` de
   `finance.js`), y porque es el punto donde un módulo hereda la caja del panel
   sin saber que está en un panel — que es justamente lo que hace que no haya
   que reescribirlos para mudarlos.

   `min-width: 0` es lo único que agrega, y es lo que evita el bug clásico:
   una tabla o una tira de chips dentro de un flex hijo desborda el panel
   entero si nadie la deja encogerse. */
.os-page {
  min-width: 0;
  max-width: none;
  margin-inline: 0;
  padding: 0;
}

/* ---------- LÁPIDA: los tres modificadores de página ----------
   `--narrow` (techo de 860), `--start` (angosta pero en la plomada del OS) y
   `--settings` (sin la cola de scroll) resolvían los tres el mismo problema:
   acomodar una vista dentro de una página de 1240 que casi nunca se llenaba.
   Dentro de un panel de 380–620 el ancho ya viene dado y los tres se volvieron
   letra muerta el mismo día. Se borran en vez de dejarse como no-ops: una clase
   que no hace nada se sigue copiando.

   Sus dos consumidores estaban en index.html (`#finRoot` y la página de
   Ajustes) y se fueron con ellos. Si algún día un módulo necesita ser más
   angosto que su panel, el número que se mueve es su `comfy` en el registro de
   app.js, no un techo aquí. */

/* ---------- LAS VISTAS, DENTRO DEL PANEL ----------
   Las siete siguen siendo `.os-view` y siguen escondiéndose con `hidden`: lo
   único que cambió es la caja que las contiene. Una sola a la vez, como
   siempre — un panel a la vez es la regla del rediseño.

   La entrada NO se anima aquí, y es para que no se anime dos veces: el sistema
   ya mueve el contenido al cambiar de módulo (`data-flyout-swap` en el cuerpo
   del panel) y al abrirlo (el panel entero entra con su rebote). Una tercera
   animación sobre la misma pintura se lee como titubeo. La regla base de
   `.os-view` se queda intacta para quien la use fuera del panel. */
.r-flyout__body .os-view { animation: none; }

/* ---------- Indicador de estado (uno solo) ----------
   La barra tenía dos puntos cuadrados pegados —disco e IA—, uno de ellos sin
   etiqueta. Ahora es un botón: el punto muestra el estado más urgente, las
   palabras aparecen solo cuando algo se rompe, y el desglose de TODAS las
   fuentes se revela al hover o al foco en un panel que flota, así que la barra
   no se mueve. Una fuente nueva es un renglón más del panel, no otro elemento
   en la barra. */
.os-stat { position: relative; padding-inline: var(--s-3); flex: 0 0 auto; }
.os-stat .r-status { gap: var(--s-2); }
/* Vacío en reposo, así que no ocupa nada y no hace falta esconderlo. Cuando
   habla, se recorta antes de empujar al resto de la barra. */
.os-stat__t {
  max-width: 34ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* El desglose. Misma receta de papel flotante que .os-wallswatch__tip: nada
   de sombras ni superficies nuevas. Anclado a la derecha del botón para que
   crecer de renglones no lo saque de la pantalla. */
.os-stat__more {
  position: absolute;
  top: calc(100% + var(--s-2));
  right: 0;
  /* CRUDO A PROPÓSITO: es apilamiento interno del botón —el panel encima de su
     propio disparador—, no una posición en la escala de capas del OS. Un
     --z-* aquí diría que este panel negocia con los orbes o con el velo, y no:
     el que negocia es el centro, que lo contiene. */
  z-index: 1;
  display: grid;
  gap: var(--s-1);
  min-width: max-content;
  text-align: left;
  padding: var(--s-2) var(--s-3);
  background: var(--surface);
  border: var(--stroke) solid var(--hairline);
  border-radius: var(--r-md);
  box-shadow: var(--e-2);
  opacity: 0;
  pointer-events: none;
  transform: translateY(calc(var(--s-1) * -1));
  transition: opacity var(--d-fast) var(--ease), transform var(--d-fast) var(--ease);
}
.os-stat:hover .os-stat__more,
.os-stat:focus-visible .os-stat__more { opacity: 1; transform: translateY(0); }

.os-stat__row {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  font-size: var(--t-micro);
}
.os-stat__k { color: var(--ink-3); min-width: 4ch; }
.os-stat__v { color: var(--ink-2); }

/* ---------- La marca de origen: qué copia del OS es ésta ----------
   Una palabra en el renglón de chrome cuando el OS se está sirviendo desde la
   carpeta de trabajo. En la copia publicada no existe: el nodo nace `hidden` y
   solo app.js lo enciende (ver el comentario en index.html).

   Es una regla de UN renglón, y eso es la señal de que la decisión estuvo bien
   tomada: el sistema ya tenía esta pieza. `.r-status` es punto cuadrado + una
   etiqueta corta, que es literalmente lo que hace falta — no se inventó ni un
   componente ni un token.

   POR QUÉ EL TINTE VA EN EL PUNTO Y NO EN LA PALABRA, que es la parte que se
   puede copiar mal: los cuatro semánticos son colores de SEÑAL y ninguno de los
   cuatro aguanta ser texto en los dos temas —`--info` (#2e4bff) da 3.4:1 sobre
   el lienzo cockpit y `--warn` 2.1:1 sobre el claro—. `.r-status` ya resuelve
   eso: el color vive en un cuadro de 7px, donde el listón que aplica es el de
   objeto gráfico (3:1) y no el de texto (4.5:1), y ahí el azul pasa en los dos
   temas —3.4:1 sobre cockpit, 5.3:1 sobre el claro, medido—. La palabra se
   queda en `--ink-2`, que el sistema ya tiene medido. Pintar "trabajo" de azul
   habría sido el mismo dato, ilegible la mitad del día.

   `flex: 0 0 auto` es lo único que hay que decir aquí, y es declaración de
   intención más que necesidad de hoy: el centro se topa en `--measure`, así que
   medido a 760px de ventana con el panel abierto al renglón todavía le sobran
   176px de `.r-spacer` y nada se comprime. Va igual porque `.r-status` es
   `inline-flex` y encogería por omisión; el que debe ceder primero es
   `#chatPick`, que para eso tiene la elipsis. */
.os-origen { flex: 0 0 auto; }

/* ---------- LÁPIDA: `.os-gear` y el bloque WALLPAPER ----------
   `.os-gear[aria-current]` pintaba la pastilla invertida del botón de Ajustes
   en la esquina de la barra. Ajustes es hoy el último orbe y el activo lo
   resuelve el sistema (`.r-orb[aria-current="page"]`, § 33b), con exactamente
   la misma receta —`--surface-invert` / `--ink-invert`— y en un solo lugar.

   El bloque WALLPAPER era una sola regla, `:root:not([data-wall="none"])
   .os-shell { background: transparent }`: abría el lienzo del chasis cuando
   había tapiz encendido. El tapiz ya no se puede apagar —no hay «Ninguno» ni
   `data-wall="none"` en ninguna parte del OS— así que la condición no tenía
   caso que la volviera falsa. La regla se fusionó en `.os-shell`, arriba, sin
   condición: el chasis es transparente y el tapiz se ve, punto.

   Si algún día vuelve un apagador, vuelve el selector: la regla condicional se
   reescribe en dos líneas y el comentario de `.os-shell` dice dónde. */

/* ============================================================
   EL CENTRO — la conversación
   ============================================================ */

/* Aquí vivía `.os-home`, la retícula de dos columnas de la vista Home: la
   conversación a la izquierda y la tarjeta de Inbox a la derecha. Se fue
   completa (ver la lápida del Inbox, más abajo) y su columna de chat ya no
   necesita una clase propia: es `.os-col`, que es la columna del centro y está
   declarada arriba con la geometría del chasis.

   Con ella se fue `.os-chatcol`, que era la columna izquierda de esa retícula y
   además el nodo que el invocador se llevaba de viaje a su recuadro flotante.
   No quedó nadie que la escriba: el invocador se fue y la columna es `.os-col`. */

/* ---------- Historial de conversaciones ----------
   El renglón que vive puesto: en qué conversación estás, el estado del OS y el
   `+`. Todo en tipo de rótulo y tinta secundaria — es la etiqueta del hueco, no
   un título. El nombre del módulo lo dice la cabecera del panel.

   Este renglón es hoy el ÚNICO chrome permanente del OS, y por eso recibió al
   indicador de estado cuando la barra superior se fue. */
.os-chats {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--s-2);
  min-width: 0;
}
.os-chats__now {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  min-width: 0;
  flex: 0 1 auto;
  padding: var(--s-1) var(--s-2);
  margin-left: calc(var(--s-2) * -1);
  border: 0;
  border-radius: var(--r-sm);
  background: none;
  color: var(--ink-3);
  font-family: inherit;
  font-size: var(--t-label);
  transition: color var(--d-fast) var(--ease), background var(--d-fast) var(--ease);
}
.os-chats__now:hover { color: var(--ink); background: var(--idle-bg); }
.os-chats__t {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* El chevron gira: apunta abajo cerrado, arriba abierto. Es la misma señal de
   «esto se abre» que usa el resto del OS, sin una palabra encima. */
.os-chats__v { transition: transform var(--d-fast) var(--ease); }
.os-chats__now[aria-expanded="true"] { color: var(--ink); }
.os-chats__now[aria-expanded="true"] .os-chats__v { transform: rotate(180deg); }

/* La lista ocupa el hueco del log: mismo alto, mismo scroll, mismo aire. */
.os-chatlist {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: var(--s-2) var(--s-1) var(--s-4);
  scrollbar-width: thin;
}

.os-chatrow {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  border-bottom: var(--stroke) solid var(--hairline);
}
.os-chatrow:last-child { border-bottom: 0; }
.os-chatrow__t {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: baseline;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-2);
  border: 0;
  border-radius: var(--r-sm);
  background: none;
  color: var(--ink-2);
  font-family: inherit;
  font-size: var(--t-body-sm);
  line-height: var(--lh-snug);
  text-align: left;
  transition: color var(--d-fast) var(--ease), background var(--d-fast) var(--ease);
}
.os-chatrow__t:hover { background: var(--surface-2); color: var(--ink); }
.os-chatrow--on .os-chatrow__t { color: var(--ink); }
.os-chatrow__n {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Cuántos mensajes y de cuándo: metadata, así que se revela con la fila.

   `pointer-events: none` mientras es invisible, y esta es la que más lo
   necesitaba de todo el archivo: `.os-chatrow__act` NO es texto, son el campo
   de renombrar y el bote de borrar. Con `opacity: 0` a secas siguen siendo
   blanco de clic —invisibles y activos, que es lo peor que puede pasar con un
   control destructivo—. Se devuelve en la misma regla que los revela, así que
   ver y poder tocar son el mismo estado y no dos que se puedan separar. */
.os-chatrow__m,
.os-chatrow__act {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--s-3);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--d-fast) var(--ease);
}
.os-chatrow:hover .os-chatrow__m,
.os-chatrow:focus-within .os-chatrow__m,
.os-chatrow:hover .os-chatrow__act,
.os-chatrow:focus-within .os-chatrow__act { opacity: 1; pointer-events: auto; }
/* La conversación en la que estás no esconde su metadata: es el renglón que se
   está leyendo. */
.os-chatrow--on .os-chatrow__m { opacity: 1; pointer-events: auto; }
.os-chatrow__act { gap: var(--s-1); }

.os-log {
  flex: 1 1 auto;
  min-width: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: var(--s-8);
  padding: var(--s-4) var(--s-2) var(--s-6);
  scrollbar-width: thin;
}

/* Estado vacío: la firma del sistema, número grande y unidad diminuta */
.os-empty {
  margin: auto;
  text-align: center;
  max-width: 44ch;
  display: grid;
  gap: var(--s-3);
  justify-items: center;
}
.os-empty__t {
  font-family: var(--font-display);
  font-weight: var(--fw-light);
  letter-spacing: var(--tr-display);
  font-size: var(--t-h1);
  line-height: var(--lh-snug);
  color: var(--ink);
  margin: 0;
}
.os-empty__s { color: var(--ink-3); font-size: var(--t-body-sm); margin: 0; }
.os-empty__seeds { display: flex; flex-wrap: wrap; gap: var(--s-2); justify-content: center; margin-top: var(--s-2); }

/* Conversación: el mensaje propio ocupa sólo lo que dice y se alinea al
   canto derecho; las respuestas conservan la columna de lectura. La forma
   distingue quién habla sin repetir un punto de estado en cada mensaje. */
.os-msg {
  display: grid;
  gap: var(--s-2);
  flex: 0 0 auto;
  width: 100%;
  min-width: 0;
  position: relative;
}
.os-msg > * { min-width: 0; max-width: 100%; }
/* ST1: el nombre no toma fila — ni al cambiar, ni en líneas de sistema.
   Queda `.r-sr`. La hora vive al hover / foco. */
.os-msg__who {
  position: absolute;
  inset-inline-end: 0;
  top: 0;
  z-index: 1;
  pointer-events: none;
  display: flex;
  align-items: baseline;
  gap: var(--s-2);
  margin: 0;
  font-size: var(--t-micro);
  line-height: var(--lh-body);
  color: var(--ink-3);
}
.os-msg__name { font-weight: var(--fw-medium); }
.os-msg__time {
  opacity: 0;
  transition: opacity var(--d-fast) var(--ease);
}
.os-msg:hover .os-msg__time,
.os-msg:focus-within .os-msg__time { opacity: 1; }
/* ST2: racha del mismo hablante / mismo tramo — se aprieta el aire,
   no los botones de permiso. */
.os-msg--sigue { margin-top: calc(var(--s-2) - var(--s-8)); }
.os-msg--meta .os-msg__body {
  font-size: var(--t-body-sm);
  color: var(--ink-3);
}
.os-msg__body {
  min-width: 0;
  font-size: var(--t-body);
  line-height: var(--lh-body);
  color: var(--ink);
  max-width: var(--measure);
  overflow-wrap: anywhere;
}
.os-msg--me {
  width: fit-content;
  max-width: calc(100% - var(--s-8));
  margin-inline-start: auto;
}
.os-msg--me .os-msg__who {
  order: 2;
  justify-content: flex-end;
  padding-inline: var(--s-2);
}
.os-msg--me .os-msg__body {
  background: var(--surface-2);
  border: var(--stroke) solid var(--hairline);
  border-radius: var(--r-lg);
  padding: var(--s-3) var(--s-4);
  color: var(--ink);
}
.os-msg--ai .os-msg__body { background: none; border: 0; }
.os-msg__body img { max-width: 100%; height: auto; }
@media (hover: none) {
  .os-msg__time { opacity: 1; }
}
.os-msg__body > *:first-child { margin-top: 0; }
.os-msg__body > *:last-child { margin-bottom: 0; }
.os-msg__body p { margin: 0 0 var(--s-3); }
.os-msg__body ul, .os-msg__body ol { margin: 0 0 var(--s-3); padding-left: var(--s-5); }
.os-msg__body li { margin-bottom: var(--s-1); }
.os-msg__body h1, .os-msg__body h2, .os-msg__body h3 {
  font-family: var(--font-display);
  font-weight: var(--fw-light);
  letter-spacing: var(--tr-tight);
  font-size: var(--t-h3);
  margin: var(--s-4) 0 var(--s-2);
}
.os-msg__body code {
  font-family: var(--font-mono);
  font-size: var(--t-body-sm);
  background: var(--surface-sunk);
  border-radius: var(--r-xs);
  padding: 1px var(--s-1);
}
.os-msg__body pre {
  font-family: var(--font-mono);
  font-size: var(--t-body-sm);
  background: var(--surface-sunk);
  border: var(--stroke) solid var(--hairline);
  border-radius: var(--r-md);
  padding: var(--s-3) var(--s-4);
  overflow-x: auto;
  margin: 0 0 var(--s-3);
}
.os-msg__body pre code { background: none; padding: 0; }
.os-msg__cites { display: flex; flex-wrap: wrap; gap: var(--s-2); }

/* Cursor de escritura del modelo */
.os-caret {
  display: inline-block;
  width: 7px; height: 7px;
  border-radius: 1px;
  background: var(--accent);
  margin-left: var(--s-1);
  /* PERIODO, no duración de gesto: `--d-*` miden lo que tarda una cosa en
     llegar, `--d-pulse-*` cada cuánto vuelve. El otro consumidor del mismo
     token es `.r-status--live` en ron.css. */
  animation: r-pulse var(--d-pulse-fast) var(--ease) infinite;
}

/* Composer */
.os-composer {
  flex: 0 0 auto;
  display: grid;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-3);
  /* Recoge el fondo en vez de taparlo: `--surface` es opaca y le ponía a la
     caja un color propio que competía con el tapiz. Frost mate, la familia de
     tarjeta —no `.r-liquid`, que es de controles. */
  background: var(--surface-frost);
  backdrop-filter: blur(var(--frost-blur)) saturate(115%);
  -webkit-backdrop-filter: blur(var(--frost-blur)) saturate(115%);
  border: var(--stroke) solid var(--hairline);
  border-radius: var(--r-xl);
  box-shadow: var(--e-1);

  /* El aura del sistema (`.r-beam` + `data-beam-on`, puestos en el marcado)
     va ENCENDIDA en reposo: aquí la luz es la identidad de la caja, no un
     aviso. Este archivo solo declara CON QUÉ FUERZA — el modo (respirar o
     dar la vuelta) lo manda el sistema y aquí no se declara.

     Por debajo del default (.7) porque este portador está SIEMPRE puesto:
     una luz permanente se juzga por cómo se ve a los diez minutos, no al
     primer vistazo. El estado de trabajo NO sube la fuerza —cambia de
     color— así que hay UNA sola perilla por tema y es esta.

     Sin `--beam-ink`: en reposo el aura se queda con las tres fuentes
     desaturadas del sistema —luz ambiente— y solo colapsa a un color
     cuando de verdad significa algo.

     ---- POR QUÉ LA FUERZA CAMBIA CON EL TEMA, y está medido ----

     El aura vive DEBAJO del texto del prompt (`z-index: -1`), y el
     textarea es transparente: lo único que separa el placeholder del aura
     es el papel de la caja. El sistema afirma —y es cierto— que el CENTRO
     de la caja queda intacto; lo que no cubre es que el renglón de texto
     de una caja ancha y BAJA (617×133) llega hasta los cantos, que es
     justo donde la luz vive. Medido sobre la banda real del placeholder,
     barriendo las tres fuentes por toda la órbita, contra `--ink-3`:

       cockpit  base 3.98 → 3.48 con el aura a .5   (cuesta 0.51)
       claro    base 3.98 → 2.31 con el aura a .5   (cuesta 1.67)  ✗

     No es simetría rota por gusto: en claro el papel es casi blanco, así
     que la luz de color RESTA luminancia en vez de sumarla, y encima
     `--beam-gain` sube a 1.3 sobre papel claro. El mismo número da dos
     efectos opuestos.

     .26 es el punto donde el placeholder vuelve a 3.04 —arriba de 3:1—
     y el aura todavía se ve en el canto. Se probaron .18 (3.31, pero
     casi invisible sobre papel) y .30 (2.91, ya por debajo): el margen
     en claro es estrecho y ese es el hallazgo, no el número. */
  --os-beam-rest: .26;                    /* claro: papel casi blanco */
  --beam-strength: var(--os-beam-rest);
}

/* Trabajando: aquí ya no hay nada que declarar, y ese es el cambio.
   Esta regla era `.os-composer[data-busy] { --beam-ink: var(--ok); }` — la
   app inventando su propio vocabulario para un hecho que no es de la app.
   Cuando el rayo del rail necesitó decir lo mismo, el estado subió al
   sistema con nombre propio (`data-beam-busy`, ron.css § 32a-bis) y de ahí
   salen el color y el ritmo. La app sólo declara el estado, y hoy lo declara
   `setSending()` en app/home.js — la función que arma y desarma el stream, o
   sea el único lugar del OS donde "el modelo está escribiendo" se sabe de
   primera mano. Lo declaraba `paintBeam()` en invoke.js, que lo ADIVINABA
   leyendo el `aria-label` del botón de enviar; con invoke.js borrado el
   atributo se quedó una corrida sin nadie que lo pusiera y el aura dejó de
   decir nada.

   Lo que sigue valiendo, y por eso se queda escrito: la fuerza NO sube al
   trabajar, y eso se decidió midiendo AQUÍ. Un multiplicador de 1.45
   dejaba el placeholder en 2.77 en tema claro —por debajo de 3:1— para
   comprar un brillo que el cambio de tres matices a uno ya estaba dando.
   Sin él, trabajar sale MÁS legible que reposar (el verde pesa menos que el
   rosa de la terna ambiente): claro 3.11 y cockpit 3.54 contra `--ink-3`,
   los dos por encima del reposo. El estado nunca cuesta contraste. */

/* El tema oscuro admite casi el triple: ahí el papel es oscuro, la luz SUMA
   luminancia y `--beam-gain` ya baja a 1. Las dos ramas están porque
   `data-theme` tiene que ganarle a la preferencia del sistema en los DOS
   sentidos — el mismo reparto que usa tokens.css. */
@media (prefers-color-scheme: dark) {
  .os-composer { --os-beam-rest: .5; }
}
:root[data-theme="dark"] .os-composer  { --os-beam-rest: .5; }
:root[data-theme="light"] .os-composer { --os-beam-rest: .26; }
/* El renglón de contexto de la caja. Era un `.r-row` con su `--gap` escrito
   como estilo en línea en el marcado; hoy la fila es de esta clase, que es lo
   que la vuelve una pieza y no una composición que hay que repetir.

   El orden dice de quién es cada cosa: a la izquierda lo tuyo —el modo, las
   notas que entran, la ayuda del prompt— y hasta la derecha lo del agente
   (`viendo · X`), que es la única etiqueta que se gana su lugar en pantalla.
   Los separa un `.r-spacer`, no un margen automático. */
.os-composer__ctx {
  display: none;
  align-items: center;
  gap: var(--s-3);
  flex-wrap: wrap;
  min-width: 0;
}
/* El renglón de contexto aparece al enfocar —o cuando Casa ya pidió el
   teclado. En reposo la caja es solo la barra: textarea + enviar. */
.os-col[data-casa-prompt] .os-composer__ctx,
.os-composer:focus-within .os-composer__ctx {
  display: flex;
}
.os-composer__row { display: flex; align-items: flex-end; gap: var(--s-3); }
.os-prompt {
  /* Una línea, no una caja vacía. `grow()` en home.js estira con el texto. */
  min-height: var(--s-8);
  max-height: 40vh;
  border-radius: var(--r-md);
  background: transparent;
  padding: var(--s-2);
  font-size: var(--t-body);
  line-height: var(--lh-body);
  resize: none;
  overflow-y: auto;
}
.os-prompt:focus { background: transparent; border-color: transparent; }
.os-send { flex: 0 0 auto; width: 44px; padding: 0; height: 44px; border-radius: var(--r-pill); }
.os-ctxchip {
  /* La manita se queda AQUÍ a propósito, contra la regla general de que un
     control de macOS usa flecha. La regla vale cuando hay otra señal de que
     algo se pica —un hover, un borde, un fondo—; esto no la tiene, y era su
     única. Quitarla dejó un elemento clicable indistinguible de un rótulo.
     El arreglo de fondo es darle hover y entonces sí volver a la flecha. */
  cursor: pointer;
}
/* EN CERO NO SE ATENÚA: SE VA. Estuvo en `opacity: 0.45` con el argumento de
   que desaparecer haría bailar el ancho del renglón, y la mitad es cierta —por
   eso se va con opacidad y no con `display`, así la caja se queda y nada se
   mueve—. La otra mitad no: un chip que dice «0» de forma permanente es un
   contador decorativo, que es literalmente lo que `CLAUDE.md` manda quitar. A
   45% seguía diciendo cero, nada más que en gris.
   Es la misma revelación de `.os-hint` y `.os-side__id`: aparece cuando tiene
   algo que decir —hay notas entrando al contexto— y calla cuando no. */
.os-ctxchip--off { opacity: 0; pointer-events: none; }

/* La ayuda del composer aparece al enfocar el prompt —o al tabular hasta
   cualquier control de la caja— y se va sola. El espacio ya estaba
   reservado por el renglón, así que no hay salto. */
.os-hint {
  /* Sin `margin-left: auto`. Lo tenía cuando era el último de la fila; hoy el
     último es `viendo · X` y un margen automático aquí se comería el espacio
     libre ANTES que el `.r-spacer` —en flexbox los márgenes auto se sirven
     primero— y los dos textos micro acabarían pegados en la esquina. */
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--d-fast) var(--ease);
}
.os-composer:focus-within .os-hint { opacity: 1; pointer-events: auto; }

/* ---------- LÁPIDA: LA TARJETA DE INBOX ----------
   Aquí vivía el Inbox de Home: la métrica de puntos con las tareas abiertas
   —la cifra más grande del OS—, la gráfica de capturas de siete días, los
   puntos de cerradas, el campo de captura rápida, la lista de tareas y su
   pie. Ocupaba la segunda columna de Home y se podía doblar.

   Se fue completo por decisión de Robbie, y la razón es la premisa del
   rediseño: el centro del OS es el agente. Un tablero de datos al lado de la
   conversación compite con ella por la atención, y el dato que enseñaba —cuántas
   tareas hay abiertas— es de Workspace, que es el módulo que las opera y que
   ahora abre en un panel a un clic de distancia.

   Lo que se borró, para que no se reimplemente por accidente:

     .os-inbox / __card / __head / __fold / __metric / __rest / __sub /
     __viz / __paper / __foot      la tarjeta y su doblez
     .os-quick                     el campo de captura rápida
     .os-tasks, .os-task*          la lista de tareas y su fila

   Y con ellos las tres perillas que existían solo para medirlos: `--os-inbox`
   (340), `--os-inbox-fold` (224) y `--os-tasks-max` (420).

   Dónde quedó cada función: capturar una tarea sin salir de la conversación es
   `/tarea` en el prompt (ya existía) y la caja de capturar del sistema
   (`.r-capture`); verlas y cerrarlas es Workspace, que tiene su propia fila
   (`.wk-*`) y no necesitaba dos. Si Home vuelve a mostrar algo de tareas, lo que
   muestra es lo que el agente notó hoy — texto en la conversación, no un
   tablero. */

/* ============================================================
   INTELLIGENCE
   ============================================================ */

/* El volumen de contexto en un renglón: tres cifras separadas por aire, sin
   tarjetas ni métricas grandes. La única métrica grande del OS es la del
   inbox. (Aquí acompañaba a `.os-datos`, el inventario de Ajustes, que ya no
   existe: ese pasó a `.r-kv` porque en una tarjeta angosta cuatro pares
   cifra+unidad en un renglón se envolvían desalineados. El patrón sigue siendo
   bueno donde el renglón cabe, que es esta tarjeta ancha.) */
/* Ya no lleva la cifra de notas —esa subió a la métrica de matriz de puntos,
   que es lo que manda en la vista— así que su margen de abajo baja: el aire
   grande lo pone ahora la métrica, y dos aires seguidos abrían un hueco donde
   no hay nada. */
.os-intel__head {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-1) var(--s-4);
  margin: var(--s-1) 0 var(--s-5);
}
.os-intel__bar { margin-bottom: var(--s-4); }
.os-bar__grow { flex: 1 1 auto; }
.os-bar__div { width: var(--stroke); height: 18px; margin: 0; flex: 0 0 auto; }

.os-tagrow { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-bottom: var(--s-5); }
.os-tagrow button {
  /* La manita se queda AQUÍ a propósito, contra la regla general de que un
     control de macOS usa flecha. La regla vale cuando hay otra señal de que
     algo se pica —un hover, un borde, un fondo—; esto no la tiene, y era su
     única. Quitarla dejó un elemento clicable indistinguible de un rótulo.
     El arreglo de fondo es darle hover y entonces sí volver a la flecha. */
  cursor: pointer;
}

.os-notes {
  border-top: var(--stroke) solid var(--hairline);
}
.os-note {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--s-2) var(--s-4);
  align-items: baseline;
  padding: var(--s-4) var(--s-2);
  border-bottom: var(--stroke) solid var(--hairline);
  transition: background var(--d-fast) var(--ease);
}
.os-note:hover { background: var(--surface-2); }
.os-note[aria-current="true"] { background: var(--surface-2); }
.os-note__t {
  font-family: var(--font-display);
  font-weight: var(--fw-light);
  letter-spacing: var(--tr-tight);
  font-size: var(--t-h3);
  color: var(--ink);
  margin: 0;
  display: flex;
  align-items: center;
  gap: var(--s-2);
}
.os-note__s {
  grid-column: 1;
  font-size: var(--t-body-sm);
  color: var(--ink-3);
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.os-note__s mark { background: var(--accent-soft); color: var(--fixed-ink); border-radius: var(--r-xs); }
/* Etiquetas y metadata de la nota: al hover y al foco de la fila (la tarjeta
   lleva tabindex, así que tabular la revela). Opacidad, no display: la
   columna sigue midiendo lo mismo y la lista no se recorre sola. */
.os-note__r {
  grid-row: 1 / span 2;
  grid-column: 2;
  text-align: right;
  display: grid;
  gap: var(--s-2);
  justify-items: end;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--d-fast) var(--ease);
}
.os-note:hover .os-note__r,
.os-note:focus-visible .os-note__r,
.os-note:focus-within .os-note__r,
.os-note[aria-current="true"] .os-note__r { opacity: 1; pointer-events: auto; }
.os-note__tags { display: flex; gap: var(--s-1); flex-wrap: wrap; justify-content: flex-end; }
.os-note__d { font-size: var(--t-micro); color: var(--ink-3); font-variant-numeric: tabular-nums; }

.os-blank {
  padding: var(--s-16) var(--s-4);
  text-align: center;
  display: grid;
  gap: var(--s-3);
  justify-items: center;
}

/* ============================================================
   SIDEBAR DE NOTA — la SEGUNDA PÁGINA de Intelligence
   ============================================================ */

/* ---------- UNA sola gramática para "una capa encima del panel" ----------
   Tres módulos tienen segunda página —la nota de Intelligence, la hoja de
   tarea de Workspace y la ficha de persona de Relaciones— y llegaron al
   rediseño con tres respuestas distintas: `fixed` a la ventana, `absolute`
   cubriendo el flotante, y item de flujo dentro de la vista. Tres respuestas
   a la misma pregunta es lo que hace que el OS se lea como dos productos.

   La que gana es la de Relaciones, y no por antigüedad: es la única que no
   copia geometría. La segunda página es HERMANA de la raíz del módulo, dentro
   de su vista, y se enseña escondiendo la raíz. De ahí salen gratis las cuatro
   cosas que las otras dos perdían:

     · La cabecera del panel se queda a la vista, así que el nombre del módulo
       y su × siguen ahí (`CLAUDE.md`: el nombre se dice UNA vez, y ese lugar
       es la cabecera).
     · La manija que redimensiona sigue alcanzable: no hay ninguna capa nueva
       compitiendo por el estrato.
     · Mide el panel sin leer `--flyout-w` ni `--os-rail-r`: el ancho lo da el
       padre y el arrastre lo sigue solo.
     · Se esconde con la vista, sin código.

   Lo que esta pieza perdía era peor que verse distinta: era `position: fixed`
   anclada a la VENTANA con 520px propios, o sea 80px MÁS ANCHA que el panel de
   Intelligence, empezando 80px a su izquierda. Medido antes del arreglo: a
   1200 ya pintaba encima de la conversación, y a 900 el editor de la nota
   tapaba la CAJA DE PROMPT y la volvía inalcanzable
   (`elementFromPoint` sobre el prompt contestaba `.os-side__body`). El centro
   es el piso del OS; ninguna gaveta se pinta encima de él.

   LÁPIDA DEL PAPEL OPACO: `.os-side` traía `background: var(--surface)` con su
   medición, y el argumento era correcto para lo que entonces era —una gaveta
   con VELO detrás, donde `--ink-2` caía a 3.52 cuando pasaba un bloque oscuro—.
   Dentro del panel no hay velo ni bloque que pase: el fondo es el papel del
   flotante, que es la misma superficie sobre la que leen los otros seis
   módulos y la que el sistema garantiza en 5.5:1 para `--ink-2`. Un rectángulo
   opaco adentro de un panel de vidrio no es papel de lectura, es un parche. */
.os-side {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  /* La misma entrada que la ficha de Relaciones, y por la misma razón: dentro
     del panel los 64px de `os-slide` son ancho que no existe y quedan como
     desborde scrolleable mientras dura la animación. */
  animation: os-slide-in var(--d-base) var(--ease-out);
}

/* Los dos keyframes SE QUEDAN aunque este archivo ya no los use: los consumen
   relations.css (5 usos), workspace.css (3) y growth.css (2), que no son de
   aquí. Borrarlos apagaría la entrada de tres módulos. */
@keyframes os-slide { from { transform: translateX(var(--s-16)); opacity: 0; } }
@keyframes os-in { from { opacity: 0; } }

/* Ya no es la cabecera de una gaveta: es el renglón que devuelve a la lista y
   ofrece fijar y borrar. Sin alto fijo ni relleno lateral —el cuerpo del panel
   ya pone el suyo— y el hairline se queda porque separa la nota de su barra.
   Copiado renglón por renglón de `.rel-side__top`: son la misma pieza. */
.os-side__top {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2);
  padding-bottom: var(--s-3);
  border-bottom: var(--stroke) solid var(--hairline);
}
/* Lo único que scrollea de la nota, y por eso el cuerpo del panel no tiene que
   hacerlo. El relleno lateral se fue con el de la cabecera; la cola de abajo
   baja de --s-16 a --s-8: 64px de cola en una columna de 440 son media
   pantalla vacía. */
.os-side__body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  padding: var(--s-5) 0 var(--s-8);
  scrollbar-width: thin;
}
/* Mismo criterio que la cabecera: el relleno lateral lo pone el cuerpo del
   panel, no la pieza. */
.os-side__foot {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding: var(--s-3) 0 0;
  border-top: var(--stroke) solid var(--hairline);
}

/* El id de la nota y los atajos del editor se revelan: el id al hover o al
   foco de su encabezado, los atajos mientras se escribe en la nota. Los dos
   con opacidad — el pie y la barra ya tienen su altura. */
.os-side__id,
.os-side__hint {
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--d-fast) var(--ease);
}
.os-side__top:hover .os-side__id,
.os-side__top:focus-within .os-side__id,
.os-side__body:focus-within ~ .os-side__foot .os-side__hint,
.os-side__foot:hover .os-side__hint { opacity: 1; pointer-events: auto; }

.os-title {
  width: 100%;
  border: 0;
  background: transparent;
  font-family: var(--font-display);
  font-weight: var(--fw-light);
  letter-spacing: var(--tr-display);
  font-size: var(--t-h2);
  line-height: var(--lh-snug);
  color: var(--ink);
  padding: 0;
  margin-bottom: var(--s-5);
}
.os-title:focus { outline: none; }
.os-title::placeholder { color: var(--ink-4); }

/* Alta, edición y palabras en un solo renglón dim: son consulta, no
   operación. Las etiquetas sí se operan, así que van aparte y visibles. */
.os-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-1) var(--s-4);
  margin: 0 0 var(--s-3);
}
.os-tags { display: flex; flex-wrap: wrap; gap: var(--s-1); justify-content: flex-end; }
.os-side__tags { margin-bottom: var(--s-2); justify-content: flex-start; }
.os-side__people { margin-bottom: var(--s-5); justify-content: flex-start; }
.os-who__go {
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
}
.os-who__go:hover, .os-who__go:focus-visible { text-decoration: underline; }
.os-tag { cursor: default; }
.os-tag__x { color: var(--ink-3); }
.os-tagadd {
  height: 26px;
  border: var(--stroke) dashed var(--hairline-strong);
  background: transparent;
  border-radius: var(--r-pill);
  color: var(--ink-3);
  font-size: var(--t-label);
  padding-inline: var(--s-3);
  font-family: inherit;
}
/* SE QUEDA OPACO, y aquí el vidrio no es un mal trato: es un NO-OP. Este campo
   nace dentro del panel lateral (intel.js), que se queda opaco, así que el
   único fondo que tendría para recoger es --surface: --surface-frost sobre
   --surface da 255,255,255 en claro y 23,23,26 en cockpit, o sea exactamente
   el mismo píxel, con un backdrop-filter de regalo. El vidrio necesita color
   detrás; aquí no hay. */
.os-tagin {
  height: 26px;
  width: 120px;
  border: var(--stroke) solid var(--hairline-strong);
  background: var(--surface);
  border-radius: var(--r-pill);
  color: var(--ink);
  font-size: var(--t-label);
  padding-inline: var(--s-3);
  font-family: inherit;
}
.os-tagin:focus { outline: none; border-color: var(--ink-4); }

/* ---------- Editor de bloques ---------- */

.os-editor { display: grid; gap: 2px; padding-bottom: var(--s-10); }

.os-b {
  position: relative;
  font-size: var(--t-body);
  line-height: var(--lh-body);
  color: var(--ink);
  padding: var(--s-1) 0;
  outline: none;
  white-space: pre-wrap;
  word-break: break-word;
}
.os-b:empty::before {
  content: attr(data-ph);
  color: var(--ink-4);
  pointer-events: none;
}
.os-b--h1, .os-b--h2, .os-b--h3 {
  font-family: var(--font-display);
  font-weight: var(--fw-light);
  letter-spacing: var(--tr-tight);
  line-height: var(--lh-snug);
  margin-top: var(--s-4);
}
.os-b--h1 { font-size: var(--t-h2); }
.os-b--h2 { font-size: var(--t-h3); }
.os-b--h3 { font-size: var(--t-body); font-weight: var(--fw-medium); letter-spacing: var(--tr-normal); }
.os-b--quote {
  border-left: var(--stroke-2) solid var(--ink-4);
  padding-left: var(--s-4);
  color: var(--ink-2);
}
.os-b--code {
  font-family: var(--font-mono);
  font-size: var(--t-body-sm);
  background: var(--surface-sunk);
  border: var(--stroke) solid var(--hairline);
  border-radius: var(--r-md);
  padding: var(--s-3) var(--s-4);
}
.os-b code { font-family: var(--font-mono); font-size: 0.9em; background: var(--surface-sunk); border-radius: var(--r-xs); padding: 1px var(--s-1); }

.os-row { display: flex; align-items: flex-start; gap: var(--s-3); }
.os-row__mk {
  flex: 0 0 auto;
  color: var(--ink-3);
  font-size: var(--t-body-sm);
  line-height: var(--lh-body);
  padding-top: var(--s-1);
  font-variant-numeric: tabular-nums;
  user-select: none;
}
.os-row__mk--dot::before { content: "—"; }
.os-row .os-b { flex: 1 1 auto; }
.os-row .r-check { margin-top: 7px; }
.os-row--done .os-b { color: var(--ink-4); text-decoration: line-through; }

.os-b--div {
  height: var(--stroke);
  background: var(--hairline-strong);
  margin: var(--s-4) 0;
  padding: 0;
}

/* Menú de bloques. El papel lo pone .r-card--solid en index.html, no una regla
   de aquí: flota sobre el texto de la nota, que es contenido arbitrario, y eso
   es exactamente lo que el papel translúcido no puede hacer legible. */
.os-slash {
  position: fixed;
  /* Un desplegable anclado a su disparador (el caret del bloque). */
  z-index: var(--z-menu);
  width: 240px;
  padding: var(--s-2);
  border-radius: var(--r-md);
  box-shadow: var(--e-3);
  max-height: 300px;
  overflow-y: auto;
}
.os-slash__i {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  width: 100%;
  border: 0;
  background: transparent;
  color: var(--ink-2);
  font-family: inherit;
  font-size: var(--t-body-sm);
  text-align: left;
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-sm);
}
.os-slash__i:hover, .os-slash__i[aria-selected="true"] { background: var(--surface-sunk); color: var(--ink); }
.os-slash__k { margin-left: auto; font-family: var(--font-mono); font-size: var(--t-micro); color: var(--ink-4); }

/* ============================================================
   SETTINGS
   ============================================================ */

/* El rótulo /kicker es el único encabezado de la tarjeta: los subtítulos
   que repetían en oración lo que ya decía el rótulo se fueron. */
/* Back office: la barra de secciones de Ajustes. La pastilla activa la pinta
   el sistema (.r-tabs__i[aria-current]); aquí solo el aire y la entrada de la
   sección, para que cambiar de pestaña fluya en vez de saltar. */
.os-subnav { margin-bottom: var(--s-6); }
/* La sección entra con la MISMA receta que el cambio de vista del rail
   —os-view-in, --d-slow, --ease-out—, no con una animación propia. Cuando se
   llega a Ajustes desde el rail las dos corren a la vez: misma duración y
   mismo easing, así que se leen como un solo gesto y no como dos saltos.
   (El apagado por prefers-reduced-motion está en la sección MOVIMIENTO.) */
.os-sec { animation: os-view-in var(--d-slow) var(--ease-out) backwards; }
/* Las tarjetas ABRAZAN su contenido en vez de estirarse al alto del renglón:
   Datos junto a Módulos dejaba 300px de papel vacío debajo de "Borrar todo",
   que se lee como algo a medio cargar y no como una tarjeta corta. Mismo
   criterio que la tarjeta del inbox en Home. */
.os-sec.r-bento { align-items: start; }

/* El rótulo de sección medía lo mismo que la etiqueta del campo más chico
   —`.r-eyebrow` declara lo mismo que `.r-label`— y Ajustes se leía plano.
   Se separa con tamaño y espacio, nunca con negritas. */
.os-sec__head { font-size: var(--t-h3); color: var(--ink); margin-bottom: var(--s-6); }
.os-sec__act { margin-top: var(--s-5); flex-wrap: wrap; }
.os-sec__kv { margin-top: var(--s-4); }

/* Las columnas las decide el ancho disponible, no un breakpoint: el mismo
   formulario da tres columnas en la tarjeta ancha de Motor, dos en la de
   Identidad y una en 375px, sin una media query que mantener. */
.os-form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--s-5);
}
.os-field { display: grid; gap: var(--s-2); align-content: start; }
.os-field--wide { grid-column: 1 / -1; }
/* Celda de acción: se alinea al PIE de su fila para que el botón quede a la
   altura de los controles que tiene al lado, no de sus etiquetas. */
.os-act { align-content: end; }
.os-inline { display: flex; gap: var(--s-2); align-items: center; }
.os-inline .r-input { min-width: 0; }

/* Ajustes › Servidor. El QR de la punta viva es lo que se enseña
   al teléfono. Copiar dice la misma URL. SVG real, currentColor. */
.os-neturl {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  color: inherit;
  text-align: left;
  cursor: pointer;
  min-width: 0;
  word-break: break-all;
}
.os-netqr {
  width: min(16rem, 72%);
  aspect-ratio: 1;
  color: var(--ink);
  margin-top: var(--s-4);
}
.os-netqr svg { display: block; width: 100%; height: 100%; }

/* Ajustes › Servidor › Nube. Pasos numerados a un tablero
   externo: frase corta + enlace, nunca un comando. */
.os-nube-pasos {
  list-style: none;
  margin: var(--s-3) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  counter-reset: nube-paso;
}
.os-nube-pasos li {
  display: flex;
  gap: var(--s-2);
  align-items: baseline;
  counter-increment: nube-paso;
  color: var(--ink-2);
  font-size: var(--t-sm);
  line-height: var(--lh-snug);
}
.os-nube-pasos li::before {
  content: counter(nube-paso);
  font-variant-numeric: tabular-nums;
  color: var(--ink-3);
  flex: 0 0 auto;
  min-width: 1.1em;
}
.os-nube-pasos a { color: inherit; }

/* Ajustes › Servidor › chooser. Timeline de setup: tres pasos,
   punto cuadrado, hairline entre ellos. El estado lo escribe JS
   (data-estado=hecho|ahora|falta). */
.os-srv-linea {
  list-style: none;
  margin: var(--s-3) 0 0;
  padding: 0;
  display: flex;
  align-items: flex-start;
  gap: 0;
}
.os-srv-linea li {
  flex: 1;
  position: relative;
  padding-top: var(--s-4);
  color: var(--ink-3);
  font-size: var(--t-micro);
  line-height: var(--lh-snug);
  font-variant-numeric: tabular-nums;
}
.os-srv-linea li::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 6px;
  height: 6px;
  background: var(--ink-3);
}
.os-srv-linea li + li::after {
  content: "";
  position: absolute;
  top: 2px;
  right: 100%;
  width: 100%;
  height: var(--stroke);
  background: var(--trace);
  pointer-events: none;
}
.os-srv-linea li[data-estado="hecho"],
.os-srv-linea li[data-estado="ahora"] {
  color: var(--ink);
}
.os-srv-linea li[data-estado="hecho"]::before,
.os-srv-linea li[data-estado="ahora"]::before {
  background: var(--ink);
}

/* Ajustes › Servidor. S1: ticker Activo/Inactivo al lado del
   nombre — estado, no nav. S3: fila servicio + Autenticar/Conectar
   que abre el panel de esa cuenta. S5: Integrar revela el muro. */
.os-srv-tick {
  flex: 0 0 auto;
  cursor: pointer;
  border: 0;
  background: transparent;
  padding: var(--s-1) var(--s-2);
  border-radius: var(--r-pill);
  font: inherit;
}
.os-srv-tick:hover { color: var(--ink); }
.os-srv-cta { margin-top: var(--s-3); }
.os-srv-mas { margin-top: var(--s-4); }
.os-srv-int {
  margin-top: var(--s-4);
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
}
.os-srv-int__row { align-items: center; }
.os-srv-int__panel { margin-top: var(--s-1); }

/* Ajustes › Sistema: una columna. El auto-fit de `.os-form` no
   gana una segunda aquí — perfil y gestos se leen apilados. */
.os-sistema .os-form { grid-template-columns: 1fr; }

/* Ajustes › Agentes: Voz | Texto. El flyout mide 480; la caja
   interior, menos. Un corte a 520 nunca prendía las dos columnas.
   360 es el equivalente interior de un panel ~520: dos columnas
   en el flyout normal, apiladas solo cuando la caja es estrecha.
   Probar conexión va centrado, fuera de la retícula. El resto
   del motor (Más, prompt, sliders) es una columna. */
#viewSettings { container-type: inline-size; container-name: set; }
.os-agente {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s-5);
  align-items: start;
}
.os-agente > .r-card { min-width: 0; }
@container set (min-width: 360px) {
  .os-agente { grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); }
}
.os-agente-probar {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--s-2);
  width: 100%;
}
.os-agente-probar .os-field { justify-items: center; width: auto; }
.os-agente-probar .r-row { justify-content: center; }
.os-agente-resto { grid-template-columns: 1fr; }

/* Ajustes › Agentes: una sola lista compacta (AD1–AD4).
   Proveedores y harnesses son el mismo renglón: nombre + estado +
   un verbo. Los wrappers (#setRealtimeConfig / #setHarnessConfig)
   no pintan chrome: son columnas flex con el mismo hueco. No
   `display: contents` — el click de Conectar/Cambiar vive en
   #setRealtimeConfig. «Detección actualizada» es una nota
   quieta, no una sección. */
.os-agente-cards {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  width: 100%;
}
#setRealtimeConfig,
#setHarnessConfig,
#setHarnessFilas {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
}
.os-agente-cards .r-card.os-int {
  padding: var(--s-3) var(--s-4);
}
#setHarnessFilas .r-card.os-int[data-activo] {
  outline: var(--stroke) solid var(--hairline-strong);
  outline-offset: 2px;
}
#setHarnessFilas .r-card.os-int {
  cursor: pointer;
}
#setHarnessActivo.r-seg {
  display: flex;
  flex-wrap: wrap;
  width: 100%;
  max-width: 100%;
  box-sizing: border-box;
}
.os-catalogo {
  border-style: dashed;
  color: var(--ink-3);
}
.os-agente-cards__nota { align-items: center; }
.os-int__row { align-items: center; flex-wrap: nowrap; }
.os-int__panel { margin-top: var(--s-3); }
.os-int--foco {
  outline: 1px solid var(--hairline-strong);
  outline-offset: 2px;
}

/* Más, en Ajustes › Agente: endpoint, destino, precios y tope viven
   comprimidos hasta que se pican. Native <details>, sin un segundo
   control. El triángulo del navegador se apaga: el label ya dice que
   hay más. */
.os-mas { grid-column: 1 / -1; display: grid; gap: var(--s-4); }
.os-mas > summary {
  cursor: default;
  list-style: none;
}
.os-mas > summary::-webkit-details-marker { display: none; }

/* Selector de wallpaper: una fila de muestras, nada más. Sin nombre
   impreso debajo — va en title/aria-label y en un tip que solo aparece
   al hover o al foco — y sin renglón de estado aparte: el único
   indicador de cuál está activa es el aro (aria-checked). La preview
   NO copia paletas: cada .os-wallswatch__prev lleva su propio
   data-wall="<variante>" (menos "Ninguno", que no lleva ninguno) y
   ron.css le resuelve --wall-a…d ahí mismo — el selector de atributo
   vive sin :root exactamente para esto. Si el sistema ajusta un tono,
   la muestra lo hereda sola. */
/* Tema y posición del rail son dos elecciones distintas, así que no van
   pegadas; y son de la misma familia —un renglón de opciones cada una— así que
   tampoco se separan como bloques. --s-3 es lo que las lee como pareja sin
   volverlas un solo control de seis pastillas. */
.os-railseg { margin-top: var(--s-3); }

.os-wallsec { margin-top: var(--s-5); }
.os-wallgrid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
}
/* Cuadro fijo (--s-10) en vez de aspect-ratio: en un <button> el
   aspect-ratio no siempre se respeta (control de formulario, no caja
   normal). Con ancho y alto iguales de un token no hace falta. */
.os-wallswatch {
  position: relative;
  width: var(--s-10);
  height: var(--s-10);
  border: 0;
  background: none;
  padding: 0;
  font-family: inherit;
}
.os-wallswatch__prev {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: var(--r-sm);
  border: var(--stroke) solid var(--hairline-strong);
  background-color: var(--surface-sunk);
  transition: transform var(--d-fast) var(--ease-spring), border-color var(--d-fast) var(--ease);
}
.os-wallswatch:hover .os-wallswatch__prev,
.os-wallswatch:focus-visible .os-wallswatch__prev {
  transform: scale(1.12);
}
.os-wallswatch[aria-checked="true"] .os-wallswatch__prev {
  border-color: var(--accent);
  border-width: var(--stroke-2);
}

/* Tip: oculto por default, aparece al hover/foco. Nada de sombra nueva
   más allá de --e-2, que ya existe para lo que flota. */
.os-wallswatch__tip {
  position: absolute;
  bottom: calc(100% + var(--s-2));
  left: 50%;
  /* CRUDO A PROPÓSITO, igual que en .os-stat__more: el tip solo tiene que
     quedar encima de las muestras vecinas de su propia fila. */
  z-index: 1;
  white-space: nowrap;
  font-size: var(--t-micro);
  color: var(--ink);
  background: var(--surface);
  border: var(--stroke) solid var(--hairline);
  border-radius: var(--r-sm);
  padding: var(--s-1) var(--s-2);
  box-shadow: var(--e-2);
  opacity: 0;
  pointer-events: none;
  transform: translate(-50%, var(--s-1));
  transition: opacity var(--d-fast) var(--ease), transform var(--d-fast) var(--ease);
}
.os-wallswatch:hover .os-wallswatch__tip,
.os-wallswatch:focus-visible .os-wallswatch__tip {
  opacity: 1;
  transform: translate(-50%, 0);
}

/* La receta (--wall-blooms + velo) es la misma de .r-wall; los cuatro
   tonos por variante los pone ron.css vía [data-wall="…"], puesto en
   este mismo elemento. "Ninguno" no lleva data-wall, así que no entra
   aquí — se queda en --surface-sunk de arriba. */
.os-wallswatch__prev[data-wall] {
  background-color: var(--canvas);
  background-image: var(--wall-blooms);
  background-repeat: no-repeat;
  /* El velo del wallpaper existe para proteger texto, y una muestra de
     40px no tiene texto encima: con 0.66 las nueve se ven iguales. Por
     eso el sistema trae un velo aparte para muestras. */
  --wall-veil: var(--wall-veil-preview);
}
.os-wallswatch__prev[data-wall]::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--canvas);
  opacity: var(--wall-veil);
}

/* Ahora es una celda de ancho completo dentro de .os-form: el gap de la
   retícula ya la separa, un margen propio lo duplicaría. */
.os-cors { margin: 0; }
.os-cors__cmd {
  margin: var(--s-2) 0 0;
  padding: var(--s-2) var(--s-3);
  background: var(--surface-sunk);
  border-radius: var(--r-sm);
  overflow-x: auto;
}

.os-mods { display: grid; gap: var(--s-1); }
.os-mod {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-2) 0;
  border-bottom: var(--stroke) solid var(--hairline);
}
.os-mod:last-child { border-bottom: 0; }
/* Sin descripción impresa (va en title) y sin palabra de estado: el punto
   cuadrado y el nombre atenuado ya dicen si el módulo existe. */
.os-mod__n { font-size: var(--t-body-sm); color: var(--ink); }
.os-mod--soon .os-mod__n { color: var(--ink-4); }
.os-mod__r { margin-left: auto; }


/* ---- La skill del agente dentro de la lista de módulos ----
   El conteo de acciones se lleva el `margin-left: auto` que traía el punto
   del módulo: los dos estados quedan juntos y en el mismo sitio en todos los
   renglones —cuántas acciones expone la API (punto info) y si el módulo está
   montado (punto ok)—. */
.os-mod__api { margin-left: auto; }
.os-mod__api + .os-mod__r { margin-left: 0; }
button.os-mod__api {
  border: 0;
  background: none;
  padding: 0;
  color: var(--ink-2);
  transition: color var(--d-fast) var(--ease);
}
/* Reusa el aire vertical de la fila al ampliar el botón táctil. La etiqueta
   sigue pequeña; la superficie de consulta pasa a 44 sin apilar otro control. */
.os-shell[data-os-mobile] button.os-mod__api {
  min-width: var(--orb-size);
  min-height: var(--orb-size);
  margin-block: calc(-1 * var(--s-2));
}
button.os-mod__api:hover,
button.os-mod__api[aria-expanded="true"] { color: var(--ink); }

/* El panel de la skill flota: mismo reset de [popover] que .r-pick__list
   —posición fija escrita por settings.js, superficie del TEMA y no del
   contexto, opaca porque flota sobre contenido arbitrario— y la misma
   degradación: sin soporte de popover se queda invisible en vez de caerse
   a media lista. Flotando, la lista de módulos no cambia de alto nunca. */
.os-skill {
  position: fixed;
  inset: auto;
  margin: 0;
  padding: var(--s-2);
  width: max-content;
  max-width: min(420px, calc(100vw - var(--s-6)));
  max-height: min(480px, 66vh);
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--theme-surface);
  color: var(--theme-ink);
  border: var(--stroke) solid var(--theme-hairline);
  border-radius: var(--r-md);
  box-shadow: var(--e-3);
  opacity: 0;
  translate: 0 -4px;
  transition: opacity var(--d-fast) var(--ease),
              translate var(--d-fast) var(--ease-out),
              overlay var(--d-fast) var(--ease) allow-discrete,
              display var(--d-fast) var(--ease) allow-discrete;
}
.os-skill:popover-open { opacity: 1; translate: 0 0; }
@starting-style {
  .os-skill:popover-open { opacity: 0; translate: 0 -4px; }
}
/* Sin velo: es un desplegable, no un diálogo. */
.os-skill::backdrop { background: transparent; }

.os-skill__a {
  display: flex;
  align-items: baseline;
  gap: var(--s-3);
  width: 100%;
  text-align: left;
  border: 0;
  background: none;
  padding: var(--s-2);
  border-radius: var(--r-sm);
  color: var(--theme-ink-2);
}
.os-skill__a:hover { background: var(--theme-surface-sunk); }
.os-skill__a[aria-expanded="true"] { color: var(--theme-ink); }
.os-skill__id { font-size: var(--t-micro); color: var(--theme-ink-3); }
.os-skill__n  { font-size: var(--t-body-sm); }
.os-skill__d {
  display: grid;
  gap: var(--s-2);
  padding: 0 var(--s-2) var(--s-3);
  color: var(--theme-ink-2);
}
.os-skill__ps { display: flex; flex-wrap: wrap; gap: var(--s-1); }
.os-mods__say { margin: 0 0 var(--s-2); }

/* ============================================================
   MOVIMIENTO
   Regla: se mueve lo que cambia de lugar, se desvanece lo que
   entra y sale. Nada dura más que --d-slow, y todo se apaga con
   prefers-reduced-motion.
   ============================================================ */

/* Cambio de vista: sube y aparece. El rail lo acompaña con su pastilla. */
@keyframes os-view-in {
  from { opacity: 0; transform: translateY(10px); }
}
.os-view { animation: os-view-in var(--d-slow) var(--ease-out) backwards; }

/* Mensajes: el del usuario lo posiciona el FLIP en home.js (vuela desde
   el composer), así que solo se anima el de la IA y el estado vacío. */
@keyframes os-rise {
  from { opacity: 0; transform: translateY(12px); }
}
.os-msg--ai { animation: os-rise var(--d-slow) var(--ease-out) backwards; }
.os-msg--seen { animation: none !important; }

.os-empty { animation: os-rise var(--d-slower) var(--ease-out) backwards; }
.os-empty > * { animation: os-rise var(--d-slow) var(--ease-out) backwards; }
.os-empty > *:nth-child(2) { animation-delay: calc(var(--stagger) * 2); }
.os-empty > *:nth-child(3) { animation-delay: calc(var(--stagger) * 4); }
.os-empty > *:nth-child(4) { animation-delay: calc(var(--stagger) * 6); }

/* Cascada de listas: el índice lo pone el JS en --i. Las conversaciones del
   historial entran igual que las tareas y las notas — es la misma clase de
   lista y no gana una curva propia. */
@keyframes os-slide-in {
  from { opacity: 0; transform: translateY(8px); }
}
/* Notas y conversaciones. `.os-task` se fue de esta lista con la tarjeta de
   Inbox: la fila de tarea vive hoy en Workspace, con su propia cascada. */
.os-note, .os-chatrow {
  animation: os-slide-in var(--d-base) var(--ease-out) backwards;
  animation-delay: calc(var(--i, 0) * var(--stagger));
}

/* Aquí vivían tres movimientos del Inbox y se fueron con él: el desdoble de la
   tarjeta (`.os-inbox__rest`), la salida de una tarea que se cierra
   (`.os-task--leaving`, que la deslizaba fuera del renglón antes de rearmar la
   lista) y el latido de la métrica al cambiar de cifra (`os-tick` +
   `.os-inbox__metric--tick`). El keyframe `os-tick` se borró con ellos: no le
   quedaba ningún consumidor.

   La regla que los tres cumplían sigue viva y la hereda quien re-hospede las
   tareas: una fila que desaparece de golpe se siente como un error, no como un
   cierre. */

/* Menú de bloques: crece desde su esquina, y también se encoge para irse.
   Era una `animation` de entrada, o sea que aparecía animado y desaparecía de
   golpe. Hoy es una transición con `display … allow-discrete`, el modelo de
   `.r-pick__list` en ron.css.

   `translate` y `scale` como propiedades independientes, no `transform`: la
   clase hermana de este nodo es `.r-card`, que YA transiciona `transform` con
   otra duración, y declarar el mismo `transform` aquí sería pelearse con el
   sistema desde una hoja local. Con las independientes las dos conviven.

   El recorrido baja de los 6px crudos del keyframe viejo a --s-1 (4px), que es
   el mismo desplazamiento con el que entran los desplegables del sistema
   (`.r-pick__list`, `.r-capture__panel`): un menú del OS no tiene por qué
   moverse distinto según el archivo donde vive. De paso se va el único valor
   crudo que quedaba en esta sección.

   Salir es más corto que entrar: el menú ya cumplió, y lo que interesa es ver
   el bloque que quedó debajo. */
.os-slash {
  display: block;
  transform-origin: top left;
  opacity: 1;
  translate: 0;
  scale: 1;
  transition: opacity var(--d-fast) var(--ease),
              translate var(--d-fast) var(--ease-out),
              scale var(--d-fast) var(--ease-out),
              display var(--d-fast) var(--ease) allow-discrete;
}
.os-slash[hidden] {
  opacity: 0;
  translate: 0 calc(var(--s-1) * -1);
  scale: 0.97;
  pointer-events: none;
  transition: opacity var(--d-fast) var(--ease),
              translate var(--d-fast) var(--ease),
              scale var(--d-fast) var(--ease),
              display var(--d-fast) var(--ease) allow-discrete;
}
@starting-style {
  .os-slash:not([hidden]) {
    opacity: 0;
    translate: 0 calc(var(--s-1) * -1);
    scale: 0.97;
  }
}

/* Composer: la caja de texto crece sin salto. */
.os-prompt { transition: height var(--d-fast) var(--ease); }

/* Chips y controles: respuesta al tacto, corta. */
.os-ctxchip, .os-note, .os-tagadd, .os-mod {
  transition: transform var(--d-fast) var(--ease), background var(--d-fast) var(--ease);
}
.os-ctxchip:active, .os-tagadd:active { transform: scale(0.96); }
.os-send { transition: transform var(--d-fast) var(--ease-spring); }
.os-send:active { transform: scale(0.9); }

@media (prefers-reduced-motion: reduce) {
  .os-view, .os-sec, .os-msg--ai, .os-empty, .os-empty > *, .os-note,
  .os-side, .os-chatrow {
    animation: none !important;
  }
  /* Los que entran y salen con `display … allow-discrete` aparecen y
     desaparecen PUESTOS. Hay que nombrar los dos estados: la transición de
     salida vive en `[hidden]`, que pesa más que la clase sola, y sin él la
     salida se quedaría animada mientras la entrada no. El `@starting-style` no
     necesita apagarse — sin transición no tiene de dónde arrancar. */
  .os-slash, .os-slash[hidden],
  .os-toast, .os-toast[hidden] { transition: none; }
  .os-note, .os-send, .os-ctxchip, .os-tagadd { transition: none; }
  .os-chats__v, .os-chats__now, .os-chatrow__t { transition: none; }
  /* El centro se recentra SIN recorrido, pero se recentra: la columna sigue
     quedando en medio de la región libre, nada más que llega de golpe. La
     regla es la misma que el sistema aplica a su panel. */
  .os-center { transition: none; }
  /* Lo que se revela al hover/foco aparece de golpe: sigue revelándose,
     nada más sin desvanecido. */
  .os-note__r, .os-hint,
  .os-chatrow__m, .os-chatrow__act,
  .os-side__id, .os-side__hint, .os-stat__more { transition: none; }
}

/* ============================================================
   TOAST
   ============================================================ */

.os-toast {
  position: fixed;
  /* Sube por encima del dock. Esto ya estaba ROTO en móvil antes de que el rail
     se pudiera mover: el aviso vivía en `--s-6` desde el canto y la cápsula
     acostada vive en `--s-4`, o sea que el aviso más visto del OS se posaba
     encima del rail —los dos centrados, uno arriba del otro, y el toast gana el
     z-index—. Restar la reserva lo arregla en las dos situaciones con dock: la
     pantalla angosta y el rail elegido abajo. Con el rail de pie vale cero. */
  bottom: calc(var(--s-6) + var(--os-rail-b));
  left: 50%;
  transform: translateX(-50%);
  /* Un aviso le gana a todo lo anterior, incluido el panel flotante de módulo
     (--z-float): un "Al inbox: …" disparado con el panel abierto tiene que
     verse. */
  z-index: var(--z-toast);
  padding: var(--s-3) var(--s-5);
  border-radius: var(--r-pill);
  background: var(--surface-invert);
  color: var(--ink-invert);
  font-size: var(--t-body-sm);
  box-shadow: var(--e-3);
  /* Entra Y SALE. Se iba de golpe cuando vencía su temporizador, que es el
     único momento en que el ojo lo está mirando: el aviso más visto del OS era
     el único sin salida. Sube al aparecer y baja al irse, con `translate`
     independiente para no pelearse con el `transform` que lo centra —las dos
     traslaciones se componen, así que el centrado no se toca—. */
  display: block;
  opacity: 1;
  translate: 0;
  transition: opacity var(--d-fast) var(--ease),
              translate var(--d-base) var(--ease-out),
              display var(--d-base) var(--ease) allow-discrete;
}
.os-toast[hidden] {
  opacity: 0;
  translate: 0 var(--s-2);
  pointer-events: none;
  transition: opacity var(--d-base) var(--ease),
              translate var(--d-base) var(--ease),
              display var(--d-base) var(--ease) allow-discrete;
}
@starting-style {
  .os-toast:not([hidden]) { opacity: 0; translate: 0 var(--s-2); }
}

/* ============================================================
   RESPONSIVO
   ============================================================ */

/* Sin hover no hay revelación: en pantallas táctiles lo secundario se
   queda visible en vez de volverse inalcanzable. La ayuda de atajos no
   entra aquí — se va del todo en móvil, más abajo. */
@media (hover: none) {
  /* `pointer-events` viaja con la opacidad, siempre. Sin la segunda
     declaración, en táctil el bote de borrar de una conversación se vería
     puesto y no respondería a un toque: la revelación por hover está apagada
     aquí, así que nadie devolvería el `auto`. */
  .os-note__r,
  .os-chatrow__m, .os-chatrow__act,
  .os-side__id, .os-side__hint { opacity: 1; pointer-events: auto; }
  /* El desglose de estado no se queda abierto tapando el renglón: sin hover, el
     :focus-visible del botón (o su title) sigue siendo el camino. */
}

/* ---------- PANTALLA ANGOSTA: LÁPIDA DE «EL PANEL PASA POR ENCIMA» ----------
   Aquí vivían dos bloques que se fueron con el chasis viejo: el de 1080, que
   apilaba el Inbox debajo del chat (ya no hay Inbox), y el de 720, que acostaba
   el rail como dock (ya no hay cápsula que acostar). Y vivía un tercero, que es
   el que se acaba de ir y el único que hacía algo:

     @media (max-width: 900px) {
       [data-flyout-open] .os-center {
         padding-inline-end: calc(var(--flyout-rail) + var(--flyout-air));
       }
     }

   Ese bloque decía que el centro DEJARA DE RESERVAR abajo de 900px, y su
   comentario prometía que «se pierde ver los dos a la vez y se gana que los dos
   sirvan por separado». La primera mitad era verdad; la segunda era falsa, y
   medirla es lo que lo cerró. Con el centro sin reservar, el panel no se movía
   de su sitio: seguía anclado al canto derecho y ENCIMA de la conversación.

     ancho   panel   se traslapa con la conversación
     600     240     224 px
     700     240     197 px
     800     240     151 px
     900     246     107 px

   Y el traslape no era cosmético: la caja de prompt mide 44px de botón de
   enviar en su canto derecho, así que en los cuatro casos ese botón quedaba
   DEBAJO del papel del panel y no se podía picar. El único camino que quedaba
   para mandar era `↩`. Un renglón de conversación cortado a media palabra y un
   botón inerte no son «los dos sirven por separado».

   ── LO QUE SE HACE HOY: EL CENTRO NO DEJA DE RESERVAR NUNCA ─────────
   No hay caso especial. El centro reserva `rail + --flyout-w + air` en todo
   ancho, y los dos se aprietan juntos hasta donde la ventana dé. Medido, con la
   misma semilla:

     ancho   panel   conversación   traslape
     600     240     248            0
     700     240     348            0
     800     240     440            0
     900     246     534            0
     1000    346     534            0

   Lo que se pierde es que la conversación baja de `--measure` abajo de ~894px.
   Se pierde a propósito: `--measure` es un TECHO de lectura —64ch, la línea más
   larga que el ojo sigue sin perderse— y ron.js lo usa además como PISO para
   que un panel ambicioso no se coma una pantalla grande. En una ventana de 600
   no hay pantalla grande que proteger: exigir 534px ahí es exactamente lo que
   producía el traslape.

   En escritorio angosto el panel conserva el límite de ron.js y el centro
   reserva su ancho; la frontera sigue cubierta por `test_traslape.py`.
   En teléfono, Home activa `data-flyout-floor` y las perillas públicas permiten
   que el módulo ocupe la pantalla. El centro y los orbes quedan ocultos e
   inertes mientras está abierto; cerrar restaura la misma conversación.
   El bloque móvil siguiente adapta esa pantalla a safe areas y al teclado. */

@media (max-width: 720px) {
  /* Los atajos de teclado no aplican con teclado en pantalla: la ayuda se va. */
  .os-hint { display: none; }
  /* El indicador es UNO y solo habla cuando algo se rompe, así que su texto no
     se esconde en móvil —es la única señal— pero se recorta antes de empujar
     al `+` fuera de la pantalla. */
  .os-stat__t { max-width: 22ch; }
  /* El historial ocupa el hueco del log, así que respeta su mismo techo. */
  .os-log, .os-chatlist { max-height: 60vh; }
  .os-center {
    --os-libre-end: calc(var(--flyout-rail) + var(--s-2));
    padding-inline: var(--s-4) var(--os-libre-end);
  }
  .os-note { grid-template-columns: minmax(0, 1fr); }
  .os-note__r { grid-row: auto; grid-column: 1; justify-items: start; }
  .os-note__tags { justify-content: flex-start; }
}

/* iPhone: la caja visible sigue al teclado y a las barras de Safari.
   Un módulo ocupa su propia pantalla; cerrar devuelve la conversación intacta.
   Las perillas públicas del flyout conservan su geometría y sus componentes. */
.os-shell[data-os-mobile] {
  --ctl-sm: var(--orb-size);
  --orb-inset: max(var(--s-4), var(--os-safe-right));
  --orb-gap: var(--s-2);
  --orb-apart: var(--s-2);
  --flyout-rail: max(var(--s-2), var(--os-safe-right));
  --flyout-pad: max(var(--s-2), var(--os-safe-left));
  --flyout-air: var(--s-2);
  --flyout-gap: max(var(--s-2), var(--os-safe-top), var(--os-safe-bottom));
  --flyout-chrome: calc(var(--flyout-rail) + var(--flyout-pad) + var(--flyout-air));
}
.os-shell[data-os-mobile] .os-center,
.os-shell[data-os-mobile][data-flyout-open] .os-center {
  --os-libre-end: calc(var(--orb-size) + var(--orb-inset) + var(--s-3));
  padding-inline: max(var(--s-4), var(--os-safe-left)) var(--os-libre-end);
  padding-top: max(var(--s-4), var(--os-safe-top));
  padding-bottom: max(var(--s-3), var(--os-safe-bottom));
  transition: none;
}
.os-shell[data-os-mobile][data-flyout-open] .os-center,
.os-shell[data-os-mobile][data-flyout-open] .os-orbs { visibility: hidden; pointer-events: none; }
.os-shell[data-os-mobile] .os-orbs {
  max-height: calc(100% - var(--os-safe-top) - var(--os-safe-bottom) - var(--s-4) * 2);
  max-width: calc(var(--orb-size) + var(--s-2));
  top: calc((100% + var(--os-safe-top) - var(--os-safe-bottom)) / 2);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: none;
}
.os-shell[data-os-mobile] .os-orbs > button { flex-shrink: 0; }
.os-shell[data-os-mobile] .os-col { min-width: 0; }
.os-shell[data-os-mobile] .os-log,
.os-shell[data-os-mobile] .os-chatlist { min-height: 0; max-height: none; overscroll-behavior: contain; }
.os-shell[data-os-mobile] .os-form { grid-template-columns: minmax(0, 1fr); }
.os-shell[data-os-mobile] .os-field { min-width: 0; }
.os-shell[data-os-mobile] .os-field > label { min-height: var(--orb-size); align-items: center; }
.os-shell[data-os-mobile] .os-subnav button,
.os-shell[data-os-mobile] .os-field button,
.os-shell[data-os-mobile] .os-int .r-btn,
.os-shell[data-os-mobile] .os-msg button { min-height: var(--orb-size); min-width: var(--orb-size); }
.os-shell[data-os-mobile] summary { min-height: var(--orb-size); align-content: center; }
/* Safari amplía al enfocar campos de menos de 1rem. Es el piso del navegador,
   no una escala tipográfica nueva; se conserva el zoom de accesibilidad. */
.os-shell[data-os-mobile] input,
.os-shell[data-os-mobile] textarea,
.os-shell[data-os-mobile] select { font-size: max(1rem, var(--t-body)); }
.os-shell[data-os-mobile] .os-composer__ctx { display: none; }
.os-shell[data-os-mobile] .os-prompt { max-height: none; }
.os-shell[data-os-mobile][data-os-compact] .os-col { gap: var(--s-2); }
.os-shell[data-os-mobile][data-os-compact] .os-log { gap: var(--s-4); padding-block: var(--s-2); }
.os-shell[data-os-mobile][data-os-compact] .os-msg--sigue { margin-top: calc(var(--s-1) - var(--s-4)); }

/* ---------- Imagen en la nota ---------- */
/* El reset del design system ya da display:block y max-width:100%. */
.os-b--img { padding: var(--s-2) 0; }
.os-b--img img { border-radius: var(--r-md); border: var(--stroke) solid var(--hairline); }
.os-b--img:focus-visible { outline: var(--stroke-2) solid var(--accent); outline-offset: var(--s-1); }
.os-editor--drop { outline: var(--stroke-2) dashed var(--accent); outline-offset: var(--s-3); border-radius: var(--r-md); }


/* ============================================================
   LA FRANJA DE ARRASTRE — el gesto que la ventana sin barra perdió
   ============================================================
   La ventana no tiene barra de título, así que el contenido llega al canto de
   arriba. Lo que se dio por hecho y era falso: que la franja transparente del
   sistema seguiría moviendo la ventana. **No lo hace** — el WebView cubre la
   ventana completa y se come el mousedown antes de que AppKit lo vea. La
   ventana quedó inamovible, y Robbie se topó con eso tres veces.

   Esto lo devuelve. El arrastre lo hace Tauri leyendo
   `data-tauri-drag-region` en `index.html`; aquí solo vive la caja.

   `z-index: 1` y no más: cualquier control del OS que caiga en esos 28px
   pinta encima y gana el clic, así que la franja arrastra **solo donde no hay
   nada**. Eso es exactamente lo que hace una barra de título de verdad, y es
   la razón de que `.os-center` reserve `--s-8` arriba — sin la reserva, el
   borde superior de los controles quedaría inerte.

   Sin fondo y sin borde: es una zona de gesto, no una superficie. Si algún día
   se ve, algo está mal. */
/* Los dos números viven AQUÍ y no en `tokens.css`, y es una decisión: no son
   escala del design system, son **geometría de macOS** —la altura de una barra
   de título y el ancho que ocupan los semáforos—. Un token de sistema que
   describe una convención de un sistema operativo mentiría en la primera
   página que se publique como artifact. Por eso llevan el prefijo del chasis.
   28px es la altura real de la franja en una ventana estándar; 78px cubre los
   tres semáforos con aire. */
:root {
  --os-drag-h: 28px;
  --os-drag-semaforos: 78px;
}

.os-drag {
  position: fixed;
  inset: 0 0 auto 0;
  height: var(--os-drag-h);
  z-index: 1;
  /* El cursor NO cambia: una barra de título de macOS tampoco lo cambia, y un
     cursor distinto en una franja invisible se lee como un borde de resize. */
}

/* Los semáforos viven en la esquina superior izquierda y son del sistema: el
   arrastre no debe robarles el clic. macOS los pinta ENCIMA de la ventana, así
   que el hueco es por si un día la franja creciera hacia ellos. */
.os-drag::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: var(--os-drag-semaforos);
  pointer-events: none;
}


/* ============================================================
   FLECHA Y NO MANITA — los controles propios del OS
   ============================================================
   Espejo de la regla del design system: en macOS un control de app usa
   FLECHA, y su rótulo no se arrastra como si fuera un documento. La manita
   grita «soy un link» y este OS no tiene ni un `<a href>` con estilo propio.

   **La frontera es el CONTROL, no la pantalla.** El contenido —una nota, la
   respuesta del agente, una celda, una cifra— se sigue seleccionando: eso es
   texto que uno quiere copiar.

   Y la excepción, que vive donde toca y no aquí: cinco elementos se quedaron
   con la manita porque era su ÚNICA señal de ser clicables (no tienen hover
   ni borde). Cada uno lo dice en su propia regla. El día que ganen hover,
   vuelven a la flecha. */
:where(
  .fin-cat, .gw-add, .gw-ax, .gw-row, .gw-opt, button.hm-card, .hm-note,
  .intel-n, .intel-ref--ir, .lv-ref--ir, .os-chats__now, .os-chatrow__t,
  .os-note, .os-tag__x, .os-tagadd, .os-slash__i, .os-wallswatch,
  button.os-mod__api, .os-skill__a, .rel-p, .rel-sin__p, .rel-chip__go,
  .rel-x, .rel-pact__t, .rel-pact__mas, .rel-note__go,
  .wk-card, .wk-more, .wk-bar, .wk-mile, .wk-off, .wk-pill, .wk-sel
) {
  cursor: default;
  user-select: none;
  -webkit-user-select: none;
}
