/* ============================================================================
   Valdra · Consola del cliente — sistema visual

   Los tokens de color son los de docs/brand/tokens.css, copiados aquí en lugar
   de importados: ese archivo no se sirve como estático y un @import remoto sería
   justo la dependencia de red que la plataforma promete no tener. Cuando cambie
   la paleta se cambia en los dos sitios; es el precio de poder demostrar la
   consola con el cable desconectado.

   Reglas del sistema, en orden de importancia:

   1. Un acento y uno solo: ámbar. Si hace falta un segundo color para
      distinguir dos cosas, lo que está mal es el diseño y no la paleta.
      Distribución de superficie 70 neutro / 20 canvas / 7 ámbar / 3 estado. El
      degradado de marca aparece solo en el logotipo. Esto lo abre un empleado
      de banco durante ocho horas; una aplicación de IA de consumo lo cansaría
      en veinte minutos.
   2. El ámbar de marca #F59E0B NO va sobre superficie clara: da 2,06:1 y no es
      legible. Sobre claro se usa --accent, que vale #B45309. Esta regla se
      rompe sola en cuanto alguien escribe un color a mano en una vista, así que
      en las vistas no hay colores literales: hay variables.
   3. Prohibido, y es literal: degradados morados o violetas, glassmorphism,
      resplandores, iconos de chispa, cerebro, varita o robot, emoji como
      iconografía funcional, y animaciones que retrasen una acción.
   4. Densidad alta pero legible. Un banco quiere ver la conversación, no aire.
   5. Los efectos son funcionales y cortos: realce de fila, anillo de foco,
      sombra de superficie elevada, 120–160 ms.

   Este archivo es HERMANO de admin/app.css, no compartido. Las clases se llaman
   igual a propósito —quien haya escrito una vista del portal escribe una de la
   consola sin releer nada— pero evolucionan por separado.
   ============================================================================ */

:root {
  color-scheme: light dark;

  /* ── Marca ─────────────────────────────────────────────────────────────────
     El ámbar de marca y el ámbar utilizable son DOS variables distintas, y esa
     separación no es un capricho de nomenclatura: medido sobre --bg, #F59E0B da
     2,06:1. No es legible, no lo es «casi», y no hay tamaño de letra que lo
     arregle. Sobre el canvas oscuro el mismo ámbar da 9,37:1.

     Consecuencia: --brand-amber solo lo lleva el núcleo del isotipo, que es
     idéntico en los dos temas porque el manual prohíbe recolorear la marca.
     Todo lo demás —texto, borde activo, enlace, icono— usa --accent, que baja
     a ámbar profundo en claro y sube al ámbar de marca en oscuro. */
  --brand-amber: #F59E0B;
  --accent:       #B45309;   /* 4,81:1 sobre --bg */
  --accent-hover: #92400E;
  --accent-deep:  #78350F;
  --accent-soft:  #FCD34D;
  --ink:          #111827;
  --canvas:       #030712;
  --grad: linear-gradient(135deg,#FCD34D 0%,#F59E0B 40%,#B45309 72%,#78350F 100%);

  /* ── Neutros ───────────────────────────────────────────────────────────── */
  --bg:            #F9FAFB;
  --surface:       #FFFFFF;
  --surface-2:     #F3F4F6;
  --surface-3:     #E5E7EB;
  --border:        #E5E7EB;
  --border-strong: #D1D5DB;
  --text:          #111827;
  --text-2:        #4B5563;
  --text-muted:    #6B7280;
  --disabled:      #D1D5DB;

  /* ── Semántica ─────────────────────────────────────────────────────────────
     El manual de marca no tiene paleta de estado: éxito, aviso y error se
     resuelven en ámbar y neutros. Aquí sí la hay, y es una desviación asumida:
     esto no es la web de la marca, es la pantalla donde alguien distingue un
     nodo caído de un lote pendiente durante ocho horas seguidas. El color va
     siempre acompañado de texto, nunca solo. */
  --success: #15803D; --success-bg: #F0FDF4;
  --warning: #B45309; --warning-bg: #FFFBEB;
  --error:   #B91C1C; --error-bg:   #FEF2F2;
  --info:    #4B5563; --info-bg:    #F3F4F6;

  --class-confidential: #A16207;

  /* Fondo de la mitad de marca de la puerta de acceso. Un blanco con un punto
     de calidez, medio grado por delante de --surface: separa el panel del
     formulario sin necesidad de meter una línea más. */
  --gate-aside-bg: #FDFCFB;

  /* ── Barra lateral ─────────────────────────────────────────────────────────
     Canvas casi negro en los dos temas: es la superficie nativa de la marca y
     la constante que hace reconocible la aplicación al cambiar de tema. Sobre
     ella el ámbar de marca sí cumple, así que --rail-accent es el puro. */
  --rail-bg:      #030712;
  --rail-line:    rgba(255,255,255,.08);
  --rail-text:    #9CA3AF;
  --rail-text-hi: #F9FAFB;
  --rail-muted:   #6B7280;
  --rail-hover:   rgba(255,255,255,.06);
  --rail-active:  rgba(245,158,11,.14);
  --rail-accent:  #F59E0B;

  /* ── Tipografía ────────────────────────────────────────────────────────── */
  --sans: system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
  --mono: ui-monospace,"SF Mono",SFMono-Regular,"JetBrains Mono",Menlo,Consolas,monospace;

  /* ── Escala de espaciado de 4 px ───────────────────────────────────────── */
  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px;
  --s5: 20px; --s6: 24px; --s7: 32px; --s8: 40px; --s9: 56px;

  /* ── Radios ────────────────────────────────────────────────────────────── */
  --r1: 4px; --r2: 6px; --r3: 8px; --r4: 10px; --r5: 12px;

  /* ── Elevación: dos niveles y ninguno de color ─────────────────────────── */
  --shadow:    0 1px 2px rgba(3,7,18,.05);
  --shadow-2:  0 1px 2px rgba(3,7,18,.06), 0 8px 24px -16px rgba(3,7,18,.24);
  --shadow-lg: 0 12px 40px -14px rgba(3,7,18,.30);

  /* ── Movimiento ────────────────────────────────────────────────────────── */
  --mo: 140ms cubic-bezier(.2,.6,.3,1);
  --mo-fast: 120ms cubic-bezier(.2,.6,.3,1);

  --ring: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
  --canvas-max: 1600px;
  --rail-w: 236px;
  --top-h: 48px;
}

/* ── Tema oscuro ─────────────────────────────────────────────────────────────
   Aquí el tema oscuro NO es una inversión del claro: es el original. El canvas
   casi negro con el acento ámbar es la superficie que define la marca, y el
   tema claro es la traducción para quien trabaja en una sucursal con la
   persiana subida.

   Va escrito dos veces a propósito. Una bajo prefers-color-scheme, para quien
   no ha elegido nada; otra bajo [data-theme="dark"], para quien sí. Definir el
   valor una sola vez dentro de la media query dejaría el conmutador manual sin
   nada que sobreescribir al pedir oscuro desde un sistema en claro, y esa es la
   mitad de los casos.                                                          */

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg:            #030712;
    --surface:       #0B1220;
    --surface-2:     #111A2B;
    --surface-3:     #1B2436;
    --border:        rgba(255,255,255,.08);
    --border-strong: rgba(255,255,255,.16);
    --text:          #F9FAFB;
    --text-2:        #D1D5DB;
    --text-muted:    #9CA3AF;
    --disabled:      #4B5563;

    /* Sobre el canvas el ámbar de marca da 9,37:1 y ya no hace falta rebajarlo:
       este es el único tema donde el acento de la interfaz y el de la marca son
       exactamente el mismo color. */
    --accent:       #F59E0B;
    --accent-hover: #FCD34D;
    --accent-deep:  #B45309;

    --success: #4ADE80; --success-bg: rgba(74,222,128,.12);
    --warning: #FBBF24; --warning-bg: rgba(251,191,36,.12);
    --error:   #F87171; --error-bg:   rgba(248,113,113,.12);
    --info:    #9CA3AF; --info-bg:    rgba(255,255,255,.06);

    --class-confidential: #FCD34D;
    --gate-aside-bg: #0A101E;

    /* La barra lateral comparte color con el lienzo, así que se separa por una
       línea y no por un tono: sobre fondo oscuro una sombra no se ve y sí
       ensucia el borde. */
    --rail-bg:   #030712;
    --rail-line: rgba(255,255,255,.10);

    --shadow:    0 1px 2px rgba(0,0,0,.40);
    --shadow-2:  0 1px 2px rgba(0,0,0,.40), 0 8px 26px -18px rgba(0,0,0,.70);
    --shadow-lg: 0 14px 46px -16px rgba(0,0,0,.72);
  }
}

