/* ==========================================================================
   Pretor Vision — Panel de configuración (v0.5.0)
   Material Design 3, escrito a mano. Sin CDN, sin build, sin fuentes
   externas: el servidor corre en una LAN que puede no tener internet, así que
   todo lo que el navegador necesita se sirve desde acá.
   Tipografía: pila del sistema. Material usa Roboto, pero descargarla de
   Google Fonts rompería el panel sin internet y mandaría datos afuera.
   ========================================================================== */

:root {
  /* --- Paleta tonal M3. Azul institucional: es un sistema de seguridad
         edilicia, no una app de consumo. El ámbar queda reservado para lo
         que hace ruido de verdad (pánico). --- */
  --primary: #0b5fa5;
  --on-primary: #ffffff;
  --primary-container: #d3e4f7;
  --on-primary-container: #001d33;

  --secondary: #526070;
  --secondary-container: #d6e4f7;
  --on-secondary-container: #0f1d2a;

  --tertiary: #8b5000;          /* pánico / voceo */
  --tertiary-container: #ffdcbe;
  --on-tertiary-container: #2c1600;

  --error: #ba1a1a;
  --error-container: #ffdad6;
  --on-error-container: #410002;

  --ok: #2e6b3e;
  --ok-container: #b8f2c4;

  --surface: #fbfcfe;
  --surface-1: #f2f5f9;
  --surface-2: #eceff4;
  --surface-3: #e5e9ef;
  --on-surface: #1a1c1e;
  --on-surface-variant: #43474e;
  --outline: #73777f;
  --outline-variant: #c3c7cf;

  --shadow-1: 0 1px 2px rgba(0,0,0,.30), 0 1px 3px 1px rgba(0,0,0,.15);
  --shadow-2: 0 1px 2px rgba(0,0,0,.30), 0 2px 6px 2px rgba(0,0,0,.15);

  --r-s: 8px;
  --r-m: 12px;
  --r-l: 16px;
  --r-xl: 28px;

  /* v0.5.4-f — LA ESCALA TIPOGRAFICA (Material Design 3).
   *
   * Habia 30 tamaños literales repartidos por el archivo, incluidos `12.5px`
   * y `10.5px`. No es solo prolijidad: sin una escala, cada texto nuevo se
   * elige mirando el de al lado, y al cabo de unas versiones hay cuatro
   * tamaños distintos haciendo el mismo papel — que es lo que pasó acá.
   *
   * Los nombres son los de M3 (display / headline / title / body / label) y
   * los valores son los que YA estaba usando el panel, redondeados: el
   * objetivo es poder nombrarlos, no cambiar como se ve. Los `12.5` pasan a
   * `13` y los `10.5` a `11`, que es la unica diferencia visible.
   */
  --t-display:  30px;    /* el numero grande de una metrica */
  --t-display-s: 26px;   /* idem, en un mosaico chico */
  --t-headline: 27px;    /* titulo de pantalla */
  --t-title:    17px;    /* titulo de tarjeta */
  --t-title-s:  16px;    /* titulo de tarjeta */
  --t-body:     15px;    /* texto corriente */
  --t-body-s:   13.5px;  /* texto secundario, filas de tabla */
  --t-label:    13px;    /* ayuda, notas al pie */
  --t-label-s:  11px;    /* insignias, mayusculas chicas */

  /* Area tactil minima de M3. El portero va a usar esto en un telefono, asi
   * que es lo que mas se nota de los cuatro puntos. */
  --tactil: 48px;

  --nav-w: 264px;
  --font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
          Arial, sans-serif;
  --mono: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, monospace;
}

/* v0.5.4-d — TRES ESTADOS DE TEMA, no dos.
 *
 * El panel ya tenia los dos juegos de colores, pero atados solamente a
 * `prefers-color-scheme`: seguia al sistema operativo y no habia forma de
 * elegir. Buen default, mal unico camino — quien usa Windows en claro y
 * quiere el panel en oscuro no tenia opcion (pedido de Diego).
 *
 * Ahora:
 *   sin `data-tema`        -> lo que diga el sistema (el default)
 *   data-tema="claro"      -> claro, aunque el sistema este en oscuro
 *   data-tema="oscuro"     -> oscuro, aunque el sistema este en claro
 *
 * ⚠️ EL ORDEN Y EL `:not()` IMPORTAN. El media query lleva
 * `:not([data-tema="claro"])` para que forzar claro le gane al sistema en
 * oscuro; y el bloque de `data-tema="oscuro"` va DESPUES para ganarle al
 * sistema en claro. Sin el `:not`, «forzar claro» no haria nada en una
 * maquina configurada en oscuro — y es justo el caso que lo motivo.
 *
 * Los tokens estan escritos dos veces, y es a proposito: la alternativa es un
 * `--tokens-oscuros` compartido, que en CSS plano no existe sin repetir igual.
 * Hay una prueba que verifica que los dos bloques definan lo mismo, asi que
 * agregar un color en uno y olvidarlo en el otro se ve.
 */
@media (prefers-color-scheme: dark) {
  :root:not([data-tema="claro"]) {
    --primary: #9ecaff;
    --on-primary: #003257;
    --primary-container: #00497c;
    --on-primary-container: #d3e4f7;
    /* ⚠️ `--secondary` faltaba en los dos bloques oscuros: estaba definida
       solo en claro, asi que cualquier cosa que la usara quedaba gris oscuro
       sobre fondo oscuro. Lo encontre al darle color a la serie del disco. Es
       el mismo error que `--surface-variant` en -h, que no existia y hacia
       caer siempre al literal gris claro. */
    --secondary: #b9c8da;
    --secondary-container: #3a4857;
    --on-secondary-container: #d6e4f7;
    --tertiary: #ffb870;
    --tertiary-container: #6a3c00;
    --on-tertiary-container: #ffdcbe;
    --error: #ffb4ab;
    --error-container: #93000a;
    --on-error-container: #ffdad6;
    --ok: #9ed7ab;
    --ok-container: #1f4d2c;
    --surface: #111417;
    --surface-1: #191c20;
    --surface-2: #1d2024;
    --surface-3: #22262a;
    --on-surface: #e2e2e6;
    --on-surface-variant: #c3c7cf;
    --outline: #8d9199;
    --outline-variant: #43474e;
  }
}

:root[data-tema="oscuro"] {
    --primary: #9ecaff;
    --on-primary: #003257;
    --primary-container: #00497c;
    --on-primary-container: #d3e4f7;
    /* ⚠️ `--secondary` faltaba en los dos bloques oscuros: estaba definida
       solo en claro, asi que cualquier cosa que la usara quedaba gris oscuro
       sobre fondo oscuro. Lo encontre al darle color a la serie del disco. Es
       el mismo error que `--surface-variant` en -h, que no existia y hacia
       caer siempre al literal gris claro. */
    --secondary: #b9c8da;
    --secondary-container: #3a4857;
    --on-secondary-container: #d6e4f7;
    --tertiary: #ffb870;
    --tertiary-container: #6a3c00;
    --on-tertiary-container: #ffdcbe;
    --error: #ffb4ab;
    --error-container: #93000a;
    --on-error-container: #ffdad6;
    --ok: #9ed7ab;
    --ok-container: #1f4d2c;
    --surface: #111417;
    --surface-1: #191c20;
    --surface-2: #1d2024;
    --surface-3: #22262a;
    --on-surface: #e2e2e6;
    --on-surface-variant: #c3c7cf;
    --outline: #8d9199;
    --outline-variant: #43474e;
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  font-family: var(--font);
  font-size: var(--t-body);
  line-height: 1.5;
  color: var(--on-surface);
  background: var(--surface);
  -webkit-font-smoothing: antialiased;
}

