/* ═══════════════════════════════════════════════════════════════════════
   ISPulso · Sistema de diseño — fundación
   Todo el frontend importa de acá. Nada de hex sueltos en los componentes.
   Derivado del brand board (2026-07-18). Dark-first; el modo claro es secundario.
   Regla de oro: el amarillo de marca #ffd500 es SOLO para UI (acentos, la P del
   wordmark, chips). En gráficos/series sobre fondo oscuro se usa --data-gold
   (#b58900), validado por contraste y daltonismo — el #ffd500 encandila como dato.
   ═══════════════════════════════════════════════════════════════════════ */

:root {
  /* ── Superficies (profundidad de consola) ───────────────────────────── */
  --bg:          #060a0f;   /* fondo de la app */
  --surface-1:   #0f1822;   /* paneles, cards */
  --surface-2:   #131f2c;   /* card elevada, filas hover */
  --surface-3:   #1b2836;   /* inputs, controles */
  --line:        rgba(140, 160, 179, 0.14);   /* bordes/divisores hairline */
  --line-strong: rgb(var(--blue-rgb) / 0.28);    /* borde marcado (foco, activo) */

  /* ── Marca ──────────────────────────────────────────────────────────── */
  /* EL AZUL ES UNA SOLA PERILLA: cambiá --blue-rgb y se recalcula todo
     (bordes, focos, glows, atmósfera, chips). Nada de hex sueltos. */
  --blue-rgb:    61 125 255;   /* azul eléctrico (dark) — antes celeste 21 155 216 */
  --blue:        rgb(var(--blue-rgb));   /* primario: acciones, links, la "IS" del wordmark */
  --blue-soft:   rgb(var(--blue-rgb) / 0.14);
  --gold:        #ffd500;   /* acento UI: chips, highlights, la "P" del wordmark */
  --gold-soft:   rgba(255, 213, 0, 0.10);
  --data-gold:   #b58900;   /* ⚠ SOLO datos: 2ª serie de gráficos en dark */

  /* ── Estados (semánticos, nunca como acento decorativo) ─────────────── */
  --up:          #34d399;   --up-soft:   rgba(52, 211, 153, 0.12);
  --warn:        #fbbf24;   --warn-soft: rgba(251, 191, 36, 0.12);
  --warn-rgb:    251 191 36;   /* canal suelto: permite rgb(var(--warn-rgb) / α) */
  --down:        #ef4444;   --down-soft: rgba(239, 68, 68, 0.10);

  /* ── Acentos COMO TEXTO ──────────────────────────────────────────────────
     Un color sirve para pintar 40 px de botón y no sirve para pintar 11 px de
     letra: es la misma tinta, pero como texto hay muchísimo menos superficie
     para que el ojo la resuelva. Auditadas las 328 reglas que pintan texto,
     43 fallaban por esto — el azul de los enlaces daba 3,97:1 y el rojo 3,98,
     contra el 4,5 que pide AA.

     Los tokens base NO se tocan: --blue sigue siendo el azul de la marca para
     fondos, avatares y el logo. Estas variantes son sólo para `color:`.
       oscuro  blue 3,97 → 5,53   ·  down 3,98 → 5,10
       (up 7,78 y warn 8,96 ya pasaban: apuntan al base)
     Los valores están medidos contra la superficie MÁS clara del tema, que
     es el peor caso para texto claro sobre fondo oscuro. */
  --blue-txt: #6b9bff;
  --down-txt: #f56a6a;
  --up-txt:   var(--up);
  --warn-txt: var(--warn);
  --gold-txt: var(--gold);   /* sobre oscuro el oro de marca sí se lee */

  /* Texto SOBRE un fondo --blue (botones primarios, pestañas activas, badges).
     En oscuro --blue es claro (#3d7dff) y el blanco encima da 3,77:1 — falla
     AA. El propio login ya lo había resuelto usando color:var(--bg). Esto lo
     sube a token: en oscuro texto casi-negro sobre azul; en claro (--blue es
     un azul profundo) el blanco sí pasa. Una perilla, no 15 decisiones. */
  --on-blue: var(--bg);

  /* pulgar de las barras de scroll · 3,93 / 3,67 / 3,29 sobre s1 / s2 / s3 */
  --sb: #637890;

  /* ── Texto ──────────────────────────────────────────────────────────────
     En oscuro NO hay gris apagado. La jerarquía se hace con TAMAÑO, PESO y
     ESPACIO, no bajando el brillo hasta que cueste leer: sobre un fondo casi
     negro, el gris medio se apaga muchísimo más de lo que la intuición dice.

     Medido sobre las cuatro superficies oscuras (bg / s1 / s2 / s3):
       antes  ink-2 #8ca0b3 → 7,37 · 6,64 · 6,19 · 5,55
              ink-3 #536779 → 3,39 · 3,05 · 2,84 · 2,55   ← nunca llegó a AA
       ahora  ink-2 #c3d3e0 → 12,96 · 11,68 · 10,88 · 9,77
              ink-3 #a8bccd → 10,15 · 9,15 · 8,52 · 7,65

     El terciario de hoy es más claro que el SECUNDARIO de ayer. Era eso lo
     que se veía "gris": el serial de la ONU y la fecha de alta salían a
     2,84:1 y había que forzar la vista. */
  --ink:    #e7eef5;   /* principal */
  --ink-2:  #c3d3e0;   /* secundario */
  --ink-3:  #a8bccd;   /* terciario / captions */

  /* ── Tipografía ─────────────────────────────────────────────────────── */
  --font-display: 'Space Grotesk', system-ui, sans-serif;  /* títulos, wordmark */
  --font-body:    'Sora', system-ui, sans-serif;          /* cuerpo, UI */
  --font-mono:    'JetBrains Mono', ui-monospace, monospace; /* IPs, MACs, números, tablas */

  --fs-hero:  clamp(28px, 4vw, 40px);
  --fs-h1:    24px;
  --fs-h2:    19px;
  --fs-h3:    15px;
  --fs-body:  14px;
  --fs-sm:    12.5px;
  --fs-xs:    11px;   /* eyebrows, labels mono en mayúsculas */

  /* ── Radios (escala del board: 6 dominante) ─────────────────────────── */
  --r-xs:  4px;
  --r-sm:  6px;    /* botones, inputs, chips */
  --r-md:  8px;
  --r-lg:  10px;
  --r-xl:  12px;   /* cards grandes, modales */
  --r-pill: 999px;

  /* ── Espaciado (escala de 4) ────────────────────────────────────────── */
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 24px; --sp-6: 32px; --sp-7: 48px; --sp-8: 64px;

  /* ── Sombras / elevación ────────────────────────────────────────────── */
  --shadow-1: 0 2px 8px -2px rgba(0, 0, 0, 0.4);
  --shadow-2: 0 12px 32px -8px rgba(0, 0, 0, 0.6);
  --shadow-pop: 0 10px 24px -14px rgba(0, 0, 0, 0.75);      /* lift de cards */
  --shadow-launcher: 0 28px 70px -24px rgba(0, 0, 0, 0.85), /* modal del lanzador */
                     0 0 0 1px rgba(255, 255, 255, 0.025), inset 0 1px 0 var(--hi);
  /* --blue-soft es azul al 11–14%: como anillo de foco era invisible, y como
     además va con outline:none, el que navega con teclado perdía el cursor.
     Azul sólido: medido, es la única opacidad que llega a los 3:1 que pide
     WCAG 1.4.11 en LOS DOS temas (al 85% el claro se queda en 2.7:1).
     Da 3.4:1 en claro y 5.3:1 en oscuro. */
  --focus-ring: 0 0 0 3px var(--blue);

  /* ── Piezas que difieren entre temas (tokenizadas para no romper el claro) ── */
  --hi:      rgba(255, 255, 255, 0.05);   /* highlight interno superior (borde vidrio) */
  --overlay: rgba(4, 7, 10, 0.6);         /* scrim detrás del lanzador */
  --mark-bg: radial-gradient(120% 120% at 30% 20%, rgb(var(--blue-rgb) / 0.35), #0b1119 70%);

  /* ── Atmósfera (fondo de la app, no plano) ──────────────────────────── */
  --app-bg:
    radial-gradient(1100px 500px at 82% -8%, rgb(var(--blue-rgb) / 0.10), transparent 60%),
    radial-gradient(800px 500px at 0% 108%, rgba(224, 178, 60, 0.05), transparent 55%),
    var(--bg);
}

/* ═══ Diagnóstico de conexión — "el pulso del enlace" ═══════════════════
   La forma la elige el TRABAJO del dato, no el gusto:
     · señal óptica  → medida contra umbrales cualitativos  → barra bullet
                        con zonas (no un arco: el arco no muestra dónde
                        cae dentro del rango sano)
     · tráfico       → magnitud contra una capacidad         → bullet
     · señal vs tiempo → cambio                              → sparkline
     · uptime, equipos → un número                           → tile
   Ninguna es categórica, así que no hay paleta de series que validar; el
   par de datos que sí lo es (--blue + --data-gold) pasó los seis chequeos.

   ⓘ Esta advertencia decía "TEXTO EN --ink-2, NUNCA --ink-3" porque ink-3
   daba 2,84:1 sobre --surface-2. Ya no aplica: los dos tokens se subieron
   (ink-2 → 10,88 · ink-3 → 8,52 sobre esa misma superficie) justamente
   porque la regla se incumplía en toda la app y era más sano arreglar el
   token que perseguir cada regla. Los dos sirven para texto.

   ⚠ Lo que SÍ sigue vigente: un acento (--blue, --down, --up, --warn) NO se
   usa como color de texto. Para eso están --blue-txt y compañía.

   ⚠ Las zonas de la barra de señal van con ETIQUETA, no sólo color: en
   modo claro rojo y ámbar quedan a ΔE 4,4 bajo deuteranopía (medido) —
   indistinguibles. El color acompaña, el texto informa. */
.dx           { display:grid; gap:var(--sp-3); margin-top:var(--sp-3); }
/*  `max(220px, 45%)` y no un mínimo fijo: con `minmax(190px,1fr)` a 900 px de
    ancho `auto-fit` armaba CUATRO columnas, y como el panel de señal ocupa la
    fila entera, los otros dos caían en 208 px con dos columnas vacías al lado.
    El 45 % fuerza dos columnas como máximo —así los dos paneles llenan el
    ancho— y el piso de 220 px las colapsa a una sola cuando ya no entran. */
.dx__fila     { display:grid; grid-template-columns:repeat(auto-fit,minmax(max(220px,45%),1fr));
                gap:var(--sp-2); }

/* tile: un número que se lee de lejos + su etiqueta */
.dx-tile      { background:var(--surface-2); border:1px solid var(--line);
                border-radius:var(--r-md); padding:10px 12px; min-width:0; }
.dx-tile__k   { font-family:var(--font-mono); font-size:var(--fs-xs); letter-spacing:.04em;
                text-transform:uppercase; color:var(--ink-2); display:block; margin-bottom:5px; }
.dx-tile__v   { font-family:var(--font-display); font-weight:600; font-size:17px;
                color:var(--ink); line-height:1.15; display:flex; align-items:baseline; gap:5px; }
.dx-tile__v small { font-family:var(--font-body); font-size:11.5px; font-weight:400; color:var(--ink-2); }
.dx-tile__x   { font-size:11px; color:var(--ink-2); margin-top:3px; }

/* medidor: barra con zonas + aguja. La aguja es la marca; las zonas, contexto */
.dx-med       { background:var(--surface-2); border:1px solid var(--line);
                border-radius:var(--r-md); padding:11px 13px 13px; }
.dx-med__cab  { display:flex; align-items:baseline; justify-content:space-between; gap:10px; margin-bottom:9px; }
.dx-med__val  { font-family:var(--font-display); font-weight:600; font-size:19px; color:var(--ink); }
.dx-med__val small { font-size:11.5px; font-weight:400; color:var(--ink-2); margin-left:3px; }
.dx-med__et   { font-family:var(--font-mono); font-size:var(--fs-xs); letter-spacing:.04em;
                text-transform:uppercase; color:var(--ink-2); }
.dx-med__pista{ position:relative; height:8px; border-radius:var(--r-xs); overflow:hidden;
                display:flex; gap:2px; }   /* 2px de superficie entre zonas */
.dx-med__z    { height:100%; }
.dx-med__ag   { position:absolute; top:-3px; width:3px; height:14px; border-radius:2px;
                background:var(--ink); box-shadow:0 0 0 2px var(--surface-2); }  /* anillo de 2px */
.dx-med__esc  { display:flex; justify-content:space-between; margin-top:6px;
                font-family:var(--font-mono); font-size:10px; color:var(--ink-2); }
/* hero: el medidor de señal es AHORA la única lectura óptica (antes convivía
   con un arco que mostraba el mismo dato). Se le da el peso que merece. */
/*  Ocupa la fila entera. Compartiendo grilla con los otros dos paneles caía
    en 280 px, y ahí los 44 segmentos de la escalera quedan en menos de 4 px
    cada uno: se convierte en una mancha y deja de mostrar DÓNDE cae la señal,
    que es su único trabajo. Los otros dos paneles sí se acomodan al lado uno
    del otro debajo. */
.dx-med--hero { grid-column:1 / -1;
                background:linear-gradient(180deg, color-mix(in srgb, var(--blue) 5%, var(--surface-2)),
                                                   var(--surface-2) 60%); }
.dx-med__hero { display:flex; align-items:baseline; gap:7px; margin:2px 0 11px; }
.dx-med__num  { font-family:var(--font-display); font-weight:700; font-size:34px; line-height:1;
                color:var(--ink); letter-spacing:-.02em; font-variant-numeric:tabular-nums; }
.dx-med__u    { font-family:var(--font-mono); font-size:12px; color:var(--ink-2); }
/* El nombre de la zona en tinta de texto + un punto de color al lado: el
   rojo como TEXTO mide 4,43:1 sobre superficie elevada y AA pide 4,5. */
.dx-med__zona { margin-left:auto; font-family:var(--font-mono); font-size:11px; font-weight:500;
                letter-spacing:.04em; text-transform:uppercase; color:var(--ink);
                display:inline-flex; align-items:center; gap:6px; }
.dx-med__zona i { width:7px; height:7px; border-radius:50%; flex-shrink:0; }

/* bullet: consumo real contra la capacidad del plan */
.dx-bul       { display:grid; gap:6px; }
.dx-bul__cab  { display:flex; align-items:baseline; justify-content:space-between; gap:8px;
                font-size:12px; color:var(--ink-2); }
.dx-bul__cab b{ font-family:var(--font-mono); color:var(--ink); font-weight:500; }
.dx-bul__alerta { color:var(--ink); font-weight:600; display:inline-flex; align-items:center; gap:4px; }
.dx-bul__alerta i { width:6px; height:6px; border-radius:50%; background:var(--warn); flex-shrink:0; }
.dx-bul__pista{ height:7px; border-radius:var(--r-xs); background:var(--surface-3); overflow:hidden; }
.dx-bul__fill { height:100%; border-radius:var(--r-xs); }

/* sparkline: la forma de la señal en el tiempo */
.dx-spark     { display:block; width:100%; height:38px; overflow:visible; }
.dx-spark path{ fill:none; stroke-width:2; stroke-linecap:round; stroke-linejoin:round; }
.dx-spark circle { r:3.5; }

/* avisos: icono + etiqueta + texto. Nunca color solo. */
.dx-av        { display:flex; flex-direction:column; gap:7px; }
.dx-av__i     { display:flex; gap:10px; align-items:flex-start; padding:9px 12px;
                border-radius:var(--r-md); border:1px solid var(--line);
                background:var(--surface-2); font-size:12.5px; line-height:1.55; color:var(--ink-2); }
.dx-av__i b   { color:var(--ink); font-weight:600; }
.dx-av__ic    { flex-shrink:0; width:18px; height:18px; margin-top:1px; }
.dx-av--critico { border-color:rgba(239,68,68,.34); background:var(--down-soft); }
.dx-av--aviso   { border-color:rgb(var(--warn-rgb)/.34); background:var(--warn-soft); }

/* ── Veredicto · la conclusión antes que los números ──────────────────
   Del panel de Claude Design. Es su mejor idea: el operador resuelve la
   pregunta sin leer un dBm, y los números quedan para entender por qué.
   Los hex del diseño se mapean a tokens — el sistema prohíbe hex sueltos
   en componentes, y además así el modo claro funciona sin reescribir. */
.dx-ver       { display:flex; align-items:flex-start; justify-content:space-between;
                gap:18px; flex-wrap:wrap; }
.dx-ver__t    { display:flex; align-items:center; gap:11px; }
.dx-ver__pt   { position:relative; width:11px; height:11px; border-radius:50%; flex-shrink:0; }
.dx-ver__h    { font-family:var(--font-display); font-weight:700; font-size:23px;
                letter-spacing:-.01em; color:var(--ink); line-height:1.2; }
.dx-ver__s    { margin-top:6px; font-size:13px; line-height:1.6; color:var(--ink-2); max-width:64ch; }
.dx-ver__chip { display:inline-flex; align-items:center; gap:7px; border-radius:var(--r-pill);
                padding:6px 14px; white-space:nowrap; font-family:var(--font-mono);
                font-size:11px; letter-spacing:.16em; color:var(--ink); font-weight:500; }
.dx-ver__chip i { width:7px; height:7px; border-radius:50%; flex-shrink:0; }
/* el punto late sólo si hay algo que mirar: una conexión sana no parpadea */
.dx-ver--alerta .dx-ver__pt { animation:dxlatido 2s ease-out infinite; }

/* ── Escalera de señal · 44 segmentos ─────────────────────────────────
   Reemplaza la barra plana: la posición se lee de un vistazo y el relieve
   (el segmento activo más alto y claro) marca dónde está sin depender del
   color, que es lo que pide el caso daltónico. */
.dx-esc       { display:flex; align-items:flex-end; gap:2px; height:26px; margin-top:14px; }
.dx-esc span  { flex:1; border-radius:2px; transition:height .35s ease, background .35s ease; }
.dx-esc__ejes { position:relative; height:14px; margin-top:7px;
                font-family:var(--font-mono); font-size:9.5px; color:var(--ink-2); }
.dx-esc__ejes span { position:absolute; white-space:nowrap; }

/* ── Chips de dispositivo ─────────────────────────────────────────── */
.dx-chips     { display:flex; flex-wrap:wrap; gap:6px; margin-top:7px; }
.dx-chip      { white-space:nowrap; font-size:11.5px; color:var(--ink-2);
                background:var(--blue-soft); border:1px solid rgb(var(--blue-rgb)/.16);
                border-radius:var(--r-pill); padding:3px 10px; }

/* ── Números lado a lado ──────────────────────────────────────────────
   `auto-fit` y no `1fr 1fr`: eran dos columnas fijas repartiéndose todo el
   ancho del modal, así que "Encendido" y "Enlace estable" quedaban separados
   por medio panel vacío. Ahora entran los que haya —dos o tres— sin estirarse
   más de lo que el número necesita. */
.dx-par       { display:grid; grid-template-columns:repeat(auto-fit,minmax(112px,1fr));
                gap:12px 18px; margin-top:13px; }
.dx-par__k    { font-size:11px; color:var(--ink-2); }
.dx-par__v    { margin-top:3px; font-family:var(--font-display); font-weight:700;
                font-size:19px; color:var(--ink); }

/* ── El hilo · divisor con la traza del pulso ──────────────────────────
   Un divisor no tiene por qué ser un hairline gris. Éste se enciende en el
   centro y se apaga en las puntas: una línea con señal encima. Es el ECG de
   la marca reducido a 1 px.

   Con un <i> adentro le corre el cometa de la casa (3,4 s, la misma cadencia
   que el login y el hilo de progreso de la píldora); sin <i> queda estático.
   La animación se reserva para superficies VIVAS —la tira de la conexión—:
   tres líneas corriendo dentro de un modal es ruido, no identidad. */
.hilo         { position:relative; height:1px; overflow:hidden; flex-shrink:0;
                background:linear-gradient(90deg, transparent,
                  rgb(var(--blue-rgb)/.42) 22%, rgb(var(--blue-rgb)/.65) 50%,
                  rgb(var(--blue-rgb)/.42) 78%, transparent); }
.hilo i       { position:absolute; inset:0 auto 0 0; display:block; width:26%;
                background:linear-gradient(90deg, transparent, var(--blue), transparent);
                animation:hilopulso 3.4s linear infinite; }
@keyframes hilopulso { from { transform:translateX(-110%); } to { transform:translateX(490%); } }
@media (prefers-reduced-motion: reduce){ .hilo i { animation:none; opacity:0; } }

/* ── Pie: tendencia + acciones ────────────────────────────────────── */
.dx-pie       { display:flex; justify-content:space-between; align-items:center; gap:20px;
                flex-wrap:wrap; padding-top:14px; border-top:1px solid var(--line); }
.dx-pie__sp   { display:flex; align-items:center; gap:13px; }
.dx-mini      { display:flex; align-items:flex-end; gap:4px; height:24px; }
.dx-mini span { width:4px; border-radius:2px; background:rgb(var(--blue-rgb)/.5); }
.dx-mini span:last-child { background:var(--blue); }

/* ── Consumo · totales + las últimas 24 h ─────────────────────────────
   Va a lo ancho, debajo de los medidores: es una serie, no un número
   suelto, y compitiendo por una columna las barras quedan ilegibles.     */
.dx-cons      { padding-top:14px; margin-top:14px; border-top:1px solid var(--line); }
.dx-cons__t   { display:flex; align-items:baseline; gap:18px; flex-wrap:wrap; margin-bottom:12px; }
.dx-cons__n   { display:flex; align-items:baseline; gap:7px; }
.dx-cons__n b { font-size:19px; font-weight:700; letter-spacing:-.01em; }
.dx-cons__n i { font-style:normal; font-size:11px; color:var(--ink-2);
                text-transform:uppercase; letter-spacing:.06em; }
/*  La bajada manda: es el número que el cliente pregunta y el que se compara
    contra el plan. La subida acompaña, más chica y en otro tono.          */
.dx-cons__up  { font-family:var(--font-mono); font-size:11.5px; color:var(--ink-2); }

/*  Las barras: una por hora, 24 en total. `align-items:flex-end` para que
    crezcan desde abajo — una hora sin tráfico tiene que verse como un hueco
    al ras, no como una barra chiquita flotando.                            */
.dx-cons__g   { display:flex; align-items:flex-end; gap:3px; height:54px; }
.dx-cons__b   { flex:1; min-width:0; border-radius:3px 3px 0 0;
                background:rgb(var(--blue-rgb)/.55); position:relative;
                transition:background .12s ease; }
.dx-cons__b:hover { background:var(--blue); }
/*  El piso de una hora CON tráfico lo pone el JS (5 px), no esta regla: al
    lado de un pico de 4 GB, 1,5 MB redondea a cero y quedaba igual de alta
    que una hora vacía. Acá sólo se garantiza que nunca desaparezca.        */
.dx-cons__b--hay { min-height:3px; }
/*  ⚠ NO va con transparencia baja. Estaba en rgb(--ink-rgb/.16) y medido da
    2,22:1 contra el fondo en modo claro — por debajo del 3:1 que pide un
    elemento de interfaz, y sobre 2 px de alto queda directamente invisible:
    los huecos se leían como un error de dibujo, no como "esta hora no usó".
    `--sb` es el tono apagado-pero-visible que ya existe para el scrollbar,
    definido en los dos temas.                                              */
.dx-cons__b--nada { background:var(--sb); min-height:2px; }
.dx-cons__x   { display:flex; justify-content:space-between; margin-top:6px;
                font-family:var(--font-mono); font-size:10.5px; color:var(--ink-3); }

/* el latido: sólo cuando el dato es de AHORA. Es la firma del sistema
   (radio, no médica) reducida a su mínima expresión. */
.dx-vivo      { display:inline-flex; align-items:center; gap:5px; font-family:var(--font-mono);
                font-size:10px; letter-spacing:.05em; text-transform:uppercase; color:var(--ink-2); }
.dx-vivo i    { width:6px; height:6px; border-radius:50%; background:var(--up);
                box-shadow:0 0 0 0 rgb(52 211 153/.55); animation:dxlatido 2.6s ease-out infinite; }
@keyframes dxlatido {
  0%   { box-shadow:0 0 0 0   rgb(52 211 153/.5); }
  70%  { box-shadow:0 0 0 7px rgb(52 211 153/0); }
  100% { box-shadow:0 0 0 0   rgb(52 211 153/0); }
}
@media (prefers-reduced-motion:reduce){ .dx-vivo i { animation:none; } }

/* ═══ Modo claro — DISEÑADO, no invertido ═══════════════════════════════
   Blueprint claro y técnico (neutro con sesgo azul, no crema editorial).
   Recalibrado: el oro brillante encandila sobre claro → ámbar profundo;
   las severidades se oscurecen para pasar contraste; sombras suaves azuladas,
   no negro pesado. El objetivo es que se sienta tan cuidado como el oscuro. */
:root[data-theme="light"] {
  /* superficies — neutro frío-limpio con leve sesgo azul */
  /* superficies con separación real: los campos NO pueden ser casi-blancos
     sobre tarjetas blancas. surface-2 baja a un celeste suave y las líneas suben. */
  --bg: #e3eaf3; --surface-1: #ffffff; --surface-2: #e8f0f8; --surface-3: #d9e5f0;
  --line: rgba(23, 45, 66, 0.18); --line-strong: rgb(var(--blue-rgb) / 0.55);

  /* marca — azul un toque más profundo (contraste sobre blanco); oro → ámbar que SÍ se lee */
  /* eléctrico un punto más profundo: sobre blanco necesita más peso para leerse */
  --blue-rgb: 10 88 235;
  --blue: rgb(var(--blue-rgb)); --blue-soft: rgb(var(--blue-rgb) / 0.11);
  --gold: #a97b00; --gold-soft: rgba(169, 123, 0, 0.12);
  --data-gold: #9a7400;

  /* severidades — más ricas para contrastar sobre claro */
  --up:   #0f9d63; --up-soft:   rgba(15, 157, 99, 0.12);
  --warn: #b7791f; --warn-soft: rgba(183, 121, 31, 0.12); --warn-rgb: 183 121 31;
  --down: #d92d2d; --down-soft: rgba(217, 45, 45, 0.10);

  /*  Acentos como TEXTO. En claro el problema era peor que en oscuro y nadie
      lo había medido: el verde daba 2,73:1 y el ámbar 2,85 sobre la
      superficie más oscura del tema —la mitad de lo que pide AA—. El fondo
      claro engaña: un color saturado "se ve" pero no contrasta.
        blue 4,55 (alcanza, queda el base) · down 3,76 → 5,17
        up   2,73 → 5,50                   · warn 2,85 → 5,32  */
  --blue-txt: var(--blue);
  --down-txt: #b3231f;
  --up-txt:   #076640;
  --warn-txt: #7d5210;
  /* en claro el azul es profundo (rgb 10 88 235): el blanco encima da 5,82:1 */
  --on-blue: #fff;

  /* pulgar de las barras de scroll · 4,08 / 3,55 / 3,19 sobre s1 / s2 / s3 */
  --sb: #6b8095;

  /*  texto — azul-tinta profundo.
      ink-3 se oscurece OTRA VEZ: #66798e seguía dando 3,50:1 sobre la
      superficie más oscura del tema (#d9e5f0) y aparecía en ~70 reglas —
      th de tablas, códigos de cliente, metadatos de servicio, placeholders.
      #4d6176 lo lleva a 4,99. El comentario viejo decía "se oscurece: era
      ilegible sobre claro"; se había oscurecido, pero no lo suficiente. */
  --ink: #101c28; --ink-2: #3d5468; --ink-3: #4d6176;
  --gold-txt: #7a5900;   /* el oro de marca da 2,97:1 como texto: no se lee */

  /* elevación / atmósfera propias de claro */
  --hi:      rgba(255, 255, 255, 0.7);
  --overlay: rgba(23, 45, 66, 0.28);
  --mark-bg: radial-gradient(120% 120% at 30% 20%, rgb(var(--blue-rgb) / 0.16), #dde7ef 72%);
  --shadow-1: 0 2px 8px -2px rgba(23, 45, 66, 0.10);
  --shadow-2: 0 14px 34px -10px rgba(23, 45, 66, 0.18);
  --shadow-pop: 0 10px 24px -14px rgba(23, 45, 66, 0.22);
  --shadow-launcher: 0 30px 70px -22px rgba(23, 45, 66, 0.28),
                     0 0 0 1px rgba(23, 45, 66, 0.05), inset 0 1px 0 var(--hi);
  --app-bg:
    radial-gradient(1100px 520px at 84% -10%, rgb(var(--blue-rgb) / 0.14), transparent 60%),
    radial-gradient(960px 560px at -6% 112%, rgba(20, 160, 150, 0.10), transparent 55%),
    var(--bg);
}

/* transición suave SOLO durante el cambio de tema (no en hovers ni animaciones) */
html.theming, html.theming * {
  transition: background-color .28s ease, border-color .28s ease, color .22s ease !important;
}

/* ── Barras de scroll ───────────────────────────────────────────────────
   Van en la fundación porque una barra de scroll es cromo del SISTEMA. Si se
   estiliza suelta en cada componente terminan siendo varias barras distintas
   —que es lo que había: `.lch-body`, `.tbl-scroll` y `.seg`, cada una por su
   cuenta y con valores propios.

   ⚠ Las dos APIs NO conviven, y esto no es folclore: medido en este Chrome,
   con `scrollbar-width` declarado el navegador IGNORA `::-webkit-scrollbar`
   por completo (pedí 18 px por webkit y salieron los 10 del estándar). Las
   reglas viejas declaraban LAS DOS, así que su parte cuidada —el pulgar
   redondeado y embutido— no se dibujaba nunca. Acá se elige una por motor:
   los pseudo-elementos donde existen, el estándar sólo donde no.

   El pulgar en reposo pasa 3:1 sobre las tres superficies del tema, que es
   lo que WCAG 1.4.11 pide para un control: una barra que no se ve no se
   puede agarrar. Al pasar por encima se va al azul, como el resto de los
   controles. El borde transparente + `background-clip` es lo que deja el
   pulgar flotando con aire en vez de pegado a los bordes del canal. */
::-webkit-scrollbar        { width: 12px; height: 12px; }
::-webkit-scrollbar-track  { background: transparent; }
::-webkit-scrollbar-thumb  { background: var(--sb); border-radius: var(--r-pill);
                             border: 3px solid transparent; background-clip: padding-box; }
::-webkit-scrollbar-thumb:hover,
::-webkit-scrollbar-thumb:active { background: var(--blue); background-clip: padding-box; }
::-webkit-scrollbar-corner { background: transparent; }

@supports not selector(::-webkit-scrollbar) {
  * { scrollbar-width: thin; scrollbar-color: var(--sb) transparent; }
}

/* ── Reset mínimo ───────────────────────────────────────────────────── */
* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; }
body {
  background: var(--app-bg); background-attachment: fixed;
  color: var(--ink); font-family: var(--font-body); font-size: var(--fs-body);
  line-height: 1.55; -webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4 { font-family: var(--font-display); font-weight: 700; margin: 0; letter-spacing: -0.01em; }
a { color: var(--blue-txt); text-decoration: none; }
:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: var(--r-sm); }

/* ═══ Primitivas de componentes ═══════════════════════════════════════ */

/* eyebrow / label mono */
.eyebrow { font-family: var(--font-mono); font-size: var(--fs-xs); letter-spacing: 0.22em;
  text-transform: uppercase; color: var(--ink-3); }

/* botones */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  font-family: var(--font-display); font-weight: 600; font-size: var(--fs-sm); cursor: pointer;
  padding: 9px 15px; border-radius: var(--r-sm); border: 1px solid var(--line);
  background: var(--surface-2); color: var(--ink-2); transition: 0.14s; }