:root[data-theme="dark"] {
  --bg:            #030712;
  --surface:       #0B1220;
  --surface-2:     #111A2B;
  --surface-3:     #1B2436;
  --border:        rgba(255,255,255,.08);
  --border-strong: rgba(255,255,255,.16);
  --text:          #F9FAFB;
  --text-2:        #D1D5DB;
  --text-muted:    #9CA3AF;
  --disabled:      #4B5563;

  --accent:       #F59E0B;
  --accent-hover: #FCD34D;
  --accent-deep:  #B45309;

  --success: #4ADE80; --success-bg: rgba(74,222,128,.12);
  --warning: #FBBF24; --warning-bg: rgba(251,191,36,.12);
  --error:   #F87171; --error-bg:   rgba(248,113,113,.12);
  --info:    #9CA3AF; --info-bg:    rgba(255,255,255,.06);

  --class-confidential: #FCD34D;
  --gate-aside-bg: #0A101E;

  --rail-bg:   #030712;
  --rail-line: rgba(255,255,255,.10);

  --shadow:    0 1px 2px rgba(0,0,0,.40);
  --shadow-2:  0 1px 2px rgba(0,0,0,.40), 0 8px 26px -18px rgba(0,0,0,.70);
  --shadow-lg: 0 14px 46px -16px rgba(0,0,0,.72);
}

/* ── Base ──────────────────────────────────────────────────────────────────── */

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: 13.5px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-variant-numeric: tabular-nums;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r1);
}
[hidden] { display: none !important; }

h1, h2, h3 { margin: 0; }
h1 { font-size: 22px; font-weight: 650; letter-spacing: -.022em; line-height: 1.25; }
h2 { font-size: 14px; font-weight: 620; letter-spacing: -.008em; }
h3 { font-size: 13px; font-weight: 620; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 2px; }

::selection { background: color-mix(in srgb, var(--accent) 24%, transparent); }

/* Una sola declaración de movimiento reducido, al final de la base: quien pide
   no ver animaciones no quiere excepciones repartidas por la hoja. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--surface); color: var(--text);
  padding: var(--s2) var(--s4);
  border: 1px solid var(--border); border-radius: 0 0 var(--r3) 0;
}
.skip:focus { left: 0; }

/* ── Utilidades ────────────────────────────────────────────────────────────── */

.mono { font-family: var(--mono); font-size: .94em; }
.xs   { font-size: 11.5px; }
.t2   { color: var(--text-2); }
.t3   { color: var(--text-muted); }
.right  { text-align: right; }
.center { text-align: center; }
.spacer { margin-left: auto; }
.block  { width: 100%; }
.num {
  font-family: var(--mono); font-variant-numeric: tabular-nums;
  text-align: right; white-space: nowrap; font-size: .95em;
}
.stack   { display: flex; flex-direction: column; gap: var(--s3); }
.rowline { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }
.sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap;
}

/* ── Marca ─────────────────────────────────────────────────────────────────── */

.brand { display: flex; align-items: center; gap: var(--s3); min-width: 0; }

/* El isotipo va en SVG y no en PNG por dos razones, y la segunda es la que manda:
   a 28 px un raster se ve blando, y el perímetro tiene que cambiar de color con
   el tema. El SVG hereda currentColor en el trazo y deja el núcleo en ámbar de
   marca, que es lo que el manual exige que no cambie nunca.

   El hexágono es más alto que ancho (razón 0,83), así que la caja lo es también:
   forzarlo a un cuadrado lo dejaría flotando en un carril de aire a los lados. */
.mark {
  width: 26px; height: 31px; flex-shrink: 0;
  color: var(--rail-text-hi);
  font-size: 0;
}
.mark svg { display: block; width: 100%; height: 100%; }

.wm {
  font-size: 16px; font-weight: 700; letter-spacing: -.02em; line-height: 1.1;
  color: var(--rail-text-hi); white-space: nowrap;
}
/* Wordmark en una sola tinta. El manual es explícito: los modelos se nombran en
   tipografía, no se señalan recoloreando la marca; partir «Valdra» en dos
   colores sería justo eso. */
.tag {
  font-size: 10px; letter-spacing: .09em; text-transform: uppercase;
  color: var(--rail-muted); margin-top: 2px; white-space: nowrap;
}
.brand-lg .mark { width: 32px; height: 38px; color: var(--text); }
.brand-lg .wm   { font-size: 20px; color: var(--text); }
.brand-lg .tag  { color: var(--text-muted); font-size: 11px; }

/* ── Puerta de acceso ──────────────────────────────────────────────────────────
   Una lámina apoyada sobre el fondo, y dentro dos mitades: identidad a la
   izquierda, formulario a la derecha. El patrón es el de una consola corporativa
   y no el de una aplicación de consumo, y esa diferencia es deliberada: quien
   entra aquí administra dinero ajeno.

   Las dos mitades siguen al tema. La versión anterior dejaba el panel de marca
   en canvas oscuro siempre; se cambió porque en tema claro el resultado era una
   pantalla partida por la mitad en dos temas distintos, y porque el manual
   permite ámbar profundo sobre superficie clara justamente para esto.

   Lo que NO hay en esta pantalla, y es una decisión y no un olvido: no hay
   «¿olvidaste tu contraseña?» porque la plataforma no tiene recuperación por
   autoservicio —el alta y la reposición las hace el propietario de la cuenta de
   la organización—, y no hay botones de SSO ni de Microsoft porque no hay
   proveedor de identidad federada conectado. Un botón que no lleva a ninguna
   parte en una pantalla de acceso no es un adorno: es una llamada al soporte.  */

.gate {
  position: fixed; inset: 0; overflow: auto;
  background: var(--bg);
  display: grid; place-items: center;
  padding: var(--s6);
}

/* La lámina. Se le pone un ancho máximo y no se deja crecer: un formulario de
   acceso a 2560 px de ancho separa tanto la etiqueta del campo que deja de
   leerse como una pareja. */
.gate-shell {
  width: 100%; max-width: 76rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r5);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.gate-split {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}

/* ── Mitad de marca ───────────────────────────────────────────────────────── */

.gate-aside {
  padding: var(--s8) var(--s7);
  display: flex; flex-direction: column; gap: var(--s5);
  position: relative; overflow: hidden;
  border-right: 1px solid var(--border);
  background: var(--gate-aside-bg, #FDFCFB);
}
/* Dos manchas de ámbar muy diluido. El manual admite «vidrio sobre una mancha»
   como recurso de profundidad y prohíbe el resplandor; la diferencia práctica es
   que la mancha se queda quieta detrás del contenido y el resplandor persigue a
   la marca. Ninguna toca el logotipo. */
.gate-aside::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(42rem 24rem at 88% 6%, rgba(245,158,11,.10), transparent 62%),
    radial-gradient(34rem 22rem at 4% 96%, rgba(180,83,9,.07), transparent 64%);
}
.gate-aside > * { position: relative; z-index: 1; }

/* flex: none es obligatorio, no cosmético: el panel es una columna flex y sin él
   el navegador encoge la caja del logotipo hasta cero para hacer sitio al resto.
   El síntoma es un panel correcto y sin marca, que es peor que un panel roto
   porque parece intencionado.

   La razón de aspecto sale del archivo real (640×207). Dársela redonda dejaría
   el logotipo nadando en una caja vacía, y ese aire de más se leería como parte
   del espacio de respeto de la marca, que es otra cosa y mide otra cosa. */
.gate-logo {
  flex: none;
  width: 11.5rem; aspect-ratio: 640 / 207;
  background: url("/assets/logo.png") left center / contain no-repeat;
}
/* En oscuro el logotipo cambia de archivo y no de filtro. Un invert() sobre el
   original convertiría el ámbar en azul, que es exactamente el color del que la
   marca huye. */
:root[data-theme="dark"] .gate-logo { background-image: url("/assets/logo-dark.png"); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .gate-logo { background-image: url("/assets/logo-dark.png"); }
}

/* Filete corto sobre el titular: la misma pieza que el logotipo lleva a los
   lados del descriptor. Marca dónde empieza el mensaje. */
.gate-rule {
  width: 2.25rem; height: 3px; border-radius: 2px;
  background: var(--accent); flex: none;
}