/* ---------------------------------------------------------------- Estructura */
/* ═══════════════════════════════════════════════════ ESTRUCTURA (v0.5.4-h)
 *
 * Es la de Material Design: «top app bar» de ancho completo arriba, y debajo
 * «navigation drawer» + contenido. La misma que usan Gmail, Drive y Calendar,
 * que fue el pedido de Diego.
 *
 * La barra estaba ANTES dentro del contenido, y eso traia dos problemas: para
 * llegar a los bordes tenia que colgarse de margenes negativos que habia que
 * mantener iguales al padding del contenido (y ya se habian desincronizado,
 * dejandola fuera de la pantalla en anchos medios), y quedaba visualmente por
 * debajo del menu cuando en realidad manda sobre toda la aplicacion.
 *
 * NADA DE ANCHOS FIJOS salvo el drawer, que es el unico elemento que de
 * verdad tiene una medida propia. El contenido usa el espacio que hay, con un
 * tope de lectura generoso y padding fluido con `clamp()`.
 */
:root {
  --appbar-h: 62px;
}

.appbar {
  position: sticky; top: 0; z-index: 30;
  display: flex; align-items: center; gap: 12px;
  height: var(--appbar-h); padding: 0 clamp(10px, 2vw, 20px);
  background: var(--surface-1);
  border-bottom: 1px solid var(--outline-variant);
}

/* El sandwich: solo en pantalla chica (ver el media query de abajo). */
.appbar-menu {
  display: none; appearance: none; background: transparent; border: none;
  border-radius: 999px; cursor: pointer; color: var(--on-surface);
  font-size: 20px; line-height: 1; flex: none;
  min-width: 44px; min-height: 44px;
  align-items: center; justify-content: center;
}
.appbar-menu:hover { background: var(--surface-3); }

/* La marca. `flex: none` para que no la aplaste el buscador, y el nombre del
   edificio recortado con puntos suspensivos en vez de romper el renglon. */