.btn:hover:not([disabled]) { color: var(--ink); border-color: var(--line-strong); }
.btn--primary { background: var(--blue); border-color: var(--blue); color: var(--on-blue); }
.btn--primary:hover:not([disabled]) { background: var(--blue); border-color: var(--blue); color: var(--on-blue); filter: brightness(1.12); }
.btn--danger { background: var(--down-soft); border-color: rgba(239,68,68,0.4); color: var(--down-txt); }
.btn--danger:hover:not([disabled]) { background: rgba(239,68,68,0.2); color: #fff; }
/*  ⚠ El deshabilitado tiene que VERSE. Hay ~19 lugares que hacen
    `disabled = true` sobre estos botones mientras guardan, y como `.btn` fija
    su propio background/color/cursor, el gris del navegador no aparece: el
    botón muerto se veía idéntico al vivo — y encima SE ILUMINABA al pasarle
    el mouse, porque los hover de arriba no filtraban. El `disabled` real
    funcionaba (no había doble envío), pero el operador no tenía forma de
    saberlo. El login ya lo resolvía por su cuenta (`.auth-btn[disabled]`);
    esto lo trae a la primitiva para que TODAS las pantallas lo hereden.
    `pointer-events` no va: mataría también el title/tooltip, y el cursor
    not-allowed ya comunica lo mismo sin esconder nada. */
.btn[disabled] { opacity: 0.55; cursor: not-allowed; }
.btn--ghost { background: none; }
.btn .ico { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round; }

/* inputs */
.field { display: block; margin-bottom: var(--sp-3); }
.field > label { display: block; font-size: var(--fs-sm); color: var(--ink-2); margin-bottom: var(--sp-1); }
.input { width: 100%; padding: 9px 11px; border-radius: var(--r-sm); background: var(--surface-3);
  border: 1px solid var(--line); color: var(--ink); font-family: var(--font-mono); font-size: var(--fs-sm);
  transition: 0.13s; }
.input:focus { outline: none; border-color: var(--blue); box-shadow: var(--focus-ring); }

/* cards */
.card { background: var(--surface-1); border: 1px solid var(--line); border-radius: var(--r-xl);
  padding: var(--sp-5); }
.card__title { font-size: var(--fs-h3); color: var(--ink); margin-bottom: var(--sp-3); }

/* Panel: contenedor con encabezado separado por hairline. Da la MISMA
   estructura a cualquier contenido → es lo que ordena visualmente. */
.panel { background: var(--surface-1); border: 1px solid var(--line);
  border-radius: var(--r-xl); overflow: hidden; margin-bottom: var(--sp-4); }
/* .panel--libre: sin recorte, para paneles que contienen menús flotantes
   (si no, el dropdown de acciones se corta contra el borde del panel). */
.panel--libre { overflow: visible; }
/* wrap obligatorio: el .panel recorta (overflow:hidden), así que lo que no
   entra en la cabecera no se puede scrollear — desaparece. En Facturación
   dejaba afuera "Emitir cuotas del mes" y "Exportar base SIRO" en el celular. */
.panel__head { position: relative; display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: var(--sp-4);
  padding: 15px 20px; background: var(--surface-2);
  border-radius: 11px 11px 0 0; }   /* redondeo propio: no depende del overflow del panel */
/*  El hairline que separaba la cabecera del cuerpo pasa a ser la traza del
    pulso. Va acá, en la primitiva, y no vista por vista: `panelHTML` es la
    MISMA estructura en todo el sistema, así que un solo cambio la lleva a
    Clientes, Servicios, Facturación, Tickets, NOC y Config a la vez.
    Como pseudo-elemento para no tocar el markup de ningún panel, y sin
    cometa: es cromo estructural, no una superficie viva. */
.panel__head::after { content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: linear-gradient(90deg, transparent, rgb(var(--blue-rgb)/.42) 22%,
    rgb(var(--blue-rgb)/.65) 50%, rgb(var(--blue-rgb)/.42) 78%, transparent); }
.panel__head h3 { font-size: var(--fs-h3); }
.panel__head .meta { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--ink-3); white-space: nowrap; }
.panel__head .acciones { display: flex; flex-wrap: wrap; align-items: center; gap: 13px; }
.panel__body { padding: 18px 20px; }
.panel__body--flush { padding: 0; }          /* tablas y listas a borde completo */