/* El descriptor de marca, y en inglés a propósito: «Sovereign Private AI» es el
   descriptor, no el mensaje. El mensaje —«Tu modelo. Tus muros.»— sí se traduce,
   y vive debajo, en el diccionario. Traducir el descriptor lo convertiría en una
   frase distinta en cada mercado, que es lo contrario de un descriptor. */
.gate-claim {
  margin: 0; max-width: 11ch;
  font-size: clamp(1.9rem, 3vw, 2.55rem); line-height: 1.06;
  letter-spacing: -.035em; font-weight: 800; color: var(--text);
  text-wrap: balance;
}
.gate-claim em {
  display: block; font-style: normal; color: var(--accent);
}
.gate-sell {
  margin: 0; max-width: 36ch; font-size: .97rem; line-height: 1.55;
  color: var(--text-2); text-wrap: pretty;
}

/* La ilustración carga el peso visual del panel y por eso se le deja consumir el
   espacio sobrante en lugar de fijarle una altura: entre 900 px y 1400 px de
   ventana el panel crece bastante, y un dibujo de altura fija dejaría un hueco
   creciente encima de las garantías. */
.gate-art {
  flex: 1 1 auto; min-height: 8rem;
  display: grid; place-items: center;
  margin: var(--s2) 0;
}
.gate-art img { width: 100%; max-width: 24rem; height: 100%; object-fit: contain; }

/* Las tres garantías, en fila. Son el resumen de lo que se promete y por eso van
   pegadas al pie del panel: es lo último que se lee antes de mirar al
   formulario. */
.gate-points {
  list-style: none; margin: 0; padding: var(--s5) 0 0; border-top: 1px solid var(--border);
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s4);
}
.gate-points li { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.gate-points .pt-ico {
  width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center;
  border: 1px solid var(--border); background: var(--surface);
  color: var(--accent); flex: none;
}
.gate-points .pt-ico svg { width: 15px; height: 15px; }
.gate-points strong { font-size: .82rem; font-weight: 620; color: var(--text); }
.gate-points span { font-size: .78rem; line-height: 1.4; color: var(--text-muted); text-wrap: pretty; }

.gate-legal {
  margin: 0; font-family: var(--mono); font-size: .68rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--text-muted);
}

/* ── Mitad de formulario ──────────────────────────────────────────────────── */

.gate-main {
  display: flex; flex-direction: column;
  padding: var(--s6) var(--s7) var(--s7);
  background: var(--bg);
}
/* Los mandos de idioma y tema, arriba y a la derecha, antes del formulario. Van
   aquí y no debajo por orden de uso: quien abre esto de noche o no lee español
   necesita arreglar eso ANTES de escribir su contraseña, no después de fallar al
   escribirla. */
.gate-bar {
  display: flex; justify-content: flex-end; gap: var(--s2);
  margin-bottom: auto; flex-wrap: wrap;
}

/* La tarjeta lleva superficie y borde propios. Sin ellos los campos flotaban
   directamente sobre el panel, y el formulario se leía como un montón de cajas
   sueltas en vez de como una unidad: no había nada que dijera dónde empieza y
   dónde acaba lo que hay que rellenar. */
.gate-card {
  width: 100%; max-width: 24rem; margin: var(--s6) auto auto;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r5);
  box-shadow: var(--shadow-2);
  padding: var(--s7) var(--s6) var(--s6);
  display: flex; flex-direction: column; gap: var(--s4);
}

/* El candado. Es el único icono decorativo de la pantalla y se gana el sitio:
   dice de qué va esto antes de que nadie lea el titular. aria-hidden en el
   marcado — para un lector de pantalla, «candado» delante de «Entrar a tu
   consola» no añade nada. */
.gate-badge {
  width: 46px; height: 46px; border-radius: 50%; margin: 0 auto;
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent);
}
.gate-badge svg { width: 20px; height: 20px; }

.gate-card h1 {
  margin: 0; font-size: 1.5rem; letter-spacing: -.028em; font-weight: 700;
  text-wrap: balance; color: var(--text); text-align: center;
}
.gate-sub {
  margin: 0; color: var(--text-2); font-size: .92rem; text-align: center;
  text-wrap: pretty;
}
.gate-form { display: flex; flex-direction: column; gap: var(--s4); }
.gate-form .field { margin-bottom: 0; }

/* Campo con icono a la izquierda y, en la contraseña, botón a la derecha. El
   relleno lateral del input se amplía en la misma medida que ocupa el adorno:
   si se superpusiera sin reservar sitio, el texto pasaría por debajo del icono
   en cuanto alguien escribiera un correo largo. */
.gate-field { position: relative; display: flex; align-items: center; }
.gate-field > svg {
  position: absolute; left: 11px; width: 16px; height: 16px;
  color: var(--text-muted); pointer-events: none;
}
.gate-field input { padding: 9px 11px 9px 34px; }
.gate-field.has-toggle input { padding-right: 38px; }
.gate-field:focus-within > svg { color: var(--accent); }

/* Mostrar la contraseña. Existe porque la alternativa real no es escribirla bien
   a ciegas: es escribirla en el campo de correo para verla, y eso la manda al
   servidor en claro dentro del usuario y la deja en el historial del navegador. */
.gate-reveal {
  position: absolute; right: 6px;
  display: grid; place-items: center; width: 28px; height: 28px;
  border: 0; border-radius: var(--r2); background: none; cursor: pointer;
  color: var(--text-muted);
  transition: color var(--mo-fast), background var(--mo-fast);
}
.gate-reveal:hover { color: var(--text); background: var(--surface-2); }
.gate-reveal svg { width: 16px; height: 16px; }

.gate-submit { padding: 10px var(--s4); font-size: 14px; }
.gate-submit svg { width: 16px; height: 16px; }

/* Sin tono es un error —así lo llama app.js cuando el acceso falla— y con .info
   es una explicación. El borde lateral hace el trabajo que el color por sí solo
   no puede hacer para quien no distingue rojo de gris. */
.gate-msg {
  margin: 0; padding: var(--s3) var(--s4);
  border-radius: var(--r2); border-left: 3px solid var(--error);
  background: var(--error-bg); color: var(--error);
  font-size: .9rem; text-wrap: pretty;
}
.gate-msg.info {
  border-left-color: var(--border-strong);
  background: var(--surface-2); color: var(--text-2);
}
.gate-foot {
  margin: 0; font-size: .85rem; color: var(--text-muted);
  text-align: center; text-wrap: pretty;
}

/* Un <button> que se lee como enlace. Es un botón y no un <a> porque no navega
   a ninguna parte: cambia de formulario dentro de la misma pantalla, y un enlace
   sin destino es una promesa que el teclado no cumple. La clase existía en el
   marcado de la puerta desde el principio pero no estaba definida en ninguna
   hoja, así que estos botones salían con el estilo nativo del navegador — un
   rectángulo gris en mitad de un párrafo. */
.linkish {
  border: 0; background: none; padding: 0;
  font: inherit; color: var(--accent); font-weight: 550;
  cursor: pointer; text-decoration: underline; text-underline-offset: 2px;
  text-decoration-thickness: 1px;
}
.linkish:hover { color: var(--accent-hover); }

/* ── Pie de la lámina ─────────────────────────────────────────────────────── */

.gate-footbar {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s4);
  padding: var(--s4) var(--s7);
  border-top: 1px solid var(--border);
  background: var(--surface-2);
  font-size: .78rem; color: var(--text-muted);
}
.gate-footbar p { margin: 0; }
.gate-footbar .mark { width: 18px; height: 21px; color: var(--accent); }
/* Aviso de «hace falta JavaScript». Reutiliza .gate y .gate-card pero sin la
   lámina de dos mitades: no hay formulario que enmarcar, solo una frase que
   explica por qué la pantalla está vacía. El margen automático de la tarjeta,
   que en la puerta la empuja hacia arriba, aquí la descentraría. */
noscript .gate-card { margin: 0 auto; max-width: 30rem; text-align: center; }

/* ── Adaptación ───────────────────────────────────────────────────────────── */

/* Por debajo de 62rem las dos mitades se apilan: mantenerlas en columnas dejaría
   el formulario en una franja donde no cabe escribir. Lo que sobrevive es el
   logotipo y el mensaje; la ilustración y las garantías se retiran, porque en un
   móvil son un obstáculo entre el usuario y el formulario. */