.appbar-marca {
  display: flex; align-items: center; gap: 9px;
  text-decoration: none; color: inherit; min-width: 0;
  padding: 4px 6px; border-radius: var(--r-m);
  /* `flex: 0 1 auto` y no `none`: en un ancho justo la marca se encoge y el
     nombre se recorta con puntos suspensivos, en vez de empujar las acciones
     fuera de la pantalla. El escudo no se encoge nunca. */
  flex: 0 1 auto;
}
.appbar-marca:hover { background: var(--surface-3); }
.appbar-escudo {
  font-size: 19px; line-height: 1;
  display: grid; place-items: center;
  width: 34px; height: 34px; flex: none; border-radius: 10px;
  background: var(--primary-container);
}
.appbar-sitio {
  font-size: var(--t-title-s); font-weight: 600; letter-spacing: -.01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* El edificio, del lado del usuario. Con forma de etiqueta y no de texto
   suelto: es un DATO de contexto —en que sistema estoy metido— y tiene que
   leerse de un vistazo sin competir con el nombre del producto. */
.appbar-edificio {
  font-size: var(--t-label); color: var(--on-secondary-container);
  background: var(--secondary-container);
  font-weight: 500; padding: 5px 11px; border-radius: 999px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  max-width: 30vw; flex: 0 1 auto;
}

.appbar-acciones {
  display: flex; align-items: center; gap: 10px; margin-left: auto;
  flex: none; min-width: 0;
}
/* El selector de nivel y la cuenta. En la barra van juntos; en un telefono
   se mudan al cajon (ver el media query), asi que se los envuelve en un solo
   elemento — mover uno es mover los dos. */
.nav-niveles { display: contents; }
.appbar-hora {
  font-size: var(--t-label); color: var(--on-surface-variant);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.appbar-btn {
  appearance: none; background: transparent; border: none;
  border-radius: 999px; cursor: pointer; color: var(--on-surface);
  font-size: var(--t-body); line-height: 1;
  min-width: 40px; min-height: 40px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
}
.appbar-btn:hover { background: var(--surface-3); }

/* La cuenta: circulo con la inicial, que es el patron de Material. En
   pantalla chica el circulo es lo unico que queda — el nombre y el rol no
   entran, y el circulo alcanza para saber con quien se entro. */
.appbar-user {
  display: flex; align-items: center; gap: 9px; flex: none;
  text-decoration: none; color: inherit;
  padding: 3px 8px 3px 3px; border-radius: 999px;
}
.appbar-user:hover { background: var(--surface-3); }
.appbar-avatar {
  display: grid; place-items: center; flex: none;
  width: 34px; height: 34px; border-radius: 50%;
  background: var(--primary); color: var(--on-primary);
  font-size: var(--t-body-s); font-weight: 600;
}
.appbar-quien { display: flex; flex-direction: column; line-height: 1.2; }
.appbar-user-n { font-size: var(--t-body-s); font-weight: 500; }
.appbar-user-r { font-size: var(--t-label-s); color: var(--on-surface-variant); }

.shell { display: flex; align-items: flex-start; }

/* El drawer. Es el UNICO elemento con un ancho propio, y tiene sentido que lo
   tenga: los nueve nombres de pantalla necesitan un ancho estable para no
   reacomodarse a cada rato.

   `top: var(--appbar-h)` y la altura restada: se pega debajo de la barra, no
   arriba, porque la barra ya ocupa esa franja. */
.nav {
  width: var(--nav-w);
  flex: 0 0 var(--nav-w);
  background: var(--surface-1);
  border-right: 1px solid var(--outline-variant);
  padding: 14px 12px;
  position: sticky;
  top: var(--appbar-h);
  height: calc(100vh - var(--appbar-h));
  overflow-y: auto;
}


/* Ítems de navegación: la píldora de M3. */
.nav-item {
  display: flex; align-items: center; gap: 14px;
  padding: 0 16px; height: 52px;
  border-radius: var(--r-xl);
  color: var(--on-surface-variant);
  text-decoration: none;
  font-size: var(--t-body-s); font-weight: 500;
  transition: background .15s ease;
}
.nav-item:hover { background: var(--surface-3); }
.nav-item.active {
  background: var(--secondary-container);
  color: var(--on-secondary-container);
  font-weight: 600;
}
/* Los tamaños de ICONO quedan literales a proposito: no son pasos de una
   escala tipografica, son «cuan grande se dibuja este emoji al lado de este
   texto». Meterlos en la escala haria que cambiar el cuerpo del texto
   cambiara los iconos, que no es lo que uno quiere. */
.nav-ico { font-size: 19px; width: 22px; text-align: center; }

.nav-foot {
  margin-top: 22px; padding: 14px 16px 0;
  border-top: 1px solid var(--outline-variant);
  font-size: var(--t-label-s); color: var(--on-surface-variant);
}

/* EL CONTENIDO USA EL ESPACIO QUE HAY.
 *
 * Diego: «la web no debería ser 800px o lo que sea fijo, debería ser
 * responsive». El padding es fluido con `clamp()` —16 px en un telefono, 32
 * en un escritorio— y el ancho lo pone el espacio disponible.
 *
 * El tope de 1400 px NO es un ancho fijo: es un limite de LECTURA. Un renglon
 * de texto de 2000 px es incomodo de leer, y las ayudas de los ajustes son
 * parrafos. Es lo que hacen Gmail y la propia documentacion de Material. Las
 * grillas de adentro (el tablero, las estadisticas) se acomodan solas con
 * `auto-fill`, asi que en una pantalla ancha se ven mas columnas y no
 * columnas mas anchas. */
.main {
  flex: 1; min-width: 0;
  padding: clamp(16px, 2.2vw, 30px) clamp(16px, 2.6vw, 32px) 140px;
  max-width: 1400px;
}

.page-title { font-size: var(--t-headline); font-weight: 600; letter-spacing: -.02em;
                    margin: 0 0 4px; }
.page-sub   { color: var(--on-surface-variant); margin: 0 0 26px;
              font-size: var(--t-body-s); }

/* -------------------------------------------------------------------- Tarjetas */
.card {
  background: var(--surface-1);
  border: 1px solid var(--outline-variant);
  border-radius: var(--r-l);
  margin-bottom: 18px;
  overflow: hidden;
}
.card-head {
  display: flex; align-items: flex-start; gap: 14px;
  padding: 18px 22px;
  cursor: pointer;
  user-select: none;
}
.card-head:hover { background: var(--surface-2); }
.card-ico {
  width: 38px; height: 38px; flex: none;
  border-radius: 10px;
  background: var(--primary-container);
  color: var(--on-primary-container);
  display: grid; place-items: center; font-size: 18px;
}
.card-t  { font-size: var(--t-title-s); font-weight: 600; }
.card-d  { font-size: var(--t-label); color: var(--on-surface-variant); margin-top: 2px; }
.card-chev {
  margin-left: auto; align-self: center;
  color: var(--on-surface-variant);
  transition: transform .2s ease; font-size: var(--t-label);
}
.card.closed .card-chev { transform: rotate(-90deg); }
.card.closed .card-body { display: none; }
.card-body { padding: 4px 22px 20px; }

/* --------------------------------------------------------------------- Campos */
.field {
  display: grid;
  grid-template-columns: 1fr 300px;
  gap: 8px 26px;
  align-items: start;
  padding: 16px 0;
  border-top: 1px solid var(--outline-variant);
}
.field:first-child { border-top: none; }

/* v0.5.4 — SE ESCONDE POR NIVEL, no por una casilla «avanzados».
   El nivel que llega en cada ajuste es `basico` / `instalador` / `fabrica`.
   Ojo con el orden de estas reglas: la de arriba esconde, las de abajo
   muestran, y ganan por venir después (misma especificidad). */
.field.lvl-instalacion, .field.lvl-fabrica { display: none; }
body.ver-instalacion .field.lvl-instalacion { display: grid; }
body.ver-fabrica    .field.lvl-instalacion,
body.ver-fabrica    .field.lvl-fabrica    { display: grid; }
/* Una tarjeta cuyos ajustes quedaron todos escondidos no se dibuja vacía. */
.card.sin-visibles { display: none; }

/* v0.5.4-d — Las tarjetas escritas a mano se esconden por nivel igual que los
   campos. La clase la pone el JS y no el CSS porque estas tarjetas no son
   todas `display: block` (hay `h2` y párrafos sueltos con `data-nivel`), y
   `display: none` es lo único que sirve para todas. */
.oculto-nivel { display: none !important; }

/* Pie de una seccion: cuantos ajustes estan pisados y como volverlos. Va
   separado por una linea porque no es un campo mas: es una accion sobre todos
   los de arriba. */
.sec-reset {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin-top: 4px; padding-top: 14px;
  border-top: 1px solid var(--outline-variant);
}
.sec-reset .btn { margin-left: auto; }

/* --------------------------------------------- Quien cambio que (v0.5.4-l)
   Una fila por cambio: cuando a la izquierda, que cambio al medio. El valor
   viejo tachado y el nuevo resaltado, para que la direccion del cambio se lea
   sin tener que comparar dos numeros. */
.cam-fila {
  display: flex; gap: 14px; align-items: flex-start;
  padding: 12px 0; border-top: 1px solid var(--outline-variant);
}
.cam-fila:first-child { border-top: none; }
.cam-cuando {
  flex: none; min-width: 82px; font-size: var(--t-label);
  font-variant-numeric: tabular-nums;
}
.cam-qué { min-width: 0; flex: 1; }
.cam-t { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
         font-size: var(--t-body-s); }
.cam-val {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin: 4px 0 3px; font-size: var(--t-label); font-family: var(--mono);
}
.cam-antes { color: var(--on-surface-variant); text-decoration: line-through; }
.cam-flecha { color: var(--on-surface-variant); }
.cam-despues {
  color: var(--on-secondary-container); background: var(--secondary-container);
  padding: 1px 7px; border-radius: 6px;
}
/* El historial desplegado dentro de un campo: mas chico, y con una linea que
   lo separa del ajuste al que pertenece. */
.cam-pie { grid-column: 1/-1; margin-top: 8px; }
.cam-mini {
  margin-top: 8px; padding-left: 12px;
  border-left: 2px solid var(--outline-variant);
}
.cam-mini .cam-fila { padding: 8px 0; }

/* v0.5.4-d — «al reiniciar»: un ajuste que se guarda ahora y se aplica
   despues. Se dibuja como las insignias de procedencia porque es lo mismo
   —informacion sobre de donde sale y cuando vale ese valor— y se pinta con el
   color de aviso, no con el de error: no esta mal, falta un paso. */
.src.frio {
  background: var(--tertiary-container, var(--surface-3));
  color: var(--on-tertiary-container, var(--on-surface-variant));
  border-color: transparent;
}

.f-label { font-size: var(--t-body-s); font-weight: 500; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.f-help  { font-size: var(--t-label); color: var(--on-surface-variant); margin-top: 4px; max-width: 62ch; }
.f-ctl   { display: flex; align-items: center; gap: 8px; justify-content: flex-end; }

/* Insignia de procedencia: el elemento distintivo del panel.
   Todo ajuste tiene tres orígenes posibles y saber cuál manda es la mitad de
   entender por qué el sistema hace lo que hace. */
.src {
  font-size: var(--t-label-s); font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase;
  padding: 2.5px 8px; border-radius: 6px;
  border: 1px solid var(--outline-variant);
  color: var(--on-surface-variant);
  white-space: nowrap;
}
.src.estado  { background: var(--primary-container); color: var(--on-primary-container); border-color: transparent; }
.src.entorno { background: var(--surface-3); }
.src.fabrica { opacity: .55; }

.f-reset {
  border: none; background: none; cursor: pointer;
  color: var(--on-surface-variant);
  font-size: var(--t-body); border-radius: 50%;
  visibility: hidden;
  /* 40 px: el minimo de M3 para un icono suelto. Medía 27 y era el control
     mas chico del panel — justo el que deshace un cambio. */
  min-width: 40px; min-height: 40px;
  display: inline-flex; align-items: center; justify-content: center;
}
.field.dirty .f-reset, .field.overridden .f-reset { visibility: visible; }
.f-reset:hover { background: var(--surface-3); color: var(--primary); }

.field.dirty .f-label::after {
  content: "sin guardar";
  font-size: var(--t-label-s); font-weight: 600; text-transform: uppercase;
  letter-spacing: .04em;
  background: var(--tertiary-container); color: var(--on-tertiary-container);
  padding: 2.5px 8px; border-radius: 6px;
}

/* Controles */
/* v0.5.2 — `password` se sumó al agregar el login. Sin esto los campos de la
   pantalla de entrada salían con el estilo de fábrica del navegador, que al
   lado del resto del panel se ve como un error. */
input[type=text], input[type=password], input[type=number], select, textarea {
  font-family: var(--font); font-size: var(--t-body-s);
  color: var(--on-surface);
  background: var(--surface);
  border: 1px solid var(--outline);
  border-radius: var(--r-s);
  padding: 10px 12px;
  width: 100%;
  transition: border-color .15s, box-shadow .15s;
}
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--primary) 26%, transparent);
}
input[type=number] { max-width: 130px; }
.unit { font-size: var(--t-label); color: var(--on-surface-variant); white-space: nowrap; }