/* lista de definiciones: etiqueta ······ valor, con hairlines */
/*  `auto-fit` sobre el ancho PROPIO, no una media query del viewport.
    Con `1fr 1fr` + `@media (max-width:700px)` la lista colapsaba a una
    columna según el tamaño de la VENTANA, aunque el contenedor tuviera
    ancho de sobra. Medido en el modal de conexión: 6 filas ocupaban 287 px
    en una columna cuando en dos entran en 145. El mismo error que ya había
    cometido con el divisor de acciones — la media query no sabe nada del
    contenedor.
    `min(100%, 260px)` es el piso: evita que el mínimo desborde en cajas
    más angostas que 260 px. */
.dl { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
  gap: 0 34px; }
.dl__i { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-4);
  padding: 12px 0; border-bottom: 1px solid var(--line); }
/* --ink-2: la etiqueta en --ink-3 medía 3,05:1 en oscuro y 4,48:1 en claro
   —AA pide 4,5— así que la mitad izquierda de toda lista de datos del
   sistema estaba por debajo del mínimo. Es la regla de arriba, incumplida
   en la primitiva más usada. */
.dl__i > span { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--ink-2); white-space: nowrap; }
.dl__i > b { font-weight: 500; font-size: 14px; color: var(--ink); text-align: right; word-break: break-word; }