@media (max-width: 62rem) {
  .gate { padding: 0; place-items: stretch; }
  .gate-shell { max-width: none; border: 0; border-radius: 0; box-shadow: none; min-height: 100%; }
  .gate-split { grid-template-columns: 1fr; }
  .gate-aside {
    padding: var(--s5) var(--s5) var(--s6);
    gap: var(--s4);
    border-right: 0; border-bottom: 1px solid var(--border);
  }
  .gate-art, .gate-points, .gate-legal { display: none; }
  .gate-claim { font-size: 1.75rem; max-width: none; }
  .gate-claim em { display: inline; }
  .gate-sell { display: none; }
  .gate-logo { width: 8.5rem; }
  .gate-main { padding: var(--s5) var(--s4) var(--s7); background: var(--surface); }
  .gate-card {
    margin-top: var(--s4); padding: 0;
    background: none; border: 0; box-shadow: none;
  }
  .gate-footbar { padding: var(--s4) var(--s5); }
}

/* ── Armazón ───────────────────────────────────────────────────────────────── */

.app { display: grid; grid-template-columns: var(--rail-w) minmax(0, 1fr); min-height: 100vh; }
@media (max-width: 960px) { .app { grid-template-columns: 1fr; } }

.rail {
  background: var(--rail-bg);
  border-right: 1px solid var(--rail-line);
  display: flex; flex-direction: column;
  padding: var(--s4) var(--s3) var(--s3);
  gap: var(--s4);
  position: sticky; top: 0; height: 100vh; min-height: 0;
}
@media (max-width: 960px) { .rail { position: static; height: auto; } }

/* ── Navegación ────────────────────────────────────────────────────────────── */

/* Tres grupos rotulados y no una lista de ocho. Mientras fueron cinco entradas
   la lista era lo correcto —cinco cosas se leen de un vistazo—, pero al entrar
   Flujos, Aprobaciones e Integraciones la lista dejó de tener forma. Los grupos
   nombran el oficio, no el módulo del backend: lo que se opera a diario, lo que
   se conecta con el banco y lo que se administra. */
.nav {
  display: flex; flex-direction: column; gap: var(--s4);
  overflow-y: auto; min-height: 0; flex: 1;
  scrollbar-width: thin;
  scrollbar-color: rgba(255,255,255,.16) transparent;
}
.nav::-webkit-scrollbar { width: 6px; }
.nav::-webkit-scrollbar-thumb { background: rgba(255,255,255,.16); border-radius: 3px; }

.nav-group { display: flex; flex-direction: column; gap: 1px; }
/* Los grupos se separan con una línea y no con más aire: en una barra estrecha
   el aire solo se lee como hueco, y la línea sí dice «aquí empieza otra cosa». */
.nav-group + .nav-group { border-top: 1px solid var(--rail-line); padding-top: var(--s4); }
.nav-title {
  font-size: 10.5px; font-weight: 650; letter-spacing: .10em; text-transform: uppercase;
  color: var(--rail-muted); padding: 0 var(--s2); margin: 0 0 var(--s2);
}

.nav button {
  position: relative; display: flex; align-items: center; gap: var(--s2);
  width: 100%; padding: 7px var(--s2); border: 0; border-radius: var(--r2);
  background: none; color: var(--rail-text); font: inherit; font-size: 13px;
  cursor: pointer; text-align: left;
  transition: background var(--mo-fast), color var(--mo-fast);
}
.nav button:hover { background: var(--rail-hover); color: var(--rail-text-hi); }
.nav button[aria-current="page"] {
  background: var(--rail-active); color: var(--rail-text-hi); font-weight: 600;
}
.nav button[aria-current="page"]::before {
  content: ""; position: absolute; left: 0; top: 6px; bottom: 6px;
  width: 2px; border-radius: 0 2px 2px 0; background: var(--rail-accent);
}
.nav .ico { width: 16px; height: 16px; flex-shrink: 0; opacity: .72; transition: opacity var(--mo-fast); }
.nav button:hover .ico,
.nav button[aria-current="page"] .ico { opacity: 1; }
.nav button[aria-current="page"] .ico { color: var(--rail-accent); }
.nav .label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Punto de aviso: la ruta existe en el menú pero su script no se registró. */
.nav .dot {
  width: 5px; height: 5px; border-radius: 50%; background: var(--warning);
  margin-left: auto; flex-shrink: 0;
}

/* ── Pie de la barra lateral ───────────────────────────────────────────────── */

.rail-foot {
  display: flex; flex-direction: column; gap: var(--s2);
  border-top: 1px solid var(--rail-line); padding-top: var(--s3);
}
.who { font-size: 11.5px; color: var(--rail-muted); line-height: 1.45; min-width: 0; }
.who b {
  color: var(--rail-text-hi); font-weight: 600; font-size: 12.5px;
  display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.who .mail, .who .org {
  display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.who .org { color: var(--rail-text); margin-top: 2px; }
.who .role {
  display: inline-block; margin-top: var(--s1); padding: 1px 6px;
  border-radius: var(--r1); border: 1px solid color-mix(in srgb, var(--accent-soft) 55%, transparent);
  color: var(--accent-soft); font-size: 10.5px; font-weight: 600;
  font-family: var(--mono); letter-spacing: -.01em;
}

/* Selectores de la barra lateral: entorno e idioma. Nombres, nunca banderas —
   una bandera es un país y no una lengua, y en portugués de Brasil se nota. */
.picker { position: relative; display: block; }
.picker > svg {
  position: absolute; left: 8px; top: 50%; transform: translateY(-50%);
  width: 14px; height: 14px; color: var(--rail-muted); pointer-events: none;
}
.picker::after {
  content: ""; position: absolute; right: 10px; top: 50%;
  width: 6px; height: 6px; margin-top: -4px; pointer-events: none;
  border-right: 1.5px solid var(--rail-muted); border-bottom: 1.5px solid var(--rail-muted);
  transform: rotate(45deg);
}
.picker select {
  appearance: none; -webkit-appearance: none;
  width: 100%; padding: 6px 24px 6px 27px;
  background: rgba(255,255,255,.05); border: 1px solid var(--rail-line);
  border-radius: var(--r2); color: var(--rail-text);
  font-family: var(--sans); font-size: 12.5px; cursor: pointer;
  transition: background var(--mo-fast), border-color var(--mo-fast);
}
.picker select:hover { background: rgba(255,255,255,.09); color: var(--rail-text-hi); }
.picker select option { background: var(--rail-bg); color: var(--rail-text-hi); }

.rail-foot .btn.ghost {
  background: rgba(255,255,255,.05); border-color: var(--rail-line); color: var(--rail-text);
}
.rail-foot .btn.ghost:hover {
  background: rgba(255,255,255,.10); border-color: rgba(255,255,255,.2); color: var(--rail-text-hi);
}
.rail-link {
  background: none; border: 0; font: inherit;
  color: var(--rail-muted); font-size: 12px; cursor: pointer;
  text-align: center; padding: var(--s1) 0;
  transition: color var(--mo-fast);
}
.rail-link:hover { color: var(--rail-text-hi); }

/* ── Zona principal ────────────────────────────────────────────────────────── */

.main { min-width: 0; display: flex; flex-direction: column; }

.top {
  height: var(--top-h); padding: 0 var(--s6);
  display: flex; align-items: center; gap: var(--s3);
  background: var(--surface); border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 5;
}
@media (max-width: 960px) { .top { padding: 0 var(--s4); } }
.crumb {
  font-size: 12.5px; color: var(--text-muted); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.crumb b { color: var(--text); font-weight: 600; }
.actions { display: flex; gap: var(--s2); align-items: center; flex-shrink: 0; }

.canvas {
  width: 100%; max-width: var(--canvas-max); margin: 0 auto;
  padding: var(--s5) var(--s6) var(--s9);
}
@media (max-width: 960px) { .canvas { padding: var(--s4) var(--s4) var(--s8); } }

/* Lienzo a sangre: la conversación ocupa el alto entero y gestiona su propio
   desplazamiento. Un chat metido en una página que también hace scroll produce
   dos barras que compiten, y el mensaje que llega empuja la pantalla. */
.canvas.full {
  max-width: none; padding: 0;
  height: calc(100vh - var(--top-h)); min-height: 0;
}
.canvas.full #view { height: 100%; min-height: 0; }

.canvas > .head { margin-bottom: var(--s5); }
.canvas h1 { margin-bottom: var(--s1); }
.sub { color: var(--text-2); font-size: 13px; margin: 0; max-width: 84ch; }
#view { outline: none; }

/* ── Tarjetas ──────────────────────────────────────────────────────────────── */

.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r4); box-shadow: var(--shadow-2);
  margin-bottom: var(--s4);
}
.card .hd {
  padding: var(--s3) var(--s4); border-bottom: 1px solid var(--border);
  display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
  background: var(--surface);
}
.card .hd h2 { font-size: 13px; font-weight: 620; }
.card .hd .n {
  font-family: var(--mono); font-size: 11px; color: var(--text-2);
  background: var(--surface-2); border: 1px solid var(--border);
  padding: 1px 6px; border-radius: var(--r1);
}
.card .bd { padding: var(--s4); }
/* El recorte va en el cuerpo pegado a los bordes —el que contiene una tabla— y
   no en la tarjeta entera: recortar la tarjeta también recortaría cualquier
   desplegable que una vista abra dentro de ella. */
.card .bd.tight { padding: 0; overflow: hidden; border-radius: 0 0 var(--r4) var(--r4); }
.card .hd:first-child { border-radius: var(--r4) var(--r4) 0 0; }

.panel {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r4);
  padding: var(--s7) var(--s6); text-align: center; box-shadow: var(--shadow);
}
.panel h2 { font-size: 15px; margin-bottom: var(--s2); }
.panel p { color: var(--text-2); font-size: 13px; margin: 0 auto; max-width: 68ch; }
.panel .btn { margin-top: var(--s4); }
.panel.deny {
  border-color: color-mix(in srgb, var(--warning) 38%, transparent);
  background: var(--warning-bg);
}