/* Quién está adentro, al pie del menú lateral (v0.5.2).
   Margen fijo y NO `margin-top:auto`: para que «auto» empuje al pie haría
   falta convertir `.nav` en flex column, y eso recalcularía el espaciado de
   todo el menú que hoy anda bien. No vale el riesgo por 20 píxeles. */
.nav-user {
  display: flex; gap: 6px; flex-wrap: wrap;
  margin-top: 14px; padding-top: 12px;
  border-top: 1px solid var(--outline-variant);
}
.nav-user-quien { font-size: var(--t-label); line-height: 1.35; min-width: 0; }
.nav-user-quien b { overflow-wrap: anywhere; }
.nav-user-rol { font-size: var(--t-label-s); color: var(--on-surface-variant); }

/* Interruptor M3 */
.sw { position: relative; display: inline-block; width: 52px; height: 32px; flex: none; }
.sw input { opacity: 0; width: 0; height: 0; }
.sw span {
  position: absolute; inset: 0; cursor: pointer;
  background: var(--surface-3);
  border: 2px solid var(--outline);
  border-radius: 16px; transition: .18s;
}
.sw span::before {
  content: ""; position: absolute;
  height: 16px; width: 16px; left: 6px; top: 6px;
  background: var(--outline); border-radius: 50%; transition: .18s;
}
.sw input:checked + span { background: var(--primary); border-color: var(--primary); }
.sw input:checked + span::before {
  transform: translateX(20px) scale(1.5);
  background: var(--on-primary); left: 4px; top: 6px;
}
.sw input:focus-visible + span { box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 30%, transparent); }

/* Editor de pares clave/valor (cámaras, eventos, audios) */
.map { width: 100%; grid-column: 1 / -1; }
.map-row { display: flex; gap: 8px; margin-bottom: 8px; align-items: center; }
.map-row input { flex: 1; }
.map-row input.k { max-width: 170px; }
.map-del {
  border: none; background: none; cursor: pointer; color: var(--on-surface-variant);
  font-size: var(--t-title); padding: 6px 9px; border-radius: 50%; flex: none;
}
.map-del:hover { background: var(--error-container); color: var(--on-error-container); }
.map-empty { font-size: var(--t-label); color: var(--on-surface-variant); font-style: italic; padding: 6px 0 10px; }

/* --------------------------------------------------------------------- Botones */
.btn {
  font-family: var(--font); font-size: var(--t-body-s); font-weight: 600;
  border-radius: var(--r-xl); border: none; cursor: pointer;
  padding: 11px 24px;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  /* v0.5.4-f — Area tactil de 48 px, que es lo que pide M3.
   *
   * `.btn-sm` medía 31 px de alto y `.f-reset` 27. En un mouse no molesta; en
   * un telefono es la diferencia entre apretar el boton y apretar al lado — y
   * el telefono es donde el portero va a usar esto.
   *
   * Se hace con `min-height` y no subiendo el padding a proposito: subir el
   * padding agranda el boton VISUALMENTE, y un «Copiar» del tamaño de
   * «Guardar cambios» desordena las filas. Asi el boton se ve igual y el area
   * que responde al dedo es mas grande que lo que se pinta. */
  min-height: var(--tactil);
  transition: background .15s, box-shadow .15s;
  position: relative;
}
/* v0.5.4-f — STATE LAYERS, en vez de `filter: brightness(1.08)`.
 *
 * `brightness` ACLARA, y aclarar no significa lo mismo en los dos temas: en
 * claro, un boton primario azul se lava; en oscuro, se acerca al blanco. O
 * sea que el mismo hover se siente distinto segun el tema, y en uno de los
 * dos se siente mal.
 *
 * M3 usa una capa translucida del color del CONTENIDO encima del boton: 8 %
 * al pasar por arriba, 12 % al apretar. Como es el color del contenido, en un
 * boton claro oscurece y en uno oscuro aclara — solo. */
.btn::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  background: currentColor; opacity: 0; transition: opacity .15s;
  pointer-events: none;
}
.btn:hover:not(:disabled)::after  { opacity: .08; }
.btn:active:not(:disabled)::after { opacity: .12; }
.btn:disabled { opacity: .38; cursor: default; }
.btn-fill  { background: var(--primary); color: var(--on-primary); box-shadow: var(--shadow-1); }
.btn-tonal { background: var(--secondary-container); color: var(--on-secondary-container); }
.btn-text  { background: none; color: var(--primary); padding: 11px 14px; }
.btn-danger{ background: var(--error-container); color: var(--on-error-container); }
.btn-sm    { padding: 7px 16px; font-size: var(--t-label); min-height: 36px; }
/* `.btn-sm` dentro de una fila de tabla o de un campo: 36 px de alto, que es
   el minimo de M3 para controles densos. Los 48 quedan para los botones que
   son la accion principal de la pantalla. */

/* Barra de guardado: aparece sola cuando hay cambios, patrón estándar de M3. */
.savebar {
  position: fixed; left: var(--nav-w); right: 0; bottom: 0;
  background: var(--surface-3);
  border-top: 1px solid var(--outline-variant);
  padding: 14px 32px;
  display: flex; align-items: center; gap: 16px;
  box-shadow: var(--shadow-2);
  transform: translateY(110%);
  transition: transform .22s cubic-bezier(.2,0,0,1);
  z-index: 40;
}
.savebar.on { transform: translateY(0); }
.savebar-txt { font-size: var(--t-body-s); font-weight: 500; }
.savebar-sp { margin-left: auto; display: flex; gap: 10px; }