/* chips / pills */
.chip { display: inline-flex; align-items: center; gap: var(--sp-1); font-family: var(--font-mono);
  font-size: var(--fs-xs); color: var(--blue-txt); background: var(--blue-soft);
  border: 1px solid var(--line-strong); border-radius: var(--r-pill); padding: 2px 9px; }
.chip--gold { color: var(--gold-txt); background: var(--gold-soft); border-color: rgba(255,213,0,0.3); }

/* estado: dot + label (forma + color, nunca color solo) */
.status { display: inline-flex; align-items: center; gap: 7px; font-family: var(--font-display);
  font-size: var(--fs-sm); font-weight: 600; }
.status .dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.status--up { color: var(--up-txt); }   .status--up .dot { animation: pulse-up 2.6s ease-in-out infinite; }
.status--warn { color: var(--warn-txt); }
.status--down { color: var(--down-txt); } .status--down .dot { animation: pulse-down 1.1s ease-in-out infinite; }
@keyframes pulse-up { 0%,100%{ box-shadow: 0 0 0 0 var(--up-soft);} 50%{ box-shadow: 0 0 0 5px transparent;} }
@keyframes pulse-down { 0%,100%{ box-shadow: 0 0 0 0 rgba(239,68,68,0.5);} 50%{ box-shadow: 0 0 0 6px transparent;} }
@media (prefers-reduced-motion: reduce) { .status .dot { animation: none !important; } }

/* KPI tile — cifra proporcional (nunca tabular en números grandes) */
.kpi { background: var(--surface-1); border: 1px solid var(--line); border-radius: var(--r-lg);
  padding: var(--sp-4); }
.kpi__label { font-size: var(--fs-xs); color: var(--ink-2); text-transform: uppercase; letter-spacing: 0.06em; }
.kpi__value { display: block; font-family: var(--font-display); font-weight: 700; font-size: 26px;
  color: var(--ink); line-height: 1.1; margin-top: 6px; }
.kpi__sub { display: block; font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--ink-3); margin-top: 4px; }

/* números en columnas → tabular */
.num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