.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: var(--s3); }

/* ── Indicadores ───────────────────────────────────────────────────────────── */

.kpis {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--s3); margin-bottom: var(--s5);
}
@media (max-width: 1400px) { .kpis { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 1040px) { .kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px)  { .kpis { grid-template-columns: 1fr; } }

/* Las tarjetas de indicador no llevan sombra: son una franja de lectura, no
   objetos flotantes. La elevación se reserva para lo que de verdad está por
   encima del lienzo. */
.kpi {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r3); padding: var(--s3) var(--s4); min-width: 0;
}
.kpi .l {
  font-size: 12px; font-weight: 500; color: var(--text-2); margin: 0 0 var(--s1);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.kpi .v {
  font-size: 22px; font-weight: 650; letter-spacing: -.028em; line-height: 1.15;
  font-variant-numeric: tabular-nums; overflow: hidden; text-overflow: ellipsis;
}
.kpi .h { font-size: 11.5px; color: var(--text-muted); margin-top: var(--s1); line-height: 1.45; }
.kpi.ok    .v { color: var(--success); }
.kpi.warn  .v { color: var(--warning); }
.kpi.error .v { color: var(--error); }

/* ── Botones ───────────────────────────────────────────────────────────────── */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 7px var(--s3); border-radius: var(--r2);
  border: 1px solid var(--border); background: var(--surface); color: var(--text);
  font-family: inherit; font-size: 13px; font-weight: 550; line-height: 1.3;
  cursor: pointer; white-space: nowrap;
  transition: background var(--mo-fast), border-color var(--mo-fast), color var(--mo-fast);
}
.btn:hover { background: var(--surface-2); border-color: var(--border-strong); }
.btn:active { background: var(--surface-3); }
.btn.primary {
  background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 600;
}
.btn.primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
.btn.danger { background: var(--error); border-color: var(--error); color: #fff; font-weight: 600; }
.btn.danger:hover { background: color-mix(in srgb, var(--error) 88%, #000); }
.btn.ghost { background: transparent; }
.btn.ghost:hover { background: var(--surface-2); }
.btn.sm { padding: 4px var(--s2); font-size: 12.5px; }
.btn.block { width: 100%; }
.btn:disabled { opacity: .5; cursor: not-allowed; }
.btn svg { width: 14px; height: 14px; }

.iconbtn {
  background: none; border: 0; color: var(--text-muted); font-size: 17px; line-height: 1;
  cursor: pointer; padding: 2px 5px; margin-left: auto; border-radius: var(--r1);
  transition: color var(--mo-fast), background var(--mo-fast);
}
.iconbtn:hover { color: var(--text); background: var(--surface-2); }

/* Botón cuadrado con icono, para las acciones de una fila de conversación. */
.sqbtn {
  display: inline-grid; place-items: center; width: 28px; height: 28px;
  border: 1px solid transparent; border-radius: var(--r2);
  background: none; color: var(--text-muted); cursor: pointer;
  transition: color var(--mo-fast), background var(--mo-fast), border-color var(--mo-fast);
}
.sqbtn:hover { color: var(--text); background: var(--surface-2); border-color: var(--border); }
.sqbtn.danger:hover { color: var(--error); border-color: color-mix(in srgb, var(--error) 40%, transparent); }
.sqbtn svg { width: 15px; height: 15px; }

/* ── Distintivos ───────────────────────────────────────────────────────────── */

.pill {
  display: inline-flex; align-items: center; gap: var(--s1);
  font-size: 11.5px; font-weight: 600; letter-spacing: .005em;
  padding: 2px 7px; border-radius: var(--r1); border: 1px solid;
  white-space: nowrap; line-height: 1.5;
}
.pill.mono { font-family: var(--mono); font-size: 11px; }
.p-ok    { color: var(--success); border-color: color-mix(in srgb, var(--success) 38%, transparent); background: var(--success-bg); }
.p-warn  { color: var(--warning); border-color: color-mix(in srgb, var(--warning) 38%, transparent); background: var(--warning-bg); }
.p-error { color: var(--error);   border-color: color-mix(in srgb, var(--error) 38%, transparent);   background: var(--error-bg); }
.p-info  { color: var(--info);    border-color: color-mix(in srgb, var(--info) 38%, transparent);    background: var(--info-bg); }
.p-muted { color: var(--text-2);  border-color: var(--border); background: var(--surface-2); }

.chip {
  display: inline-flex; align-items: center; gap: var(--s1);
  font-family: var(--mono); font-size: 11.5px; color: var(--text-2);
  background: var(--surface-2); border: 1px solid var(--border);
  padding: 1px 6px; border-radius: var(--r1); white-space: nowrap;
}
.chip svg { width: 12px; height: 12px; }

/* ── Formularios ───────────────────────────────────────────────────────────── */

.field { margin-bottom: var(--s3); }
.field.full { grid-column: 1 / -1; }
label {
  display: block; font-size: 12px; font-weight: 550; color: var(--text-2);
  margin-bottom: var(--s1); letter-spacing: 0;
}
input, select, textarea {
  width: 100%; padding: 7px 10px;
  border: 1px solid var(--border); border-radius: var(--r2);
  background: var(--surface); color: var(--text);
  font-family: var(--sans); font-size: 13px; line-height: 1.4;
  transition: border-color var(--mo-fast), box-shadow var(--mo-fast);
}
input.mono, textarea.mono, select.mono { font-family: var(--mono); font-size: 12.5px; }
textarea { min-height: 96px; line-height: 1.55; resize: vertical; }
input:hover, select:hover, textarea:hover { border-color: var(--border-strong); }
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--accent); box-shadow: var(--ring);
}
input:disabled, select:disabled, textarea:disabled {
  background: var(--surface-2); color: var(--text-muted); cursor: not-allowed;
}
select { appearance: auto; }

.check { display: flex; align-items: flex-start; gap: var(--s2); }
.check input { width: auto; margin-top: 3px; }
.check label { font-size: 13px; font-weight: 500; color: var(--text); margin: 0; }
.hint { font-size: 12px; color: var(--text-muted); margin: var(--s1) 0 0; line-height: 1.45; }
.hint code, .hint .mono { font-family: var(--mono); font-size: 11.5px; }

.uf-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 0 var(--s4); }
.uf-err {
  margin: var(--s3) 0 0; padding: var(--s3); border-radius: var(--r2); font-size: 13px;
  border: 1px solid color-mix(in srgb, var(--error) 32%, transparent);
  background: var(--error-bg); color: var(--text-2);
}
.uf-foot { display: flex; gap: var(--s2); align-items: center; margin-top: var(--s4); flex-wrap: wrap; }

.toolbar {
  display: flex; align-items: flex-end; gap: var(--s3); flex-wrap: wrap;
  padding: var(--s3) var(--s4); border-bottom: 1px solid var(--border);
  background: var(--surface-2);
}
.toolbar .field { margin: 0; min-width: 160px; }
.toolbar .grow { flex: 1; min-width: 200px; }

/* ── Tablas ────────────────────────────────────────────────────────────────── */