/* ------------------------------------------------------------------- Avisos */
.banner {
  display: flex; gap: 13px; align-items: flex-start;
  padding: 14px 18px; border-radius: var(--r-m);
  margin-bottom: 18px; font-size: var(--t-body-s);
}
.banner-warn { background: var(--tertiary-container); color: var(--on-tertiary-container); }
.banner-err  { background: var(--error-container);    color: var(--on-error-container); }
.banner-ok   { background: var(--ok-container);       color: var(--on-surface); }
.banner-info { background: var(--surface-2); color: var(--on-surface-variant); border: 1px solid var(--outline-variant); }
.banner b { display: block; margin-bottom: 2px; }
.banner ul { margin: 6px 0 0; padding-left: 18px; }

/* v0.5.3 — Listas de la ayuda. Antes no habia ninguna regla para `ul` fuera
   de los banners, asi que una lista en el cuerpo de una tarjeta salia con el
   sangrado por omision del navegador, distinto en cada uno. */
.lista { margin: 8px 0 0; padding-left: 20px; }
.lista li { margin: 6px 0; line-height: 1.5; }
.lista li b { font-weight: 600; }

/* Aviso efímero */
.toast {
  position: fixed; bottom: 96px; left: 50%; transform: translateX(-50%) translateY(20px);
  background: var(--on-surface); color: var(--surface);
  padding: 13px 22px; border-radius: var(--r-s);
  font-size: var(--t-body-s); box-shadow: var(--shadow-2);
  opacity: 0; pointer-events: none; transition: .22s; z-index: 60;
  max-width: 90vw;
}
.toast.on { opacity: 1; transform: translateX(-50%) translateY(0); }
.toast.bad { background: var(--error); color: #fff; }

/* -------------------------------------------------------------------- Barra sup */
.topbar { display: flex; align-items: center; gap: 16px; margin-bottom: 24px; flex-wrap: wrap; }
.topbar .grow { flex: 1; }
.chk { display: flex; align-items: center; gap: 9px; font-size: var(--t-body-s); color: var(--on-surface-variant); cursor: pointer; }

/* ------------------------------------------------- Botones segmentados (M3)
   Material Design los usa para elegir UNA opción de pocas y excluyentes, que
   es exactamente «hasta qué nivel de ajustes quiero ver». Un desplegable para
   tres opciones esconde las opciones; una fila de casillas sugiere que se
   pueden combinar. */
.seg { display: inline-flex; border-radius: 999px; overflow: hidden;
       border: 1px solid var(--outline); }
.seg button {
  appearance: none; background: transparent; cursor: pointer;
  border: none; border-left: 1px solid var(--outline);
  padding: 7px 16px; font: inherit; font-size: var(--t-label); font-weight: 500;
  color: var(--on-surface-variant); transition: background .15s;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  min-height: 36px;
}
.seg button:first-child { border-left: none; }
.seg button:hover { background: var(--surface-3); }
.seg button[aria-pressed="true"] {
  background: var(--secondary-container); color: var(--on-secondary-container);
}
.seg button[aria-pressed="true"]::before { content: "✓"; font-size: var(--t-label-s); }
.seg-lbl { font-size: var(--t-label); color: var(--on-surface-variant); }

/* ------------------------------------------------- El tablero de Inicio
   Baldosas: una por cosa que hay que saber. Se acomodan solas con
   `auto-fill`, asi que la misma grilla sirve para el celular (una columna)
   y para el escritorio (tres o cuatro) sin media queries.

   El estado se ve por el borde IZQUIERDO y por el icono, no solo por color:
   un borde de color como unico indicador no lo distingue quien no ve bien
   los colores. */
.tablero {
  display: grid; gap: 14px; margin-bottom: 10px;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
}
.bald {
  display: flex; gap: 13px; align-items: flex-start;
  background: var(--surface-1); border: 1px solid var(--outline-variant);
  border-left: 4px solid var(--outline-variant);
  border-radius: var(--r-l); padding: 15px 17px;
}
.bald-mal {
  border-left-color: var(--error, #b3261e);
  background: var(--error-container, var(--surface-1));
}
.bald-ico { font-size: 21px; line-height: 1.1; }
.bald-txt { min-width: 0; }
.bald-t {
  font-size: var(--t-label-s); font-weight: 600; letter-spacing: .05em;
  text-transform: uppercase; color: var(--on-surface-variant);
}
.bald-v { font-size: 19px; font-weight: 500; margin-top: 3px; }
.bald-d {
  font-size: var(--t-label); color: var(--on-surface-variant); margin-top: 5px;
  line-height: 1.45;
}
.bald-a {
  display: inline-block; margin-top: 8px; font-size: var(--t-label);
  font-weight: 500; color: var(--primary); text-decoration: none;
}
.bald-a:hover { text-decoration: underline; }

/* Una linea por evento en «Lo ultimo que paso». */
.ev-fila {
  display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap;
  padding: 9px 0; border-top: 1px solid var(--outline-variant);
  font-size: var(--t-body-s);
}
.ev-fila:first-child { border-top: none; }

/* Selector de rol en la tabla de personas: chico, sin borde de campo, para
   que no compita con las insignias de la misma fila. */
.sel-rol {
  font: inherit; font-size: var(--t-label); padding: 3px 6px;
  border-radius: 8px; border: 1px solid var(--outline-variant);
  background: var(--surface-2); color: var(--on-surface); cursor: pointer;
  max-width: 190px;
}


/* ------------------------------------------------------------- Buscador
   Los resultados salen en una capa flotante y no empujando la pagina: es un
   sobrevuelo, no una navegacion. Y se cierra con Escape o al perder el foco,
   que es lo que uno intenta primero. */
.buscador {
  position: relative; min-width: 0;
  /* Crece con el espacio libre y se achica hasta desaparecer del todo en
     pantalla chica (ver el media query): en un telefono, un buscador y una
     marca no entran juntos, y saber en que edificio se esta importa mas. */
  flex: 1 1 260px; max-width: 520px;
}
.buscador input {
  width: 100%; font: inherit; font-size: var(--t-body-s); padding: 7px 12px;
  border-radius: 999px; border: 1px solid var(--outline-variant);
  background: var(--surface-2); color: var(--on-surface);
}
.buscador input:focus { outline: none; border-color: var(--primary); }
.buscador-res {
  position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 30;
  max-height: 60vh; overflow-y: auto;
  background: var(--surface-1); border: 1px solid var(--outline-variant);
  border-radius: var(--r-m); box-shadow: 0 8px 24px rgba(0, 0, 0, .18);
  min-width: 320px;
}
.res-item {
  display: block; width: 100%; text-align: left; appearance: none;
  background: transparent; border: none; cursor: pointer; font: inherit;
  padding: 10px 14px; border-top: 1px solid var(--outline-variant);
  color: inherit;
}
.res-item:first-child { border-top: none; }
.res-item:hover, .res-item:focus { background: var(--surface-3); outline: none; }
.res-t { font-size: var(--t-body-s); font-weight: 500; }
.res-d {
  font-size: var(--t-label-s); color: var(--on-surface-variant); margin-top: 2px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.res-donde {
  font-size: var(--t-label-s); text-transform: uppercase; letter-spacing: .04em;
  color: var(--on-surface-variant); margin-top: 3px;
}
.res-vacio { padding: 14px; font-size: var(--t-label); color: var(--on-surface-variant); }

/* v0.5.4-e — El campo al que llegaste desde el buscador, marcado unos
   segundos. Sin esto el buscador te deja en la pantalla correcta con la
   tarjeta abierta y quince campos iguales: encontrar sigue siendo tu
   problema. */
@keyframes destello {
  from { background: var(--primary-container); }
  to   { background: transparent; }
}
.field.buscado { animation: destello 2.5s ease-out; }

/* El velo, solo cuando el menu esta abierto. */
.velo {
  position: fixed; inset: 0; z-index: 39;
  background: rgba(0, 0, 0, .45);
}

/* ══════════════════════════════════════════════ PANTALLA CHICA (v0.5.4-h)
 *
 * Un solo corte, en 900 px, y la regla es la de Material: abajo de eso el
 * drawer pasa a ser MODAL —entra desde la izquierda con velo— y aparece el
 * sandwich. Arriba de eso el drawer es permanente y el sandwich no existe:
 * un boton para esconder un menu que ya esta comodo es un control para
 * empeorar la pantalla.
 *
 * Antes habia DOS cortes (720 y 900) tocando piezas que tenian que moverse
 * juntas, y en el medio la barra se salia de la pantalla. Uno solo.
 */
@media (max-width: 900px) {
  .appbar-menu { display: inline-flex; }

  /* EL DRAWER, MODAL. No se `display: none`: se lo saca con `transform`, asi
     la animacion la hace el compositor y no recalcula el layout de la pagina
     entera al abrir. */
  .nav {
    position: fixed; left: 0; z-index: 40;
    /* ⚠️ ARRANCA DEBAJO DE LA APP BAR, no arriba de todo.
     *
     * Material admite las dos variantes. Se elige esta porque el pedido de
     * Diego es que «el logo de Pretor tenga que estar siempre visible»: un
     * cajon que arranca en 0 tapa el sandwich y el logo justo cuando esta
     * abierto, y para volver a verlos hay que cerrarlo. Asi el logo no se va
     * nunca, y el sandwich queda a mano para cerrar. */
    top: var(--appbar-h); bottom: 0;
    width: 300px; max-width: 86vw; height: calc(100vh - var(--appbar-h));
    flex: none; overflow-y: auto; padding: 16px 12px;
    transform: translateX(-100%);
    transition: transform .22s ease;
    box-shadow: var(--shadow-2);
  }
  body.menu-abierto .nav { transform: translateX(0); }
  /* El velo, tambien debajo de la barra: si la tapara, el sandwich dejaria de
     poder cerrarla y habria que adivinar que se cierra tocando al costado. */
  .velo { top: var(--appbar-h); }
  /* Con el cajon abierto, el fondo no se desplaza: mover lo que esta tapado
     es de las cosas que hacen sentir que la pagina se colgo. */
  body.menu-abierto { overflow: hidden; }
  .nav-item { height: auto; white-space: normal; }

  /* El buscador se va: en un telefono, la marca y un buscador no entran
     juntos, y saber en que edificio se esta importa mas. Se llega a el desde
     cualquier pantalla de ajustes igual. */
  .buscador { display: none; }

  /* EL SELECTOR DE NIVEL Y LA CUENTA SE VAN AL CAJON.
   *
   * En la barra de un telefono no entran: son tres botones con texto («Uso
   * diario», «Instalación», «Todos») mas un renglon que dice cuantos ajustes
   * se estan escondiendo. Medido: la barra pasaba de 360 a 418 px y aparecia
   * desplazamiento horizontal.
   *
   * No se duplican en dos lugares — dos copias de un control que tiene que
   * estar sincronizado es una fabrica de bugs. Es UN elemento que el JS MUEVE
   * de padre segun el ancho (`reubicarNiveles`), asi que sus escuchas y su
   * estado viajan con el.
   */
  .nav-niveles { display: block; margin: 4px 0 10px; }
  .nav-niveles .seg { display: flex; }
  .nav-niveles .seg button { flex: 1; }

  /* ⚠️ ESTAS TRES SE PERDIERON al rehacer el media query, y son las que
   * hacen usable un formulario en un telefono. Se reponen con el motivo:
   *
   *   · la barra de guardar arranca en `--nav-w` para no taparse con el
   *     menu; en movil el menu no ocupa lugar, asi que tiene que arrancar
   *     en 0 — si no, se sale 264 px de la pantalla.
   *   · un campo son dos columnas (etiqueta | control) que en 360 px no
   *     entran: pasa a una sola, con el control debajo.
   */
  .savebar { left: 0; padding: 12px 16px; flex-wrap: wrap; }
  .savebar-sp { margin-left: 0; width: 100%; }
  .savebar-sp .btn { flex: 1; }
  .field { grid-template-columns: 1fr; }
  .f-ctl { justify-content: flex-start; }
}

/* Mas angosto todavia: el nombre y el rol del usuario no entran, y queda solo
   el circulo con la inicial — que es el patron de Material y alcanza para
   saber con quien se entro. */
@media (max-width: 560px) {
  .appbar-quien { display: none; }
  /* El reloj se va antes que el edificio: la hora la muestra el telefono en
     su propia barra, y en que edificio estoy no lo muestra nadie. */
  .appbar-hora { display: none; }
  .appbar-edificio { max-width: 42vw; }
  :root { --appbar-h: 56px; }
}

/* --------------------------------------------------------------------- Tablas */
.tbl { width: 100%; border-collapse: collapse; font-size: var(--t-body-s); }
.tbl th {
  text-align: left; font-size: var(--t-label-s); text-transform: uppercase;
  letter-spacing: .05em; color: var(--on-surface-variant);
  padding: 10px 14px; border-bottom: 1px solid var(--outline-variant); font-weight: 600;
}
.tbl td { padding: 13px 14px; border-bottom: 1px solid var(--outline-variant); vertical-align: middle; }
.tbl tr:last-child td { border-bottom: none; }
code, .mono { font-family: var(--mono); font-size: var(--t-label); }

.pill {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--t-label-s); font-weight: 600;
  padding: 4px 11px; border-radius: 20px;
}
.pill-ok   { background: var(--ok-container); color: var(--on-surface); }
.pill-off  { background: var(--surface-3); color: var(--on-surface-variant); }
.pill-adm  { background: var(--primary-container); color: var(--on-primary-container); }
.pill-warn { background: var(--tertiary-container); color: var(--on-tertiary-container); }

/* Métricas de diagnóstico */
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 14px; }
.metric { background: var(--surface-2); border-radius: var(--r-m); padding: 18px; }
.metric-v { font-size: var(--t-display); font-weight: 600; letter-spacing: -.02em; line-height: 1.1; }
.metric-l { font-size: var(--t-label); color: var(--on-surface-variant); margin-top: 5px; }
.metric.alert .metric-v { color: var(--error); }

.empty { text-align: center; padding: 52px 20px; color: var(--on-surface-variant); }
.empty-ico { font-size: 42px; opacity: .4; display: block; margin-bottom: 12px; }

/* ------------------------------------------------------------------ Adaptable */
@media (max-width: 900px) {
  /* v0.5.4-g — Antes esto convertia el menu en una tira horizontal con
   * desplazamiento lateral. Las nueve pantallas no entran, asi que la mitad
   * quedaba fuera de la vista sin nada que lo indicara — y de paso escondia
   * la marca y el nombre del edificio. Ahora es un cajon con sandwich (ver
   * mas arriba); lo que queda aca es el resto del acomodo. */
  .main { padding: 20px 16px 150px; width: 100%; }
  .savebar { left: 0; padding: 12px 16px; flex-wrap: wrap; }
  .field { grid-template-columns: 1fr; }
  .f-ctl { justify-content: flex-start; }
  .savebar-sp { margin-left: 0; width: 100%; }
  .btn { flex: 1; justify-content: center; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}

:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

/* ------------------------------------------------- Agregados v0.5.0 (fixes) */
/* Ejemplo concreto debajo de la ayuda. Se diferencia visualmente porque
   responde otra pregunta: la ayuda dice QUÉ es, el ejemplo dice CÓMO se llena. */
.f-example {
  font-size: var(--t-label);
  color: var(--on-surface-variant);
  margin-top: 6px;
  padding: 7px 11px;
  background: var(--surface-2);
  border-left: 3px solid var(--outline-variant);
  border-radius: 0 6px 6px 0;
  max-width: 62ch;
}
.f-example b { color: var(--on-surface); font-weight: 600; }

/* Explicación permanente de cada métrica: un número sin contexto no se puede
   interpretar, y el tooltip no existe en pantalla táctil. */
.metric-h {
  font-size: var(--t-label-s);
  line-height: 1.45;
  color: var(--on-surface-variant);
  margin-top: 8px;
  opacity: .85;
}

/* Grupos dentro de los desplegables del catálogo de eventos */
optgroup { font-weight: 600; color: var(--on-surface-variant); }
optgroup option { font-weight: 400; color: var(--on-surface); }

/* ------------------------------------------- Consola de registro (v0.5.0) */
/* Monoespaciada y con scroll propio: es un log, no prosa. Se queda pegada al
   final solo si el usuario ya estaba abajo, para no interrumpir su lectura. */
.consola {
  background: #0d1117;
  border: 1px solid var(--outline-variant);
  border-radius: var(--r-s);
  padding: 10px 12px;
  max-height: 460px;
  overflow: auto;
  font-family: var(--mono);
  font-size: var(--t-label-s);
  line-height: 1.6;
  color: #c9d1d9;
}
.log-line { display: flex; gap: 10px; padding: 1px 0; white-space: pre-wrap; word-break: break-word; }
.consola.nowrap .log-line { white-space: nowrap; }
.log-t { color: #6e7681; flex: none; }
.log-n { flex: none; width: 62px; font-size: var(--t-label-s); opacity: .8; }
.log-INFO .log-n { color: #58a6ff; }
.log-WARNING { color: #e3b341; }
.log-ERROR, .log-CRITICAL { color: #ff7b72; }

/* --------------------------------------- Validación en el campo (v0.5.0n) */
/* El límite se muestra ANTES de tipear, y el error EN el campo — no en un
   cartel arriba, donde no se sabe cuál de veinte campos está mal. */
.f-rango { opacity: .7; font-style: italic; }

.f-invalido {
  color: var(--error);
  font-weight: 500;
  margin-top: 6px;
}
.field.invalido input[type=number],
.field.invalido input[type=text] {
  border-color: var(--error);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--error) 22%, transparent);
}
.field.invalido .f-label::after {
  content: "revisar";
  font-size: var(--t-label-s); font-weight: 600; text-transform: uppercase;
  letter-spacing: .04em;
  background: var(--error-container); color: var(--on-error-container);
  padding: 2.5px 8px; border-radius: 6px;
}

/* ------------------------------------------------ v0.5.1 · C6 recursos --- */

/* Subtítulo dentro de una tarjeta. Nació al unificar respaldo y ajustes
   internos en una sola tarjeta (v0.5.1): hacía falta separar dos temas
   adentro del mismo cuerpo sin volver a partirlo en dos. */
.sub-t {
  font-weight: 600; font-size: .95rem; margin: 4px 0 6px;
  color: var(--on-surface, inherit);
}

/* --------------------------------------- v0.5.1-f · C2 listas largas ------ */
/* Altura fija con scroll propio. Sin esto, «Eventos que llegaron» estiraba la
   página sin fin y había que hacer scroll de todo el documento para llegar a
   las secciones de abajo. El tope de filas se elige aparte, en el selector:
   un navegador con miles de filas se pone lento, y eso es un límite real. */
.lista-scroll {
  max-height: 420px;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* ⚠️ `.tabla-wrap` LA USABAN TRES PLANTILLAS Y NO EXISTIA.
   Encontrado el 2026-09-10 por la prueba de clases sin CSS. Es el envoltorio
   que le da scroll horizontal a una tabla ancha, y sin el las tablas de Ayuda
   y de Instalacion desbordaban la pagina en un telefono: el cuerpo entero se
   corria, que es justo lo que la regla responsive prohibe.
   Lo ancho scrollea ADENTRO de su caja, nunca el body. */
.tabla-wrap {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
}
.tabla-wrap > table { min-width: 460px; }
/* El encabezado de la tabla queda fijo al hacer scroll: si no, se pierde de
   vista y las columnas dejan de significar nada. */
.lista-scroll .tbl thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  /* ⚠️ `--surface-2` y no `--surface-container`, que NO EXISTE en la paleta:
     la cadena de respaldo caia siempre a `--surface`, el mismo fondo que el
     cuerpo de la tabla, y al hacer scroll las filas se veian pasar por debajo
     del encabezado fijo. Mismo patron que `--surface-variant` en -h. */
  background: var(--surface-2);
}

/* ================= Estadísticas de uso (v0.5.2-l) ========================= */
/* Gráfico con divs, sin librería: el servidor no tiene internet para un CDN
   y el panel no tiene paso de build. Para barras por día alcanza. */
.stats-controles {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-bottom: 18px;
}
.stats-controles select { max-width: 150px; }
.stats-controles .btn { margin-left: auto; }

.stats-grid {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(165px, 1fr));
}
.stat {
  background: var(--surface-1); border-radius: var(--r-m); padding: 14px 16px;
}
.stat-v { font-size: var(--t-display-s); font-weight: 600; letter-spacing: -.02em; }
.stat-t { font-size: var(--t-label); color: var(--on-surface-variant); margin-top: 2px; }
.stat-h { font-size: var(--t-label-s); color: var(--on-surface-variant); opacity: .75;
          margin-top: 4px; }

/* ====================================================== GRAFICAS (v0.5.4-n)
   Se dibujan en SVG con ejes, grilla y escala. Reemplazan a las de `div`s con
   `height:%` de -j, que mostraban la forma y ningun numero.

   ⚠️ LOS NOMBRES DE LAS SERIES CAMBIARON (`bar-rx` -> `s-rx`) A PROPOSITO.
   Las viejas `.chart-bar.bar-*` ponian el color con `background`, que en un
   `<rect>` de SVG no hace nada: SVG pinta con `fill`. Reusar el nombre habria
   dejado dos definiciones del mismo color para dos motores distintos, que es
   la clase de choque que en -h dejo la barra de arriba en 8 px y gris.

   Tambien se fue `.chart-leg .sw`: `.sw` ya era el interruptor de 52×32 px de
   los formularios, y el cuadradito de la leyenda vivia de que estuviera
   anidado. Ahora es `.gr-sw`, que no se pisa con nada. */
.gr { display: block; max-width: 100%; overflow: visible; }

/* La grilla y la linea de base. La base es mas marcada porque es el cero: sin
   distinguirla, una barra chica parece flotar. */
.gr-grid { stroke: var(--outline-variant); stroke-width: 1; stroke-dasharray: 3 4; }
.gr-base { stroke: var(--outline); stroke-width: 1.5; }

/* Los numeros de los ejes. Chicos pero no decorativos: son la razon de ser de
   todo este cambio, asi que van con el color de texto normal y no atenuados
   hasta desaparecer. */
.gr-ety, .gr-etx {
  font-size: var(--t-label-s); fill: var(--on-surface-variant);
  font-variant-numeric: tabular-nums;      /* que no bailen al redibujar */
}

/* Barras. `fill` y no `background`: es SVG. */
.gr-barra { fill: var(--outline); }
/* El rayado de «reconstruido»: se distingue de un vistazo qué es medición y
   qué es deducción, sin tener que leer la nota. Blanco translúcido, que
   funciona igual sobre las series claras y sobre las oscuras. */
.gr-raya { fill: rgba(255, 255, 255, .5); }
:root[data-theme="dark"] .gr-raya { fill: rgba(0, 0, 0, .38); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .gr-raya { fill: rgba(0, 0, 0, .38); }
}

/* Linea acumulada. Sin relleno: con dos areas encima, cual era cual
   dependia de saber que estaban apiladas, que es justo lo que no se ve. */
/* ⚠️ `polyline.` Y NO `.gr-linea` A SECAS, y esto es la tercera vez que la
   especificidad de CSS me muerde en este archivo.

   Las series necesitan `fill` porque las usan las BARRAS, que son `<rect>` y
   se pintan con fill. Pero `.s-rx` esta mas abajo en la hoja y tiene la misma
   especificidad que `.gr-linea`, asi que le ganaba: las lineas salian
   RELLENAS, y el atributo `fill="none"` que el JS les pone en el nodo no
   alcanza — en SVG el CSS le gana al atributo de presentacion.

   Diego lo reporto dos veces: «sigues haciendo rellenas las graficas de
   telegram y de bajado, hazlas lineales con relleno transparente».
   `polyline.gr-linea` suma el selector de elemento (0,1,1 contra 0,1,0) y
   ademas dice lo que significa: una polilinea no se rellena nunca. */
polyline.gr-linea { fill: none; stroke-linejoin: round; }

/* El TOTAL: solido, naranja y mas gruesa. Pedido de Diego — «linea solida y
   naranja el total del trafico». El naranja es el color mas saturado de la
   paleta, asi que gana la mirada sin competir con el azul de la marca. */
.gr-total { stroke: var(--tertiary); stroke-width: 2.5; stroke-dasharray: none; }

/* Las partes: punteadas y con TRAZOS DISTINTOS entre si, no solo distinto
   color. En una pantalla chica, o para quien no distingue bien los colores,
   dos punteados iguales de colores parecidos son la misma linea. */
.gr-parte { stroke-width: 1.75; }
.gr-parte.s-rx { stroke-dasharray: 2 3; }
.gr-parte.s-tx { stroke-dasharray: 7 4; }

/* La punta del total lleva su color, no el de la marca: es el final de la
   linea naranja. */
.gr-punta { fill: var(--tertiary); stroke: var(--surface); stroke-width: 2; }
.gr-punta-t {
  font-size: var(--t-label); font-weight: 600; fill: var(--on-surface);
  font-variant-numeric: tabular-nums;
}

/* La zona invisible que da el detalle al pasar el mouse. `fill: transparent`
   y no `opacity: 0`: con opacidad cero el navegador igual la considera para
   el hover, pero con `visibility` heredada rara en algunos temas no. */
.gr-zona { fill: transparent; cursor: default; }
.gr-zona:hover { fill: color-mix(in srgb, var(--on-surface) 6%, transparent); }

/* Los colores de cada serie, en un solo lugar para linea, area, barra y
   cuadradito de la leyenda. NO se repiten entre graficas distintas: que dos
   series de graficas distintas compartan color invita a compararlas, y no se
   comparan — una esta en bytes y la otra en porcentaje. */
/* ⚠️ v0.5.4-p — «bajado del grabador» es GRIS y «subido a Telegram» CELESTE.
   Antes bajado era azul y subido naranja, y el naranja ahora es el total: dos
   cosas del mismo color en la misma grafica es lo que hizo que no se
   entendiera cual era cual. */
.s-rx  { fill: var(--outline);        stroke: var(--outline); }
.s-tx  { fill: var(--primary);        stroke: var(--primary); }
.s-cpu { fill: var(--ok);             stroke: var(--ok); }
.s-mem { fill: var(--outline);        stroke: var(--outline); }
.s-msg { fill: var(--primary);        stroke: var(--primary); }
.s-avi { fill: var(--tertiary);       stroke: var(--tertiary); }
/* El disco tiene su propio color: no comparte grafica con ninguna otra serie,
   pero si pantalla, y repetir el color del procesador invitaria a compararlos
   —uno esta en bytes y el otro en porcentaje—. */
.s-disco { fill: var(--secondary);   stroke: var(--secondary); }

.gr-leg {
  display: flex; gap: 16px; align-items: center; flex-wrap: wrap;
  margin: 12px 0 18px; font-size: var(--t-label); color: var(--on-surface-variant);
}
.gr-sw {
  display: inline-block; width: 11px; height: 11px; border-radius: 3px;
  margin-right: 6px; vertical-align: -1px; flex: none;
}
/* El cuadradito de la leyenda se pinta con `background` porque es un `<i>` de
   HTML, no un nodo de SVG. Por eso hay dos declaraciones por serie y no una:
   son dos motores de dibujo distintos. */
.gr-sw.s-rx  { background: var(--outline); }
.gr-sw.s-tx  { background: var(--primary); }
.gr-sw.s-cpu { background: var(--ok); }
.gr-sw.s-mem { background: var(--outline); }
.gr-sw.s-msg { background: var(--primary); }
.gr-sw.s-avi { background: var(--tertiary); }
.gr-sw.s-disco { background: var(--secondary); }
/* El de la linea del total no es un cuadrado de color: es una raya, para que
   se corresponda con lo que se ve en la grafica. */
.gr-sw-total {
  background: var(--tertiary); height: 3px; border-radius: 2px;
  width: 16px; vertical-align: 3px;
}

/* Nombre del edificio en el menú (v0.5.2-o). Se destaca sobre el subtítulo
   normal porque es lo que hay que mirar para saber DÓNDE se está tocando. */