.tw { overflow-x: auto; }
table.t { width: 100%; border-collapse: collapse; font-size: 13px; }
table.t caption {
  caption-side: top; text-align: left; font-size: 12px; color: var(--text-muted);
  padding: var(--s3) var(--s4) var(--s2);
}
table.t th {
  text-align: left; font-size: 12px; font-weight: 600; color: var(--text-2);
  padding: var(--s2) var(--s4); white-space: nowrap;
  background: var(--surface-2); border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 1;
}
table.t th.right { text-align: right; }
table.t td { padding: 7px var(--s4); border-bottom: 1px solid var(--border); vertical-align: middle; }
table.t td.right { text-align: right; }
table.t td.mono { font-family: var(--mono); font-size: 12px; }
table.t tbody tr { transition: background var(--mo-fast); }
table.t tbody tr:hover { background: var(--surface-2); }
table.t tbody tr:last-child td { border-bottom: 0; }
table.t tbody tr.clickable { cursor: pointer; }
table.t tbody tr.clickable:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
table.t tbody tr.is-empty:hover { background: transparent; }

th .sortbtn {
  display: inline-flex; align-items: center; gap: var(--s1);
  background: none; border: 0; padding: 0; font: inherit; color: inherit;
  cursor: pointer; transition: color var(--mo-fast);
}
th .sortbtn:hover { color: var(--text); }
th .arrow { font-size: 8px; opacity: .3; }
th[aria-sort] .arrow { opacity: 1; color: var(--accent); }
th[aria-sort] { color: var(--text); }

.pager {
  display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
  padding: var(--s2) var(--s4); border-top: 1px solid var(--border);
  background: var(--surface-2); font-size: 12.5px; color: var(--text-2);
}
.pager .grow { flex: 1; }

/* ── Estados vacíos ────────────────────────────────────────────────────────── */

.empty { padding: var(--s7) var(--s5); text-align: center; }
.empty > svg {
  width: 28px; height: 28px; color: var(--text-muted); opacity: .7;
  margin: 0 auto var(--s3); display: block;
}
.empty .et { font-size: 13.5px; font-weight: 600; margin: 0 0 var(--s1); color: var(--text); }
.empty .eh { font-size: 12.5px; color: var(--text-2); margin: 0 auto; max-width: 62ch; line-height: 1.5; }
.empty .btn { margin-top: var(--s4); }

.skel {
  display: block; height: 11px; border-radius: var(--r1); background: var(--surface-2);
  animation: pulse 1.5s ease-in-out infinite;
}
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: .5; } }

/* ── Gráficos ──────────────────────────────────────────────────────────────── */

.chart-host { width: 100%; min-width: 0; }
.chart { width: 100%; height: auto; display: block; overflow: visible; }
.chart .ax-line { stroke: var(--border); stroke-width: 1; }
.chart .ax-base { stroke: var(--border-strong); stroke-width: 1; }
.chart .ax-text { fill: var(--text-muted); font-family: var(--mono); font-size: 10px; }
.chart .ax-text.mid { text-anchor: middle; }
.chart .ax-text.end { text-anchor: end; }
.chart .guide { stroke: var(--border-strong); stroke-width: 1; stroke-dasharray: 3 3; }
.chart .serie { fill: none; stroke-width: 1.75; stroke-linejoin: round; stroke-linecap: round; }
/* El área bajo la línea da volumen sin competir con ella: a más del 10 % de
   opacidad la serie deja de leerse y el gráfico se vuelve decorativo. */
.chart .area { stroke: none; fill-opacity: .08; }
.chart .dot { stroke: var(--surface); stroke-width: 2; }
.chart .marker { stroke: var(--surface); stroke-width: 1.5; }
.chart .bar { transition: opacity var(--mo-fast); }
.chart .bar:hover { opacity: .78; }
.chart .tip { pointer-events: none; }
.chart .tip-bg { fill: var(--surface); stroke: var(--border-strong); stroke-width: 1; }
.chart .tip-t { fill: var(--text); font-family: var(--mono); font-size: 10.5px; }
.chart .tip-t.mute { fill: var(--text-muted); }

.chart-empty {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--s2); min-height: 148px; padding: var(--s6) var(--s5); text-align: center;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r3);
}
.chart-empty svg { width: 26px; height: 26px; color: var(--text-muted); opacity: .7; }
.chart-empty .et { font-size: 13px; font-weight: 600; margin: 0; color: var(--text); }
.chart-empty .eh { font-size: 12.5px; color: var(--text-2); margin: 0; max-width: 56ch; line-height: 1.5; }

.chart-legend {
  display: flex; gap: var(--s4); flex-wrap: wrap; margin-top: var(--s2);
  font-size: 12px; color: var(--text-2);
}
.chart-legend span.sw {
  display: inline-block; width: 10px; height: 3px; border-radius: 2px;
  vertical-align: middle; margin-right: 6px;
}

/* ── Avisos ────────────────────────────────────────────────────────────────── */

.toasts {
  position: fixed; top: var(--s3); right: var(--s3);
  display: flex; flex-direction: column; gap: var(--s2);
  z-index: 60; max-width: min(400px, calc(100vw - var(--s6)));
}
.toast {
  background: var(--surface); border: 1px solid var(--border); border-left-width: 3px;
  border-radius: var(--r3); box-shadow: var(--shadow-lg);
  padding: var(--s3); font-size: 13px; color: var(--text-2);
  display: flex; gap: var(--s2); align-items: flex-start;
}
.toast b { color: var(--text); font-weight: 600; }
.toast.ok    { border-left-color: var(--success); }
.toast.warn  { border-left-color: var(--warning); }
.toast.error { border-left-color: var(--error); }
.toast.info  { border-left-color: var(--accent); }
.toast .x {
  background: none; border: 0; color: var(--text-muted); cursor: pointer;
  font-size: 15px; line-height: 1; padding: 0; margin-left: auto;
}
.toast .x:hover { color: var(--text); }

.notice {
  border-radius: var(--r3); padding: var(--s3) var(--s4); font-size: 13px;
  color: var(--text-2); margin-bottom: var(--s4);
  border: 1px solid var(--border); background: var(--surface-2);
}
.notice b { color: var(--text); }
.notice.warn  { border-color: color-mix(in srgb, var(--warning) 32%, transparent); background: var(--warning-bg); }
.notice.error { border-color: color-mix(in srgb, var(--error) 32%, transparent);   background: var(--error-bg); }
.notice.info  { border-color: color-mix(in srgb, var(--info) 32%, transparent);    background: var(--info-bg); }

/* ── Capas: modal y cajón ──────────────────────────────────────────────────── */

/* La capa aparecía de golpe, y aparecer de golpe se lee como un fallo del
   navegador: era literalmente la queja —«se ve que hay error»—. Con un velo que
   entra en 140 ms y un panel que sube 8 px en 180 ms, el ojo entiende que algo
   se ha abierto encima y no que la pantalla ha parpadeado.

   Los tiempos son cortos a propósito: es un formulario que un operador abre
   veinte veces al día, y por encima de 200 ms una animación deja de informar y
   empieza a estorbar. El bloque de movimiento reducido del principio de la hoja
   ya las desactiva para quien las haya pedido desactivadas. */
@keyframes scrim-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes modal-in {
  from { opacity: 0; transform: translateY(8px) scale(.985); }
  to   { opacity: 1; transform: none; }
}
@keyframes drawer-in {
  from { opacity: 0; transform: translateX(28px); }
  to   { opacity: 1; transform: none; }
}

.scrim {
  position: fixed; inset: 0; background: rgba(3,20,47,.42);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
  z-index: 40; display: grid; place-items: center; padding: var(--s5);
  animation: scrim-in .14s ease-out both;
}
.modal {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r5);
  /* Al relieve se le suma un filo de un píxel: sobre fondo oscuro el borde solo
     no separa el panel del velo, y el diálogo parecía flotar sin contorno. */
  box-shadow: var(--shadow-lg), 0 0 0 1px color-mix(in srgb, var(--border) 55%, transparent);
  width: 100%; max-width: 540px; max-height: 86vh;
  display: flex; flex-direction: column; overflow: hidden;
  animation: modal-in .18s cubic-bezier(.16, 1, .3, 1) both;
}
.modal .hd, .drawer .hd {
  padding: var(--s4) var(--s5); border-bottom: 1px solid var(--border);
  display: flex; align-items: center; gap: var(--s3);
}
.modal .hd h2, .drawer .hd h2 { font-size: 14px; }
.modal .bd {
  padding: var(--s5); overflow-y: auto; color: var(--text-2); font-size: 13px;
}
/* El pie se asienta sobre el tono secundario: separa la zona de decisión del
   contenido sin necesidad de una segunda línea divisoria. */
.modal .ft {
  padding: var(--s4) var(--s5); border-top: 1px solid var(--border);
  background: var(--surface-2);
  display: flex; gap: var(--s3); justify-content: flex-end; flex-wrap: wrap;
}

/* `right` aquí significa «pegado al borde derecho», pero el mismo nombre existe
   como utilidad de texto —`.right { text-align: right; }`— y esa declaración se
   heredaba a todo el cajón: etiquetas, ayudas y párrafos salían alineados a la
   derecha. Se devuelve la alineación al valor natural del documento; el nombre
   de la clase se deja como está para no tocar el marcado de todas las vistas. */
.scrim.right { place-items: stretch; justify-content: flex-end; padding: 0; text-align: initial; }
.drawer {
  background: var(--surface); border-left: 1px solid var(--border);
  width: min(620px, 100vw); height: 100vh; display: flex; flex-direction: column;
  box-shadow: var(--shadow-lg);
  animation: drawer-in .2s cubic-bezier(.16, 1, .3, 1) both;
}
.drawer .bd { padding: var(--s5); overflow-y: auto; flex: 1; }

/* ── Definiciones clave/valor y medidor ────────────────────────────────────── */

.kv {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--s3) var(--s5); margin: 0;
}
.kv div { min-width: 0; }
.kv dt { font-size: 12px; font-weight: 500; color: var(--text-2); }
.kv dd { margin: 2px 0 0; font-size: 13px; word-break: break-word; }
.kv dd .mono { font-family: var(--mono); font-size: 12.5px; }

.meter { height: 6px; border-radius: 3px; background: var(--surface-3); overflow: hidden; margin-top: var(--s1); }
.meter > i { display: block; height: 100%; background: var(--accent); border-radius: 3px; }
.meter.warn  > i { background: var(--warning); }
.meter.error > i { background: var(--error); }

/* ══════════════════════════════════════════════════════════════════════════════
   Conversación

   Es la pantalla donde se juega la comparación con las consolas que el usuario
   ya conoce, y por eso tiene su propio bloque: nada de lo que hay aquí se
   reutiliza en las otras cuatro secciones, y mezclarlo con el sistema general
   haría creer que sí.
   ══════════════════════════════════════════════════════════════════════════════ */

.chat {
  display: grid; grid-template-columns: 280px minmax(0, 1fr);
  height: 100%; min-height: 0;
}
@media (max-width: 900px) { .chat { grid-template-columns: 1fr; } }

/* ── Columna de conversaciones ─────────────────────────────────────────────── */

.chat-side {
  border-right: 1px solid var(--border); background: var(--surface);
  display: flex; flex-direction: column; min-height: 0;
}
@media (max-width: 900px) {
  /* En pantalla estrecha la lista se pliega a una altura fija: el hilo es lo
     que se viene a leer, y una lista de veinte conversaciones empujándolo hacia
     abajo lo convierte en la segunda cosa de la pantalla. */
  .chat-side { border-right: 0; border-bottom: 1px solid var(--border); max-height: 34vh; }
}
.chat-side .hd { padding: var(--s3); border-bottom: 1px solid var(--border); }
.chat-side .filters {
  display: flex; gap: var(--s1); padding: var(--s2) var(--s3);
  border-bottom: 1px solid var(--border); background: var(--surface-2);
}
.chat-side .filters button {
  flex: 1; padding: 3px var(--s2); border: 1px solid transparent; border-radius: var(--r1);
  background: none; color: var(--text-2); font: inherit; font-size: 12px; cursor: pointer;
  transition: background var(--mo-fast), color var(--mo-fast);
}
.chat-side .filters button:hover { background: var(--surface); color: var(--text); }
.chat-side .filters button[aria-pressed="true"] {
  background: var(--surface); border-color: var(--border); color: var(--text); font-weight: 600;
}

.conv-list { overflow-y: auto; flex: 1; min-height: 0; padding: var(--s2); }
.conv {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center;
  gap: var(--s1); width: 100%; text-align: left;
  padding: var(--s2); border: 1px solid transparent; border-radius: var(--r3);
  background: none; color: inherit; font: inherit; cursor: pointer;
  transition: background var(--mo-fast), border-color var(--mo-fast);
}
.conv:hover { background: var(--surface-2); }
.conv[aria-current="true"] {
  background: var(--surface-2); border-color: var(--border);
}
.conv .ct {
  font-size: 13px; font-weight: 550; margin: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.conv .cm {
  grid-column: 1 / -1; font-size: 11.5px; color: var(--text-muted); margin: 2px 0 0;
  display: flex; gap: var(--s2); align-items: center;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.conv .cm .dot { width: 3px; height: 3px; border-radius: 50%; background: currentColor; opacity: .6; }
/* El objetivo se distingue por color de texto y no por un icono más: en una
   lista de treinta filas, treinta iconos son ruido y una palabra es un dato. */
.conv .tgt { color: var(--accent); font-weight: 600; }
.conv .tgt.model { color: var(--text-2); }

/* ── Hilo ──────────────────────────────────────────────────────────────────── */

.chat-main { display: flex; flex-direction: column; min-height: 0; min-width: 0; }

.chat-head {
  display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
  padding: var(--s3) var(--s5); border-bottom: 1px solid var(--border);
  background: var(--surface);
}
.chat-head h2 {
  font-size: 14px; font-weight: 620; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chat-head .grow { flex: 1; min-width: 0; }

/* Franja de coste. Siempre a la vista y pegada bajo la cabecera: un usuario que
   ve lo que gasta se autorregula, y el gasto de una conversación es cuadrático
   —el historial se reenvía entero en cada vuelta—, así que esconderlo hasta la
   factura es exactamente el error que esta pantalla viene a corregir. */
.costbar {
  display: flex; align-items: center; gap: var(--s4); flex-wrap: wrap;
  padding: var(--s2) var(--s5); border-bottom: 1px solid var(--border);
  background: var(--surface-2); font-size: 12px; color: var(--text-2);
}
.costbar .m { display: inline-flex; align-items: baseline; gap: var(--s1); }
.costbar .m b {
  font-family: var(--mono); font-variant-numeric: tabular-nums;
  color: var(--text); font-weight: 600; font-size: 12.5px;
}
.costbar .src { margin-left: auto; color: var(--text-muted); font-size: 11.5px; }
.costbar .win { color: var(--text-muted); }
.costbar .win.trim { color: var(--warning); }

.thread {
  flex: 1; min-height: 0; overflow-y: auto;
  padding: var(--s5) var(--s5) var(--s6);
  scroll-behavior: smooth;
}
.thread-inner { max-width: 860px; margin: 0 auto; display: flex; flex-direction: column; gap: var(--s5); }

.msg { display: flex; flex-direction: column; gap: var(--s2); min-width: 0; }
.msg .who {
  display: flex; align-items: center; gap: var(--s2);
  font-size: 11.5px; color: var(--text-muted);
}
.msg .who b { color: var(--text-2); font-weight: 600; font-size: 12px; }

/* El turno del usuario va en una superficie propia y alineado a la derecha; el
   del asistente ocupa el ancho de lectura sin caja. Es la asimetría que hace que
   se distingan de un vistazo sin colorear medio hilo. */
.msg.user .who { justify-content: flex-end; }
.msg.user .body {
  align-self: flex-end; max-width: min(80%, 62ch);
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--r4) var(--r4) var(--r1) var(--r4);
  padding: var(--s3) var(--s4);
}
.msg.assistant .body { max-width: 78ch; }
.msg .body { font-size: 14px; line-height: 1.62; white-space: pre-wrap; word-break: break-word; }
.msg .body p { margin: 0 0 var(--s3); }
.msg .body p:last-child { margin-bottom: 0; }

/* Markdown ya interpretado. El `pre-wrap` sirve mientras el modelo escribe;
   después los saltos son elementos y mantenerlo duplicaría cada separación. */
.msg .body.md { white-space: normal; }
.msg .body .md-h {
  font-size: 14.5px; font-weight: 650; line-height: 1.35;
  margin: var(--s4) 0 var(--s2); color: var(--text);
}
.msg .body .md-h:first-child { margin-top: 0; }
.msg .body .md-list { margin: 0 0 var(--s3); padding-left: 1.35em; }
.msg .body .md-list:last-child { margin-bottom: 0; }
.msg .body .md-list li { margin: 0 0 calc(var(--s1) / 1.5); }
.msg .body .md-list li:last-child { margin-bottom: 0; }
.msg .body .md-list ul, .msg .body .md-list ol { margin-top: calc(var(--s1) / 1.5); }
.msg .body .md-inline {
  font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 0.9em; padding: 0.1em 0.34em; border-radius: var(--r1, 4px);
  background: var(--surface-2, rgba(127, 127, 127, 0.12));
}
.msg .body .md-code {
  margin: 0 0 var(--s3); padding: var(--s3); overflow-x: auto;
  border: 1px solid var(--border); border-radius: var(--r2, 6px);
  background: var(--surface-2, rgba(127, 127, 127, 0.08));
}
.msg .body .md-code:last-child { margin-bottom: 0; }
.msg .body .md-code code {
  font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 12.5px; line-height: 1.5; white-space: pre;
}
.msg .body .md-quote {
  margin: 0 0 var(--s3); padding: var(--s1) 0 var(--s1) var(--s3);
  border-left: 2px solid var(--border); color: var(--text-2);
}
.msg .body .md-hr { border: 0; border-top: 1px solid var(--border); margin: var(--s4) 0; }
.msg .body a { color: var(--accent, currentColor); text-decoration: underline; }
.msg.error .body {
  color: var(--text-2); border: 1px solid color-mix(in srgb, var(--error) 32%, transparent);
  background: var(--error-bg); border-radius: var(--r3); padding: var(--s3) var(--s4);
}

/* Cursor de generación. Es un bloque que parpadea; no hay puntos saltando ni
   ninguna otra animación que compita con el texto que está llegando. */
.caret {
  display: inline-block; width: 7px; height: 1.05em; margin-left: 2px;
  background: var(--accent); vertical-align: text-bottom;
  animation: blink 1s steps(2, start) infinite;
}
@keyframes blink { 50% { opacity: 0; } }

/* ── Razonamiento, herramientas y citas ────────────────────────────────────── */

/* Los tres se pliegan por defecto y comparten forma: son el andamiaje de la
   respuesta, no la respuesta. Quien quiera auditarla los abre. */
.fold {
  border: 1px solid var(--border); border-radius: var(--r3);
  background: var(--surface); overflow: hidden;
}
.fold + .fold { margin-top: var(--s2); }
.fold > summary {
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s2) var(--s3); cursor: pointer; list-style: none;
  font-size: 12.5px; color: var(--text-2); background: var(--surface-2);
}
.fold > summary::-webkit-details-marker { display: none; }
.fold > summary:hover { color: var(--text); }
.fold > summary svg { width: 14px; height: 14px; flex-shrink: 0; opacity: .8; }
.fold > summary b { color: var(--text); font-weight: 600; }
.fold > summary .n { margin-left: auto; color: var(--text-muted); font-size: 11.5px; }
.fold .fb { padding: var(--s3); font-size: 12.5px; color: var(--text-2); }
.fold pre {
  font-family: var(--mono); font-size: 11.5px; color: var(--text-2);
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r2);
  padding: var(--s2) var(--s3); margin: var(--s2) 0 0;
  white-space: pre-wrap; word-break: break-word; overflow-x: auto; max-height: 320px;
}

.tool { border-bottom: 1px solid var(--border); padding: var(--s3); }
.tool:last-child { border-bottom: 0; }
.tool .tn {
  display: flex; align-items: center; gap: var(--s2);
  font-size: 12.5px; font-weight: 600; color: var(--text);
}
.tool .tn .ms { margin-left: auto; font-family: var(--mono); font-size: 11px; color: var(--text-muted); font-weight: 400; }
.tool .lbl { font-size: 11px; color: var(--text-muted); margin: var(--s2) 0 0; text-transform: none; }

/* Citas. Un banco no acepta una respuesta sin saber de dónde salió, así que la
   cita no se pliega dentro del razonamiento: va debajo de la respuesta, visible,
   con documento y página. Lo que se pliega es el fragmento literal. */
.cites { display: flex; flex-direction: column; gap: var(--s2); }
.cites .ct {
  display: flex; align-items: center; gap: var(--s2);
  font-size: 11.5px; color: var(--text-muted);
}
.cite {
  border: 1px solid var(--border); border-left: 2px solid var(--accent);
  border-radius: var(--r2); background: var(--surface); padding: var(--s2) var(--s3);
}
.cite .doc { font-size: 12.5px; font-weight: 600; color: var(--text); }
.cite .doc .frag { font-weight: 400; color: var(--text-2); }
.cite .loc { font-size: 11.5px; color: var(--text-2); margin-top: 1px; }
.cite .ex {
  font-size: 12px; color: var(--text-2); margin-top: var(--s2);
  border-left: 2px solid var(--border); padding-left: var(--s3); line-height: 1.55;
}

/* ── Redacción del turno ───────────────────────────────────────────────────── */

.composer {
  border-top: 1px solid var(--border); background: var(--surface);
  padding: var(--s3) var(--s5) var(--s4);
}
.composer-inner { max-width: 860px; margin: 0 auto; }
.composer .box {
  display: flex; align-items: flex-end; gap: var(--s2);
  border: 1px solid var(--border); border-radius: var(--r4);
  background: var(--surface); padding: var(--s2);
  transition: border-color var(--mo-fast), box-shadow var(--mo-fast);
}
.composer .box:focus-within { border-color: var(--accent); box-shadow: var(--ring); }
.composer textarea {
  flex: 1; border: 0; background: none; padding: var(--s2);
  min-height: 24px; max-height: 220px; resize: none; font-size: 14px; line-height: 1.55;
}
.composer textarea:focus { outline: none; box-shadow: none; }
.composer .send {
  display: inline-grid; place-items: center; width: 34px; height: 34px; flex-shrink: 0;
  border: 0; border-radius: var(--r3); background: var(--accent); color: #fff;
  cursor: pointer; transition: background var(--mo-fast);
}
.composer .send:hover { background: var(--accent-hover); }
.composer .send:disabled { background: var(--disabled); cursor: not-allowed; }
.composer .send svg { width: 17px; height: 17px; }
.composer .send.stop { background: var(--error); }
.composer .send.stop:hover { background: color-mix(in srgb, var(--error) 88%, #000); }
.composer .foot {
  display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap;
  margin-top: var(--s2); font-size: 11.5px; color: var(--text-muted);
}
.composer .foot .opt { display: inline-flex; align-items: center; gap: var(--s1); cursor: pointer; }
.composer .foot .opt input { width: auto; margin: 0; }
.composer .foot kbd {
  font-family: var(--mono); font-size: 10.5px; border: 1px solid var(--border);
  border-bottom-width: 2px; border-radius: var(--r1); padding: 0 4px; color: var(--text-2);
}

/* ── Elección de con quién se habla ────────────────────────────────────────── */

/* Es la decisión que el usuario no conoce —un agente lleva instrucciones,
   conocimiento y herramientas; un modelo no lleva nada— y por eso ocupa la
   pantalla en lugar de esconderse en un desplegable. Se elige una vez, al abrir
   la conversación, y ya no se vuelve a preguntar. */
.pick { max-width: 720px; margin: 0 auto; padding: var(--s7) var(--s5); }
.pick > h2 { font-size: 17px; margin-bottom: var(--s1); }
.pick > .sub { margin-bottom: var(--s5); }
.pick-cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s3); }
@media (max-width: 700px) { .pick-cards { grid-template-columns: 1fr; } }

.pickcard {
  text-align: left; padding: var(--s4); border: 1px solid var(--border);
  border-radius: var(--r4); background: var(--surface); cursor: pointer;
  font: inherit; color: inherit;
  transition: border-color var(--mo-fast), background var(--mo-fast);
}
.pickcard:hover { border-color: var(--border-strong); background: var(--surface-2); }
.pickcard[aria-pressed="true"] {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
}
.pickcard .ph { display: flex; align-items: center; gap: var(--s2); }
.pickcard .ph svg { width: 18px; height: 18px; color: var(--accent); }
.pickcard .ph b { font-size: 14px; font-weight: 620; }
.pickcard p { margin: var(--s2) 0 0; font-size: 12.5px; color: var(--text-2); line-height: 1.5; }

.pick .choice { margin-top: var(--s5); }
.pick .choice .field { max-width: 420px; }
.pick .go { margin-top: var(--s4); }
