/* ================================================================
   Lyheroc — Design System
   La identidad sale del design system del sitio lyheroc.com
   (tokens.css del prototipo): azul marino de marca #003066, azul de
   acento #006aff, naranja #f97415 para llamadas a la acción, azul del
   logotipo #2758a4, éxito #16a249. Claro y oscuro.

   Regla de la hoja: TODO color vive en los bloques de tokens de abajo
   (el claro en `:root`, el oscuro dos veces: por preferencia del sistema
   y por elección manual). Fuera de ellos solo se escribe `var(--…)`.
   Un hexadecimal suelto en una regla es un color que no cambia con el
   tema: en oscuro sale un recuadro claro o un texto que no se lee.
   ================================================================ */

/* ── Tipografía ────────────────────────────────────────────────────────────
   La del sitio lyheroc.com: la pila de sistema del design system
   (`ui-sans-serif, system-ui, …`). Decisión del 2-oct-2026; antes era Jost,
   servida desde /static/fonts, y se retiró junto con su archivo.

   Sigue siendo UNA familia —`--display`, `--body` y `--mono` resuelven a la
   misma pila— y sigue sin pedir nada fuera de este dominio: la CSP es
   `default-src 'self'` sin `font-src`, y un <link> a otra fuente quedaría
   bloqueado sin error visible. Una pila de sistema no descarga nada.

   Lo que hay que cuidar: Jost traía figuras tabulares (`tnum`) y por eso pudo
   sustituir a la monoespaciada en cifras y folios. Las fuentes de sistema de
   Mac (SF) y Windows (Segoe UI) también las traen, pero solo se activan
   pidiéndolas: todo lo que alinea dígitos declara
   `font-variant-numeric: tabular-nums` (ver «Cifras alineadas» abajo). */

:root {
  /* ── Neutros ── */
  --bg: #f3f4f6;               /* fondo de la aplicación (secondary del sitio) */
  --panel: #ffffff;            /* tarjetas, menú, modales, cajón */
  /* Papel impreso (etiqueta del QR de la unidad): igual en los dos temas. */
  --papel: #fff;
  --papel-tinta: #000;
  --papel-corte: #999;
  --panel-2: #f1f5f9;          /* cabeceras, filas al pasar, rellenos */
  --input-bg: #ffffff;         /* fondo de los campos */
  --border: #e2e8f0;
  --border-2: #cbd5e1;
  --ink: #0f172a;
  --ink-2: #334155;
  --ink-3: #5c6b80;            /* también: «no se midió» (nunca rojo); un punto más
                                  oscuro que el gris del sitio para pasar 4.5:1 sobre --bg */
  --ink-4: #94a3b8;
  --titulo: #003066;           /* titulares de pantalla, en marino como el sitio */

  /* ── Marca ──
     `--accent` es la línea de marca: filete del menú activo, subrayado de
     pestaña, fila seleccionada, barras de avance y la franja «titular» de las
     tarjetas KPI. Es marino en claro y un azul luminoso en oscuro, porque
     tiene que leerse como LÍNEA sobre el panel en los dos temas.
     El botón primario NO usa `--accent` a pelo: lleva texto blanco y necesita
     su propio par (`--primario*`), que en oscuro es otro azul. */
  --accent: #003066;
  --accent-hover: #00244c;
  --accent-soft: #e5f2ff;      /* azul claro del sitio: fondo del activo */
  --accent-ink: #003066;       /* texto de marca sobre `--accent-soft` o el panel */
  --primario: #003066;         /* fondo del botón primario */
  --primario-2: #006aff;       /* final del degradado del botón primario */
  --primario-hover: #00244c;
  --on-accent: #ffffff;        /* texto sobre un fondo sólido de marca o de alarma */
  --focus: #006aff;            /* anillo de foco: el azul de acento del sitio */
  --naranja: #f97415;          /* llamadas a la acción; se usa con mesura */
  --naranja-hover: #ea650a;
  --on-naranja: #1c0d00;       /* texto sobre naranja: el blanco da 2.8:1 */
  --hero-a: #001d3d;           /* degradado de marca del login */
  --hero-b: #003066;

  /* ── Gramática de color de los KPI ──
     --bad mal · --warn cuesta · --good bien · --info volumen ·
     --accent titular · --ink-3 sin medición (nunca rojo).
     Cada uno trae su `-soft` (fondo) y su `-ink` (texto legible sobre ese
     fondo). El color base es para bordes, puntos y barras: como TEXTO sobre
     claro casi ninguno da el contraste mínimo, y por eso existe el `-ink`. */
  --good: #16a249;
  --good-soft: #e7f6ec;
  --good-ink: #157f3c;
  --bad: #ef4444;
  --bad-soft: #fde8e8;
  --bad-ink: #b91c1c;
  --bad-solid: #dc2626;        /* fondo sólido con texto blanco (botón, globo) */
  --warn: #eab308;
  --warn-soft: #fef9e0;
  /* El ámbar del TEXTO. `--warn` es el amarillo del borde y del punto, y sobre
     un fondo claro no se lee: hace falta uno oscurecido para las letras. */
  --warn-ink: #8a6d00;
  --on-warn: #1f1600;          /* texto sobre un fondo sólido `--warn` */
  /* Volumen en azul cielo y no en el azul de marca: si `--info` fuera el mismo
     azul que `--accent`, una tarjeta de volumen se leería como titular. */
  --info: #0284c7;
  --info-soft: #e0f2fe;
  --info-ink: #075985;
  /* Mapa (js/mapa.js): las teselas de OpenStreetMap son claras; en oscuro se
     atenúan con un filtro para no encandilar. En claro, sin filtro. */
  --mapa-filtro: none;

  /* ── Sombras y velos (tinte marino, como el sitio) ── */
  --shadow: 0 1px 2px rgba(0,48,102,.05), 0 4px 6px -1px rgba(0,48,102,.08);
  --shadow-lg: 0 12px 32px -10px rgba(0,48,102,.28);
  --sombra-cajon: -14px 0 42px -18px rgba(0,29,61,.45);
  --velo: rgba(0,29,61,.45);
  --punto-borde: rgba(0,0,0,.10);
  --shadow-campo: 0 1px 2px rgba(0,0,0,.05);
  --fondo-logo: transparent;   /* recuadro tras el JPEG del logotipo */

  /* ── Formas ── */
  --radius: 8px;
  --radius-sm: 6px;
  --radius-lg: 12px;
  --control-h: 40px;           /* alto de botones y campos, como el sitio */
  --ease: cubic-bezier(.4,0,.2,1);

  color-scheme: light;

  /* UNA familia para todo: la pila de sistema del sitio lyheroc.com.
     `--body` y `--mono` se CONSERVAN como nombres y apuntan a la misma pila:
     hay cientos de sitios que los usan y renombrarlos no gana nada. Lo que no
     puede pasar es que resuelvan a familias distintas. */
  --display: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
  --body: var(--display);
  --mono: var(--display);
  --font: var(--body);
  /* `--bg-2` lo pide Roles (cabecera de su árbol) y nunca estuvo declarado:
     la cabecera salía sin fondo. Alias del relleno de cabeceras. */
  --bg-2: var(--panel-2);

  /* ── Escala tipográfica ──────────────────────────────────────────────
     Ocho pasos y una sola perilla. Antes había **19 tamaños distintos
     declarados a mano en 256 sitios**, y esa es la razón de fondo por la que
     nada parecía alineado: dos textos con el mismo papel salían a 11 y a 12
     según quién escribiera la pantalla.

     **Se baja el techo y se sostiene el suelo, no se escala todo por igual.**
     El texto pequeño no puede encoger al mismo ritmo que el grande, porque ya
     estaba en su límite: los tres peldaños de abajo se quedan (10, 11, 11.5)
     y lo que baja es lo que de verdad se veía grande.

     `--escala` multiplica los ocho a la vez. Subir o bajar la densidad de la
     interfaz entera es cambiar ESE número —0.95 aprieta, 1.05 desahoga— y no
     volver a recorrer 256 sitios. */
  --escala: 1;
  --txt-2xs: calc(10px   * var(--escala));  /* versalitas, folios */
  --txt-xs:  calc(11px   * var(--escala));  /* datos de tarjeta */
  --txt-sm:  calc(11.5px * var(--escala));  /* secundario */
  --txt-md:  calc(12.5px * var(--escala));  /* cuerpo */
  --txt-lg:  calc(13.5px * var(--escala));  /* destacado */
  --txt-xl:  calc(16px   * var(--escala));  /* título de tarjeta */
  --txt-2xl: calc(20px   * var(--escala));  /* título de pantalla */
  --txt-3xl: calc(25px   * var(--escala));  /* cifra grande */
}

/* ── Tema oscuro ───────────────────────────────────────────────────────────
   Valores del modo oscuro del design system (tokens.css), con dos ajustes
   para una aplicación de datos: el panel se separa del fondo (el sitio usa el
   mismo #020817 para los dos y aquí las tarjetas desaparecerían) y cada color
   de la gramática trae su versión clara para texto.

   Se declara DOS veces y las dos tienen que decir lo mismo:
   · por preferencia del sistema, salvo que la persona haya elegido «claro»;
   · por elección manual (`data-theme="dark"` en <html>, lo pone `tema.js`).
   CSS no deja compartir un bloque entre un @media y un selector; la copia es
   el precio de no tener preprocesador. Si cambias uno, cambia el otro. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #020817;
    --panel: #0b1324;
    --panel-2: #1e293b;
    --input-bg: #020817;
    --border: #1e293b;
    --border-2: #334155;
    --ink: #f8fafc;
    --ink-2: #cbd5e1;
    --ink-3: #94a3b8;
    --ink-4: #64748b;
    --titulo: #f8fafc;
    --accent: #3388ff;
    --accent-hover: #5c9dff;
    --accent-soft: #00264c;
    --accent-ink: #a8d1ff;
    --primario: #006be6;
    --primario-2: #004fb0;
    --primario-hover: #005bc4;
    --on-accent: #ffffff;
    --focus: #3388ff;
    --naranja: #fa9247;
    --naranja-hover: #fba467;
    --hero-a: #00264c;
    --hero-b: #020817;
    --good: #3ae478;
    --good-soft: #052e16;
    --good-ink: #86efac;
    --bad: #f87171;
    --bad-soft: #450a0a;
    --bad-ink: #fca5a5;
    --bad-solid: #dc2626;
    --warn: #facc15;
    --warn-soft: #422006;
    --warn-ink: #fde047;
    --on-warn: #1f1600;
    --info: #38bdf8;
    --info-soft: #082f49;
    --info-ink: #7dd3fc;
    --mapa-filtro: invert(.92) hue-rotate(180deg) brightness(.95) contrast(.9);
    --shadow: 0 1px 2px rgba(0,0,0,.4), 0 4px 6px -1px rgba(0,0,0,.35);
    --shadow-lg: 0 12px 32px -10px rgba(0,0,0,.7);
    --sombra-cajon: -14px 0 42px -18px rgba(0,0,0,.8);
    --velo: rgba(0,0,0,.6);
    --punto-borde: rgba(255,255,255,.18);
    --fondo-logo: #ffffff;
    color-scheme: dark;
  }
}
:root[data-theme="dark"] {
  --bg: #020817;
  --panel: #0b1324;
  --panel-2: #1e293b;
  --input-bg: #020817;
  --border: #1e293b;
  --border-2: #334155;
  --ink: #f8fafc;
  --ink-2: #cbd5e1;
  --ink-3: #94a3b8;
  --ink-4: #64748b;
  --titulo: #f8fafc;
  --accent: #3388ff;
  --accent-hover: #5c9dff;
  --accent-soft: #00264c;
  --accent-ink: #a8d1ff;
  --primario: #006be6;
  --primario-2: #004fb0;
  --primario-hover: #005bc4;
  --on-accent: #ffffff;
  --focus: #3388ff;
  --naranja: #fa9247;
  --naranja-hover: #fba467;
  --hero-a: #00264c;
  --hero-b: #020817;
  --good: #3ae478;
  --good-soft: #052e16;
  --good-ink: #86efac;
  --bad: #f87171;
  --bad-soft: #450a0a;
  --bad-ink: #fca5a5;
  --bad-solid: #dc2626;
  --warn: #facc15;
  --warn-soft: #422006;
  --warn-ink: #fde047;
  --on-warn: #1f1600;
  --info: #38bdf8;
  --info-soft: #082f49;
  --info-ink: #7dd3fc;
  --mapa-filtro: invert(.92) hue-rotate(180deg) brightness(.95) contrast(.9);
  --shadow: 0 1px 2px rgba(0,0,0,.4), 0 4px 6px -1px rgba(0,0,0,.35);
  --shadow-lg: 0 12px 32px -10px rgba(0,0,0,.7);
  --sombra-cajon: -14px 0 42px -18px rgba(0,0,0,.8);
  --velo: rgba(0,0,0,.6);
  --punto-borde: rgba(255,255,255,.18);
  --fondo-logo: #ffffff;
  color-scheme: dark;
}

*,*::before,*::after { box-sizing: border-box; margin: 0; padding: 0; }
html { font-size: var(--txt-lg); }
body {
  font-family: var(--font); background: var(--bg); color: var(--ink-2);
  min-height: 100vh; -webkit-font-smoothing: antialiased;
}
a { color: inherit; text-decoration: none; }
table { border-collapse: collapse; width: 100%; }
input,select,textarea,button { font-family: inherit; font-size: inherit; }
input:focus,select:focus,textarea:focus { outline: 2px solid var(--focus); outline-offset: -1px; }
:focus-visible { outline-color: var(--focus); }
/* Enlaces dentro del contenido: el azul de marca, no el color heredado. Los
   del menú y la marca llevan su propia regla. */
.app-content a:not([class]) { color: var(--accent-ink); }
.app-content a:not([class]):hover { text-decoration: underline; }

/* Login */
/* Fondo con el degradado de marca del sitio (azul oscuro → marino); en
   oscuro, el degradado profundo del prototipo, para que la tarjeta destaque. */
.login-wrap { display: flex; align-items: center; justify-content: center; min-height: 100vh; padding: 20px;
  background: var(--hero-a) linear-gradient(135deg, var(--hero-a), var(--hero-b)); }
.login-card { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); padding: 36px 32px 32px; width: 100%; max-width: 380px; text-align: center; }
.login-card h1 { font-family: var(--display); font-weight: 500; font-size: var(--txt-3xl); letter-spacing: -.012em; margin: 12px 0 4px; color: var(--titulo); }
.login-card .sub { color: var(--ink-3); margin-bottom: 24px; }
.login-card .field { text-align: left; margin-bottom: 14px; }
.login-card .field label { display: block; font-family: var(--body); font-size: var(--txt-2xs); font-weight: 600; color: var(--ink-3); margin-bottom: 5px; text-transform: uppercase; letter-spacing: .08em; }
.login-card input[type="text"],.login-card input[type="password"] { width: 100%; min-height: var(--control-h); padding: 9px 12px; border: 1px solid var(--border-2); border-radius: var(--radius-sm); background: var(--input-bg); color: var(--ink); }
.login-card .btn-primary { margin-top: 6px; }

/* Layout
   ─────────────────────────────────────────────────────────────────────────
   No hay barra superior: la marca y la sesión viven dentro de la lateral, así
   que el contenido dispone del alto completo de la ventana. Cualquier `calc()`
   que descontara los 52px del encabezado que había antes está desactualizado. */
.app-layout { display: flex; min-height: 100vh; }

/* Nav
   ─────────────────────────────────────────────────────────────────────────
   La barra es pegajosa y de alto completo con su propio desplazamiento: con
   muchos módulos la lista puede pasar del alto de la ventana, y sin esto el
   menú se iría hacia arriba al recorrer una tabla larga.

   Los enlaces van A SANGRE —sin margen ni esquinas redondeadas— y el estado
   activo lo marcan tres señales a la vez: el filete marino de 3px a la
   izquierda, el fondo azul claro de marca y el texto en marino a peso 600.
   Ninguna depende sola del contraste de un color. */
.app-nav {
  width: 214px; flex-shrink: 0;
  background: var(--panel); border-right: 1px solid var(--border);
  display: flex; flex-direction: column;
  padding: 18px 0 0;
  position: sticky; top: 0; height: 100vh; overflow-y: auto;
  transition: width .2s var(--ease);
}
.app-nav.collapsed { width: 56px; }

/* Marca. El filete debajo —marino a naranja, los dos colores del sitio— es
   lo que ata el logo al resto; es de los pocos sitios donde va el naranja. */
.nav-brand {
  display: block; padding: 0 18px 14px;
  border-bottom: 2px solid var(--accent); margin-bottom: 6px;
  border-image: linear-gradient(90deg, var(--accent), var(--naranja)) 1;
}
.nav-brand { text-decoration: none; color: var(--ink); }
.app-nav a.nav-brand { flex-direction: column; align-items: flex-start; gap: 0;
  border-left: 0; padding: 0 18px 14px; }
.app-nav a.nav-brand:hover { background: none; }
/* Plegada, el logo mide ~40px de ancho y saldría de 12px de alto: ilegible.
   Se oculta y queda el filete de marca. */
.app-nav.collapsed .nav-brand,
.app-nav.collapsed a.nav-brand { padding: 0 8px 12px; }
.app-nav.collapsed .nav-brand .marca-logo,
.app-nav.collapsed .nav-brand .marca-sub { display: none; }

.nav-toggle { align-self: flex-end; background: none; border: none; cursor: pointer; padding: 6px 10px; color: var(--ink-3); border-radius: var(--radius-sm); margin-right: 6px; }
.nav-toggle:hover { background: var(--panel-2); color: var(--ink); }
.app-nav.collapsed .nav-toggle { margin-right: auto; margin-left: 6px; transform: rotate(180deg); }

.nav-links { display: flex; flex-direction: column; gap: 1px; padding: 2px 0 8px; flex: 1; }
.nav-section {
  font-family: var(--body); font-size: var(--txt-2xs); font-weight: 400;
  text-transform: uppercase; letter-spacing: .13em;
  color: var(--ink-4); padding: 16px 18px 5px;
}
.app-nav.collapsed .nav-section { display: none; }
.nav-links hr { border: 0; border-top: 1px solid var(--border); margin: 8px 18px; }

.app-nav a {
  display: flex; align-items: center; gap: 9px;
  padding: 8px 18px; margin: 0; border-radius: 0;
  border-left: 3px solid transparent;
  color: var(--ink-2); font-size: var(--txt-md); font-weight: 500;
}
.app-nav a:hover { background: var(--panel-2); color: var(--ink); }
.app-nav a.active {
  background: var(--accent-soft); color: var(--accent-ink);
  border-left-color: var(--accent); font-weight: 600;
}
.app-nav.collapsed a { justify-content: center; padding: 8px 0; border-left-width: 0; }
.app-nav.collapsed a.active { box-shadow: inset 3px 0 0 var(--accent); }
.app-nav.collapsed .nav-label { display: none; }
.nav-ico { display: flex; align-items: center; flex-shrink: 0; opacity: .75; }
.app-nav a.active .nav-ico { opacity: 1; }

/* Pie de sesión. `margin-top: auto` lo fija abajo aunque la lista sea corta. */
.nav-user {
  margin-top: auto; border-top: 1px solid var(--border);
  padding: 12px 14px 16px; display: flex; flex-direction: column; gap: 8px;
}
.nav-user .quien {
  font-family: var(--body); font-size: var(--txt-2xs); letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-3);
  /* El nombre puede ser largo y no hay a dónde desbordarlo: parte la palabra
     antes que sacar una barra horizontal en el menú. */
  overflow-wrap: anywhere; line-height: 1.5;
}
.app-nav.collapsed .nav-user { padding: 12px 6px 16px; align-items: center; }
.app-nav.collapsed .nav-user .quien { display: none; }
.app-nav.collapsed #logout-btn { width: auto; padding: 6px 8px; }

/* Content */
.app-content { flex: 1; min-width: 0; padding: 24px; max-width: 1400px; }
/* Los titulares del contenido van a peso 600 y en marino, como los del
   sitio (en oscuro, `--titulo` es casi blanco).
   Las reglas con clase propia (.section-header h2, .modal-header h3…) ya lo
   declaran; esto cubre los h2/h3 que los módulos pintan sueltos. */
.app-content h1, .app-content h2, .app-content h3 {
  font-family: var(--display); font-weight: 600; letter-spacing: -.012em;
  color: var(--titulo);
}

/* Barra lateral en pantalla estrecha: pasa a ser una tira horizontal que
   fluye, y el indicador de activo se muda del costado a la base. */
/* **El menú se recoge solo por debajo de 1280 px, y lo decide el JS.**

   Aquí hubo una regla de medios que ponía `.app-nav { width: 56px }` y repetía,
   una por una, las diez declaraciones que `.app-nav.collapsed` ya tenía. Dos
   problemas, y el segundo se reportó a la media hora:

   1. Era una copia. Diez declaraciones duplicadas que se separan a la primera
      edición, y entonces el menú recogido por el corte se vería distinto del
      recogido a mano.
   2. **Rompía el botón.** La regla no miraba la clase, así que por debajo de
      1280 el menú estaba recogido pase lo que pase: quitar `.collapsed` no
      ensanchaba nada y el botón de plegado no hacía nada visible. Justo en las
      pantallas donde más falta hace poder desplegarlo para leer los rótulos.

   El corte sigue existiendo, pero como **valor de partida** que pone `app.js`:
   una clase, un solo aspecto, y la decisión de la persona por encima de la del
   diseño. Ver `ANCHO_MENU_RECOGIDO`. */

@media (max-width: 900px) {
  .app-layout { flex-direction: column; }
  .app-nav {
    width: auto; position: static; height: auto; padding: 0;
    border-right: 0; border-bottom: 1px solid var(--border);
    flex-direction: row; flex-wrap: wrap; align-items: center; gap: 4px;
  }
  .nav-brand, .app-nav a.nav-brand { border-bottom: 0; border-image: none; margin: 0; padding: 8px 14px; width: 150px; }
  .nav-brand .marca-sub { display: none; }
  .nav-toggle { display: none; }
  .nav-links { flex-direction: row; flex-wrap: wrap; flex: 1 1 100%; padding: 0 8px 4px; }
  .nav-section { display: none; }
  .nav-links hr { display: none; }
  .app-nav a { border-left: 0; border-bottom: 2px solid transparent; padding: 7px 12px; }
  .app-nav a.active { border-left: 0; border-bottom-color: var(--accent); }
  .nav-user { margin: 0 0 0 auto; border-top: 0; flex-direction: row;
              align-items: center; padding: 8px 14px; }
  /* Recogido también ocupa el ancho: `.app-nav.collapsed { width: 56px }` pesa
     más que `.app-nav` y en un teléfono dejaba una columna de íconos de ~540 px
     encima del contenido (simulación de operación, 10-oct-2026). */
  .app-nav.collapsed { width: auto; }
  .app-nav.collapsed a { padding: 7px 10px; }
  .app-nav.collapsed .nav-user { padding: 4px 14px 8px; }
  .app-nav.collapsed .nav-user .tema-sel { flex-direction: row; width: auto; }
}

/* Buttons */
button,.btn,.btn-primary,.btn-danger,.btn-ghost { display: inline-flex; align-items: center; gap: 6px; padding: 8px 16px; border-radius: var(--radius-sm); font-weight: 600; font-size: var(--txt-md); cursor: pointer; border: 1px solid transparent; transition: all .12s var(--ease); white-space: nowrap; }
/* Las formas del sitio: controles de 40px, radio de 6px y el primario con el
   degradado de marca (marino → azul de acento). El `min-height` va solo en las
   clases de botón, no en `button`: pestañas, chips y cierres son botones y
   tienen su propio alto. */
.btn,.btn-primary,.btn-danger,.btn-ghost { min-height: var(--control-h); justify-content: center; }
.btn-primary { background-color: var(--primario); background-image: linear-gradient(135deg, var(--primario), var(--primario-2)); color: var(--on-accent); border-color: transparent; }
.btn-primary:hover { background-image: none; background-color: var(--primario-hover); }
.btn-danger { background: var(--bad-solid); color: var(--on-accent); }
.btn-danger:hover { opacity: .9; }
.btn-ghost { background: var(--panel); color: var(--ink-2); border-color: var(--border-2); }
.btn-ghost:hover { background: var(--accent-soft); color: var(--accent-ink); border-color: var(--accent); }
/* Llamada a la acción en naranja, como el sitio. Opcional y con mesura: una
   por pantalla como mucho, o deja de llamar. */
.btn-cta { background: var(--naranja); color: var(--on-naranja); min-height: var(--control-h); justify-content: center; }
.btn-cta:hover { background: var(--naranja-hover); }
button:disabled,.btn:disabled,.btn-primary:disabled,.btn-ghost:disabled,.btn-danger:disabled { opacity: .5; cursor: not-allowed; }
.btn-sm { padding: 4px 10px; font-size: var(--txt-sm); min-height: 30px; }
/* Chip de filtro. El encendido es sólido en el color del botón primario con
   texto blanco: se distingue de uno apagado de lejos, en claro y en oscuro. */
button.pill {
  font-family: var(--body); font-size: var(--txt-2xs); font-weight: 600;
  letter-spacing: .06em; text-transform: uppercase;
  padding: 5px 10px; border-radius: var(--radius-sm);
  border: 1px solid var(--border-2); background: var(--panel); color: var(--ink-2);
}
button.pill:hover { border-color: var(--ink-4); color: var(--ink); }
button.pill.on { background: var(--primario); color: var(--on-accent); border-color: var(--primario); }
.btn-block { width: 100%; justify-content: center; }

/* Alerts */
.alert { padding: 12px 16px; border-radius: var(--radius); font-size: var(--txt-md); margin-bottom: 12px; }
/* Texto en el `-ink` de cada color: el color base como texto sobre su fondo
   suave no llega al contraste mínimo. */
.alert-error { background: var(--bad-soft); color: var(--bad-ink); border: 1px solid var(--bad); }
.alert-success { background: var(--good-soft); color: var(--good-ink); border: 1px solid var(--good); }
.alert-info { background: var(--info-soft); color: var(--info-ink); border: 1px solid var(--info); }
.alert-warning { background: var(--warn-soft); color: var(--warn-ink); border: 1px solid var(--warn); }

/* Table
   ─────────────────────────────────────────────────────────────────────────
   Toda tabla de datos vive dentro de un contenedor que se desplaza SOLO,
   con la altura acotada al viewport y el encabezado pegado arriba.

   Sin tope, `.table-wrap` crece con su contenido: una tabla de 445 filas da
   un contenedor de 21,870 px de alto, así que la barra horizontal queda al
   final de esos 21,870 px —hay que bajar toda la página para mover la tabla a
   los lados— y una vez abajo el encabezado está 22,000 px arriba, sin forma
   de saber qué columna se está leyendo.

   Acotar la altura es lo que hace posible las dos cosas a la vez: deja la
   barra horizontal siempre a la vista y convierte el contenedor en el
   ancestro con scroll que `position: sticky` necesita para funcionar.

   Ojo con la tentación de poner solo `overflow-x`: por especificación, si un
   eje es `visible` y el otro no, el `visible` se computa como `auto`. O sea
   que `overflow-x: auto` YA convertía el contenedor en scroller vertical —de
   altura infinita, por eso el encabezado pegajoso no habría servido de nada
   sin el `max-height`. */
.table-wrap {
  overflow: auto;
  /* Valor de arranque, NO el definitivo: 168px ≈ título de pantalla +
     filtros. Sirve para el primer pintado, antes de que corra el JS.

     La altura real la MIDE `ajustarAltoDeTablas()` en `ui.js` y la escribe
     en línea, porque una constante no puede valer para todas las pantallas:
     con banner y resumen encima, la cabecera ocupa 420-520 px y el borde
     inferior del contenedor —con su barra horizontal— queda por debajo del
     pliegue en un portátil de 1366×768: «se ve horrible en windows, y los
     scroll». */
  max-height: calc(100vh - var(--alto-cabecera, 168px));
  /* Al llegar al final, la rueda no continúa desplazando la página detrás:
     con tablas anidadas en un tablero largo eso da saltos desconcertantes. */
  overscroll-behavior: contain;
}
.table-wrap > table.data thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  /* Fondo opaco obligatorio: con transparencia las filas se ven pasar por
     debajo del encabezado al desplazarse. */
  background: var(--panel);
  /* El borde va como sombra interior, no como `border-bottom`: con
     `border-collapse: collapse` el borde pertenece a la tabla y NO se
     desplaza con la celda pegajosa, así que se queda arriba mientras el
     encabezado baja. Es el clásico "la línea se despega del encabezado". */
  box-shadow: inset 0 -2px 0 var(--border);
  border-bottom: none;
}

/* Dentro de un modal manda el modal: su propio tope es 85vh, y un contenedor
   calculado contra la ventana entera lo llenaría sin dejar sitio al título ni
   al pie, con dos barras de desplazamiento compitiendo. */
.modal .table-wrap { max-height: 55vh; }

table.data { width: 100%; border-collapse: collapse; font-size: var(--txt-md); }
table.data th { text-align: left; padding: 9px 12px; font-family: var(--body); font-weight: 600; color: var(--ink-3); border-bottom: 2px solid var(--border); font-size: var(--txt-xs); text-transform: uppercase; letter-spacing: .07em; white-space: nowrap; }
table.data td { padding: 8px 12px; border-bottom: 1px solid var(--border); color: var(--ink-2); }
table.data tr:hover { background: var(--panel-2); }
table.data .actions { display: flex; gap: 4px; }

/* Status badges */
.status-badge { display: inline-flex; align-items: center; gap: 4px; padding: 2px 9px; border-radius: 999px; font-family: var(--body); font-size: var(--txt-2xs); letter-spacing: .05em; text-transform: uppercase; font-weight: 600; white-space: nowrap; }
.status-badge.bad { background: var(--bad-soft); color: var(--bad-ink); }
.status-badge.warn { background: var(--warn-soft); color: var(--warn-ink); }
.status-badge.good { background: var(--good-soft); color: var(--good-ink); }
.status-badge.info { background: var(--info-soft); color: var(--info-ink); }
.status-badge.muted { background: var(--panel-2); color: var(--ink-3); border: 1px solid var(--border); }

/* Generic badges (for counts etc.) */
.badge { display: inline-flex; align-items: center; justify-content: center; min-width: 22px; height: 22px; padding: 0 8px; border-radius: 999px; font-family: var(--body); font-variant-numeric: tabular-nums; font-size: var(--txt-xs); font-weight: 700; white-space: nowrap; }
.badge-info { background: var(--info-soft); color: var(--info-ink); }
.badge-muted { background: var(--panel-2); color: var(--ink-3); border: 1px solid var(--border); }

table.data .sub-table td { border-bottom: 1px solid var(--border-2); }

/* Stats */
.stats-bar { display: flex; gap: 16px; margin-bottom: 20px; flex-wrap: wrap; }
.stat-card { flex: 1; min-width: 140px; background: var(--panel); border-radius: var(--radius); padding: 16px 20px; border: 1px solid var(--border); box-shadow: var(--shadow); }
/* La franja lateral de color la pone `tarjetaKPI` en línea con el token de la
   gramática (`var(--bad)`, `var(--accent)`…), así que cambia sola con el tema. */
.stat-card .label { font-family: var(--body); font-size: var(--txt-2xs); font-weight: 600; color: var(--ink-3); text-transform: uppercase; letter-spacing: .11em; }
/* Cifra en monoespaciada con figuras tabulares: en una fila de tarjetas los
   dígitos quedan en la misma rejilla y se comparan de un vistazo. */
.stat-card .value { font-family: var(--mono); font-variant-numeric: tabular-nums;
  font-size: var(--txt-3xl); font-weight: 700; letter-spacing: -.025em;
  color: var(--ink); margin-top: 4px;
  /* **Última red: una cifra que no cabe se parte, nunca se sale.** Con la
     escala de `tarjetaKPI` basta en casi todos los casos, pero un valor
     imprevisto —un texto largo donde se esperaba un número— rompería el
     diseño saliéndose por la derecha. Partida es fea; fuera de la tarjeta
     tapa lo que tiene al lado y se lee como que la pantalla está rota.

     **`break-word` y no `anywhere`**, y la diferencia se vio en pantalla:
     `anywhere` entra además en el cálculo del ancho MÍNIMO intrínseco, así
     que el navegador da por bueno un ancho menor y parte cosas que sí
     cabían — «13 de 25» se rompió en dos líneas al probarlo. `break-word`
     solo actúa cuando de verdad no cabe. */
  overflow-wrap: break-word; }

/* La cifra se encoge según cuántos caracteres tiene, y el escalón lo pone
   `tarjetaKPI` contando caracteres —no midiendo píxeles, que exigiría tener
   el elemento ya en el documento—.
   Medido a 1093 px de viewport (un portátil de 1366 al 125 % de escalado):
   `$31,716,850.61` ocupa 202 px en una tarjeta con 140 px útiles. En un Mac a
   1512 la misma cifra se sale 10 px y apenas se nota — de ahí que el mismo
   defecto se vea «más o menos» en uno y «horrible» en el otro. El tamaño
   final lo MIDE `ajustarCifrasKPI` tras el pintado. */
.stat-card.v-media  .value { font-size: var(--txt-2xl); }
.stat-card.v-larga  .value { font-size: var(--txt-xl); }
.stat-card.v-enorme .value { font-size: var(--txt-lg); line-height: 1.25; }

/* Filters */
/* Barra de filtros: a sangre, sin tarjeta.
   Era `background: var(--panel)` con borde y esquinas, y el resultado es que
   una tarjeta con cuatro desplegables competía con la tarjeta de la tabla que
   viene justo debajo: el ojo leía dos bloques donde hay uno solo. */
.filters { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 13px; }
/* El contador de resultados cierra la barra por la derecha. */
.filters .cnt { font-family: var(--mono); font-variant-numeric: tabular-nums;
  font-size: var(--txt-xs); color: var(--ink-3); margin-left: auto; }
.filters input:not([type="checkbox"]):not([type="radio"]),.filters select { min-height: 34px; padding: 6px 10px; border: 1px solid var(--border-2); border-radius: var(--radius-sm); background: var(--input-bg); color: var(--ink); font-size: var(--txt-md); }
.filters input[type="text"] { min-width: 180px; }
/* La casilla de un filtro («Solo abiertas») no es un campo de texto: sin esto
   heredaba alto, padding y borde de los inputs y se pintaba como una caja vacía. */
.filters input[type="checkbox"],.filters input[type="radio"] { width: auto; min-height: 0; padding: 0; accent-color: var(--primario); }
.filters .field.inline { display: flex; flex-direction: row; align-items: center; gap: 6px; margin: 0; }
.filters label { font-family: var(--body); font-size: var(--txt-2xs); font-weight: 600; text-transform: uppercase; letter-spacing: .08em; color: var(--ink-3); }

/* Form */
.field { margin-bottom: 12px; }
.field label { display: block; font-family: var(--body); font-size: var(--txt-2xs); font-weight: 600; text-transform: uppercase; letter-spacing: .08em; color: var(--ink-3); margin-bottom: 5px; }
.field input,.field select,.field textarea { width: 100%; padding: 8px 12px; border: 1px solid var(--border-2); border-radius: var(--radius-sm); background: var(--input-bg); color: var(--ink); font-size: var(--txt-md); box-shadow: var(--shadow-campo); }
.field input:not([type="checkbox"]):not([type="radio"]),.field select { min-height: var(--control-h); }
.field input:focus,.field select:focus,.field textarea:focus { border-color: var(--focus); }
.field input::placeholder,.field textarea::placeholder,.filters input::placeholder { color: var(--ink-4); }
.field input[type="checkbox"],.field input[type="radio"] { accent-color: var(--primario); }
.field textarea { resize: vertical; min-height: 60px; }
/* Casilla con su leyenda al lado (no una caja vacía a lo ancho). */
.field.inline { display: flex; flex-direction: row; align-items: center; gap: 8px; }
.field.inline input[type="checkbox"] { width: auto; flex: none; margin: 0; box-shadow: none; }
.field.inline label { margin: 0; }
/* Una casilla CON su leyenda dentro de un .field (listas de sistemas, evidencia, días…): el estilo
   general de arriba pondría la casilla a todo lo ancho con relleno y la leyenda en bloque y en
   mayúsculas, y se enciman. Aquí la casilla vuelve a su tamaño y la leyenda a texto normal. */
.field label:has(> input[type="checkbox"]), .field label:has(> input[type="radio"]) {
  display: inline-flex; align-items: center; gap: 6px; margin: 3px 12px 3px 0;
  font-family: inherit; font-size: var(--txt-sm); font-weight: 400; text-transform: none;
  letter-spacing: normal; color: var(--ink); cursor: pointer; }
.field label > input[type="checkbox"], .field label > input[type="radio"] {
  width: auto; min-height: 0; padding: 0; margin: 0; border: 0; box-shadow: none; flex: none; }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }

/* Cajón de detalle
   ─────────────────────────────────────────────────────────────────────────
   El detalle de una fila entra deslizando desde la derecha en vez de abrirse
   en un modal centrado. No es estética: revisar veinte unidades con modal son
   sesenta clics —abrir, mirar, cerrar— y se pierde el sitio en la lista cada
   vez. Es el mecanismo de detalle de toda la aplicación.

   **Sin velo, y eso obliga a resolver algo que un cajón con velo no tiene.**
   Con velo da igual tapar la tabla, porque está bloqueada. Sin velo, un cajón
   flotante taparía la parte derecha de una tabla que SIGUE siendo clicable: se
   ve y no se alcanza, que es peor que un modal. Por eso `.app-content` gana
   sangrado mientras el cajón está abierto (ver `body.con-cajon` abajo): la
   tabla se estrecha en vez de quedar cubierta, y como toda tabla vive dentro
   de `.table-wrap` con su propio desplazamiento horizontal, no se pierde
   ninguna columna. */
.cajon {
  position: fixed; top: 0; right: 0;
  width: var(--cajon-ancho); height: 100vh;
  background: var(--panel); border-left: 1px solid var(--border-2);
  box-shadow: var(--sombra-cajon);
  overflow-y: auto; overscroll-behavior: contain;
  z-index: 900;
  /* Fuera de pantalla con 102% y no 100%: el 2% de más esconde la sombra, que
     si no asoma como una línea gris en el borde derecho estando cerrado. */
  transform: translateX(102%);
  transition: transform .22s var(--ease);
}
.cajon.abierto { transform: none; }
/* Quien pide menos movimiento no debería ver el deslizamiento. */
@media (prefers-reduced-motion: reduce) { .cajon { transition: none; } }

:root { --cajon-ancho: min(500px, 94vw); }

body.con-cajon .app-content { padding-right: calc(var(--cajon-ancho) + 24px); }

/* Cabecera pegajosa: el nombre de lo que se está mirando no se va al
   desplazar un detalle largo. */
.cajon-cab {
  position: sticky; top: 0; z-index: 2;
  display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
  padding: 16px 20px; background: var(--panel-2);
  border-bottom: 1px solid var(--border);
  /* Filete de marca arriba: el cajón se reconoce como parte de la aplicación
     aunque tape media pantalla. */
  box-shadow: inset 0 3px 0 var(--accent);
}
.cajon-cab b { display: block; font-family: var(--display); font-weight: 600;
  font-size: var(--txt-xl); letter-spacing: -.01em; color: var(--titulo); }
.cajon-cab span { font-size: var(--txt-sm); color: var(--ink-3); }
.cajon-cerrar {
  flex: none; width: 26px; height: 26px; padding: 0;
  background: none; border: 1px solid var(--border); border-radius: var(--radius-sm);
  color: var(--ink-2); cursor: pointer; font-size: var(--txt-lg); line-height: 1;
}
.cajon-cerrar:hover { background: var(--panel); color: var(--ink); }

/* La gramática del contenido. Es lo que hace que doce detalles escritos por
   separado se lean igual. */
.cajon-sec { padding: 14px 20px; border-bottom: 1px solid var(--border); }
.cajon-sec:last-child { border-bottom: 0; }
.cajon-sec > h4 {
  margin: 0 0 10px; font-family: var(--body); font-weight: 600;
  font-size: var(--txt-2xs); letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-3);
}
/* Etiqueta a la izquierda, cifra a la derecha en mono tabular: la misma regla
   que se acaba de aplicar a las columnas numéricas de las tablas. */
.cajon-kv { display: grid; grid-template-columns: 1fr auto; gap: 5px 12px;
  font-size: var(--txt-md); align-items: baseline; }
.cajon-kv > :nth-child(odd) { color: var(--ink-2); min-width: 0; }
.cajon-kv > :nth-child(even) { font-family: var(--mono);
  font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; }
/* Con texto largo —un enlace, una descripción— el valor no cabe en una línea:
   con `nowrap` estira la columna y se sale de su tarjeta. Aquí la etiqueta se
   encoge y el valor se parte donde haga falta. */
.cajon-kv.texto-largo { grid-template-columns: max-content minmax(0, 1fr); }
.cajon-kv.texto-largo > :nth-child(even) { text-align: left; white-space: normal;
  overflow-wrap: anywhere; min-width: 0; }
/* En un teléfono el valor no cabe en una línea: la insignia tapaba la etiqueta
   («Es[EJECUTADA]») y lo largo se cortaba a la derecha. Etiqueta arriba de su
   valor y todo puede partirse. */
@media (max-width: 480px) {
  .cajon-kv { grid-template-columns: minmax(0, 1fr); gap: 0 12px; }
  .cajon-kv > :nth-child(odd) { margin-top: 6px; font-size: var(--txt-sm); }
  .cajon-kv > :nth-child(even) { text-align: left; white-space: normal;
    overflow-wrap: anywhere; min-width: 0; }
  .cajon-kv .status-badge { white-space: normal; }
}

/* Fila abierta en el cajón (`tr.sel`, la pone `cajon()`). El filete de acento
   a la izquierda es lo que mantiene visible qué fila está abierta al desplazar
   la tabla: solo con fondo, la fila se pierde entre las de `:hover`. */
table.data tbody tr.sel { background: var(--accent-soft); }
table.data tbody tr.sel td:first-child { box-shadow: inset 3px 0 0 var(--accent); }

@media (max-width: 1180px) {
  :root { --cajon-ancho: 100vw; }
  /* En estrecho no caben los dos, así que el cajón ocupa todo y el contenido
     no se sangra: sangrarlo dejaría la tabla en cero. */
  body.con-cajon .app-content { padding-right: 24px; }
}

/* Modal */
.modal-overlay { position: fixed; inset: 0; background: var(--velo); z-index: 1000; display: flex; align-items: center; justify-content: center; padding: 20px; }
.modal { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); width: 100%; max-width: 560px; max-height: 85vh; overflow-y: auto; }
.modal-lg { max-width: 860px; }
.modal-header { display: flex; align-items: center; justify-content: space-between; padding: 16px 20px; border-bottom: 1px solid var(--border); }
.modal-header h3 { font-family: var(--display); font-weight: 600; font-size: var(--txt-xl); letter-spacing: -.01em; color: var(--titulo); }
.modal-body { padding: 20px; }
.modal-footer { padding: 12px 20px; border-top: 1px solid var(--border); display: flex; gap: 8px; justify-content: flex-end; }
.close-btn { background: none; border: none; font-size: var(--txt-2xl); cursor: pointer; color: var(--ink-3); padding: 4px; line-height: 1; }
.close-btn:hover { color: var(--ink); }

/* Empty state */
.empty { text-align: center; padding: 48px 20px; color: var(--ink-3); }
.empty p { font-size: var(--txt-lg); }

/* Section header */
.section-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; flex-wrap: wrap; gap: 8px; }
.section-header h2 { font-family: var(--display); font-weight: 600; font-size: var(--txt-2xl); letter-spacing: -.012em; color: var(--titulo); }

/* Loading */
.skeleton { background: linear-gradient(90deg, var(--panel-2) 25%, var(--border) 50%, var(--panel-2) 75%); background-size: 200% 100%; animation: shimmer 1.5s infinite; border-radius: var(--radius-sm); min-height: 20px; }
@keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }

/* DS grid helpers */
.ds-grid { display: grid; gap: 14px; }
.ds-grid-2 { grid-template-columns: 1fr 1fr; }
.ds-grid-4 { grid-template-columns: repeat(4, 1fr); }
.ds-grid-5 { grid-template-columns: repeat(5, 1fr); }
.ds-num { text-align: right; font-variant-numeric: tabular-nums; }

/* Text helpers */
.text-danger { color: var(--bad); }
.text-warning { color: var(--warn-ink); }
.fw-bold { font-weight: 700; }

/* Responsive */
@media (max-width: 768px) { .form-row { grid-template-columns: 1fr; } .ds-grid-2,.ds-grid-4,.ds-grid-5 { grid-template-columns: 1fr 1fr; } .stats-bar { flex-direction: column; } .stat-card { min-width: auto; } }
@media (max-width: 480px) { .ds-grid-2,.ds-grid-4,.ds-grid-5 { grid-template-columns: 1fr; } .app-content { padding: 16px; } }

/* ── Árbol de permisos (módulo Roles) ─────────────────────────────────────
   Cuatro casillas por módulo agrupadas por sección del menú. El objetivo es
   que se pueda leer de un vistazo qué concede un rol, sin cruzar la vista
   entre una columna de encabezados y una fila lejana. */
.arbol-permisos { display: flex; flex-direction: column; gap: 14px; }

.grupo-permisos {
  border: 1px solid var(--border-2);
  border-radius: 8px;
  overflow: hidden;
}
.cabecera-grupo {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 12px;
  background: var(--panel-2);
  font-family: var(--body); font-weight: 600; font-size: var(--txt-2xs);
  text-transform: uppercase; letter-spacing: .11em;
  color: var(--ink-2);
  cursor: pointer;
}
.modulos-grupo { display: flex; flex-direction: column; }

.fila-permiso {
  display: flex; align-items: center; gap: 12px;
  padding: 7px 12px;
  border-top: 1px solid var(--border-2);
  flex-wrap: wrap;
}
.nombre-modulo {
  flex: 1 1 190px; min-width: 0;
  display: flex; align-items: center; gap: 6px;
  font-size: var(--txt-md); color: var(--ink-2);
}
.nombre-modulo small { color: var(--ink-4); cursor: help; }

.acciones-permiso { display: flex; gap: 4px; flex-wrap: wrap; }
.casilla-accion {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px;
  border: 1px solid var(--border-2);
  border-radius: 999px;
  font-size: var(--txt-xs); color: var(--ink-3);
  cursor: pointer;
  user-select: none;
  /* Ancho fijo para que las cuatro columnas de casillas queden alineadas
     entre módulos: sin esto, "Ver" y "Eliminar" desplazan a sus vecinas y la
     rejilla se lee en zigzag. */
  min-width: 82px;
}
.casilla-accion:hover { border-color: var(--ink-4); }
.casilla-accion:has(input:checked) {
  background: var(--good-soft);
  border-color: var(--good);
  color: var(--ink);
  font-weight: 600;
}
.casilla-accion input { margin: 0; cursor: pointer; }

/* Sección dentro de un módulo: sangrada y con una guía a la izquierda, para que
   se lea que depende de la fila de arriba y no como un módulo más. */
.fila-permiso.es-seccion {
  padding-left: 30px;
  background: var(--panel-2);
  position: relative;
}
.fila-permiso.es-seccion::before {
  content: "";
  position: absolute;
  left: 16px; top: 0; bottom: 0;
  border-left: 2px solid var(--border-2);
}
.fila-permiso.es-seccion .nombre-modulo { font-size: var(--txt-sm); color: var(--ink-3); }

/* Tarjeta genérica: panel con borde, radio y sombra, como el resto de la
   aplicación. */
.card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

/* El encabezado sigue a su columna. En números va a la DERECHA y no al centro
   a propósito: alineados a la derecha los dígitos quedan en la misma vertical
   —unidades con unidades, millares con millares— y dos cifras se comparan de
   un vistazo. Centradas, cada número queda desplazado según su largo y hay que
   leerlas una por una. Es la misma razón por la que aquí se usan figuras
   tabulares. */
table.data th.num, table.data td.num, .num { text-align: right; }

/* ── Una tarjeta que ABRE algo se ve distinta de una que no ────────────────
   Si en una misma fila unas tarjetas abren un detalle y otras no, **sin
   ninguna diferencia visual**, desde fuera se lee como que la aplicación
   responde a veces sí y a veces no, que es peor que si ninguna abriera.

   La marca es discreta a propósito: la flecha en gris y el realce sólo al
   apuntar. Una tarjeta que abre no es más importante que una que informa —a
   veces es al revés—, así que no puede robarle la atención al número, que es
   lo único que hay que leer para decidir si vale la pena entrar. */
.stat-card.abre {
  cursor: pointer;
  position: relative;
  transition: border-color .12s var(--ease), box-shadow .12s var(--ease);
}
.stat-card.abre:hover,
.stat-card.abre:focus-visible {
  border-color: var(--border-2);
  box-shadow: var(--shadow-lg);
}
.stat-card.abre:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.stat-card .abre-flecha {
  position: absolute; top: 8px; right: 10px;
  font-size: var(--txt-lg); line-height: 1; color: var(--ink-4);
  transition: transform .12s var(--ease), color .12s var(--ease);
}
.stat-card.abre:hover .abre-flecha { color: var(--ink-2); transform: translateX(2px); }
/* Qué mide la tarjeta: al pie, separado del dato por una raya fina para que
   no se lea como parte de la cifra. */
.stat-card .kpi-ayuda {
  margin-top: 6px; padding-top: 5px; border-top: 1px dashed var(--border);
  font-size: var(--txt-2xs); line-height: 1.4; color: var(--ink-3);
}

/* ══════════════════════════════════════════════════════════════════════════
   Barra de periodo (`barraPeriodo()` de ui.js)
   ══════════════════════════════════════════════════════════════════════════ */
.ts-periodo { display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 6px 8px; margin-bottom: 14px; }
.ts-seg { display: flex; background: var(--panel-2); border-radius: 6px; padding: 2px; }
.ts-seg button { border: 0; background: none; padding: 5px 10px; border-radius: 5px;
  font-size: var(--txt-sm); color: var(--ink-3); cursor: pointer; }
.ts-seg button.on { background: var(--panel); color: var(--ink); font-weight: 600; box-shadow: var(--shadow); }
.ts-periodo input[type=date], .ts-periodo select { font-size: var(--txt-sm); }
.ts-periodo .ts-vs { font-size: var(--txt-xs); color: var(--ink-3); text-transform: uppercase; letter-spacing: .08em; }
.ts-periodo .ts-leyenda { margin-left: auto; font-size: var(--txt-xs); color: var(--ink-3); }
.ts-leyenda { display: flex; gap: 14px; flex-wrap: wrap; margin-top: 10px; font-size: var(--txt-xs); color: var(--ink-3); }

/* ── Logotipo ──────────────────────────────────────────────────────────────
   `img/logo-lyheroc.jpg` es un JPEG de 392×128 con el fondo BLANCO. En claro
   se funde con el panel blanco; en oscuro un JPEG no puede volverse
   transparente, así que va dentro de un recuadro blanco con radio y un poco
   de aire (`--fondo-logo`): un rectángulo blanco a propósito y no uno cortado.
   El ancho manda y el alto sale de la proporción; el `aspect-ratio` reserva el
   hueco antes de que llegue la imagen, para que el menú no salte. */
.marca-logo {
  display: block; width: 100%; max-width: 168px; height: auto;
  aspect-ratio: 392 / 128;
  background: var(--fondo-logo); border-radius: var(--radius-sm);
}
.marca-sub {
  display: block; font-size: var(--txt-2xs); letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-3); margin-top: 6px;
}
.login-logo { display: flex; flex-direction: column; align-items: center; margin-bottom: 24px; }
.login-logo .marca-logo { max-width: 220px; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .marca-logo { padding: 4px 8px; box-sizing: content-box; max-width: 152px; }
  :root:not([data-theme="light"]) .login-logo .marca-logo { max-width: 204px; }
}
:root[data-theme="dark"] .marca-logo { padding: 4px 8px; box-sizing: content-box; max-width: 152px; }
:root[data-theme="dark"] .login-logo .marca-logo { max-width: 204px; }

/* ── Selector de tema (pie del menú) ───────────────────────────────────────
   Claro / oscuro / sistema. Lo conecta `tema.js`, que también aplica el tema
   guardado ANTES de pintar (se carga síncrono en el <head>). El botón activo
   lleva `aria-pressed="true"`: el estado vive en el atributo y la hoja solo
   lo lee, así un lector de pantalla y la vista dicen lo mismo. */
.tema-sel {
  display: flex; gap: 2px; padding: 2px;
  background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.tema-sel .tema-btn {
  flex: 1; justify-content: center; padding: 4px 0; min-height: 26px;
  background: none; border: 0; border-radius: 4px; color: var(--ink-3);
}
.tema-sel .tema-btn:hover { color: var(--ink); }
.tema-sel .tema-btn[aria-pressed="true"] {
  background: var(--panel); color: var(--accent-ink); box-shadow: var(--shadow);
}
.app-nav.collapsed .tema-sel { flex-direction: column; width: 100%; }

/* ── Cifras alineadas ──────────────────────────────────────────────────────
   Todo lo que va en `--mono` es una cifra o un código que se compara por
   dígito. Con Jost las figuras tabulares venían de la fuente; con la pila de
   sistema hay que pedirlas, y se piden aquí, en un solo sitio. */
table.data td.num, .num { font-variant-numeric: tabular-nums; }

/* ── Selector de empresa ─────────────────────────────────────────────────── */
.nav-empresa { padding: 6px 18px 10px; }
.nav-empresa label {
  display: block; font-size: var(--txt-2xs); text-transform: uppercase;
  letter-spacing: .13em; color: var(--ink-4); margin-bottom: 4px;
}
.nav-empresa select { width: 100%; }
.nav-empresa .empresa-fija { font-weight: 600; color: var(--ink); }
.app-nav.collapsed .nav-empresa { display: none; }

/* Campana de avisos (js/campana.js)
   ─────────────────────────────────────────────────────────────────────────
   Va en la barra lateral (no hay barra superior). El panel es `fixed` y se
   anexa al body: dentro del menú, su `overflow-y: auto` lo recortaría. */
.nav-campana { padding: 0 0 4px; }
.campana-btn {
  display: flex; align-items: center; gap: 9px; width: 100%;
  padding: 8px 18px; border: 0; border-left: 3px solid transparent; border-radius: 0;
  background: none; color: var(--ink-2); font-size: var(--txt-md); font-weight: 500;
  position: relative; min-height: 0;
}
.campana-btn:hover { background: var(--panel-2); color: var(--ink); }
.campana-btn[aria-expanded="true"] { background: var(--accent-soft); color: var(--accent-ink); border-left-color: var(--accent); }
.campana-cuenta {
  margin-left: auto; min-width: 20px; height: 20px; padding: 0 6px; border-radius: 999px;
  background: var(--bad); color: var(--on-accent); font-size: var(--txt-2xs); font-weight: 700;
  display: inline-flex; align-items: center; justify-content: center; font-variant-numeric: tabular-nums;
}
.campana-cuenta[hidden] { display: none; }
.app-nav.collapsed .campana-btn { justify-content: center; padding: 8px 0; border-left-width: 0; }
.app-nav.collapsed .campana-cuenta { position: absolute; top: 2px; right: 6px; margin: 0; }
.campana-panel {
  position: fixed; z-index: 950; max-height: min(480px, calc(100vh - 16px));
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--panel); border: 1px solid var(--border-2); border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
}
.campana-panel[hidden] { display: none; }
.campana-cab, .campana-pie {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 10px 12px; flex-shrink: 0;
}
.campana-cab { border-bottom: 1px solid var(--border); }
.campana-pie { border-top: 1px solid var(--border); font-size: var(--txt-sm); }
.campana-lista { list-style: none; margin: 0; padding: 0; overflow-y: auto; flex: 1; }
.campana-vacia { padding: 24px 14px; color: var(--ink-3); text-align: center; }
.aviso-item { display: flex; align-items: stretch; border-bottom: 1px solid var(--border); border-left: 3px solid transparent; }
.aviso-item.no-leido { background: var(--accent-soft); }
.aviso-item.urgente { border-left-color: var(--bad); }
.aviso-abrir {
  flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  padding: 10px 12px; border: 0; border-radius: 0; background: none; color: var(--ink);
  text-align: left; white-space: normal; font-weight: 400; min-height: 0;
}
.aviso-abrir:hover { background: var(--panel-2); }
.aviso-titulo { font-weight: 600; font-size: var(--txt-sm); }
.aviso-item:not(.no-leido) .aviso-titulo { font-weight: 500; }
.aviso-cuerpo { color: var(--ink-2); font-size: var(--txt-sm); }
.aviso-meta { color: var(--ink-3); font-size: var(--txt-xs); }
.aviso-leido { border: 0; background: none; color: var(--ink-3); padding: 0 12px; border-radius: 0; min-height: 0; }
.aviso-leido:hover { color: var(--good-ink); background: var(--panel-2); }
tr.aviso-fila-nueva td { background: var(--accent-soft); }
.aviso-urgente-txt { color: var(--bad-ink); font-size: var(--txt-xs); font-weight: 600; }
.envio-estado { font-weight: 600; }
.envio-estado.enviado { color: var(--good-ink); }
.envio-estado.fallido { color: var(--bad-ink); }
.envio-estado.sin_smtp, .envio-estado.sin_credencial, .envio-estado.sin_destino { color: var(--warn-ink); }
.envio-estado.pendiente, .envio-estado.en_resumen { color: var(--info-ink); }
.envios-config .alert { margin-bottom: 8px; }
@media (max-width: 900px) {
  .nav-campana { padding: 0; order: 2; }
  .campana-btn { padding: 8px 12px; border-left: 0; width: auto; }
}

/* Bitácora de la OS (cajón de Órdenes): un evento por renglón, fecha y quién en gris. */
.tb-hist { list-style: none; margin: 0; padding: 0; }
.tb-hist li {
  display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap;
  padding: 5px 0; border-bottom: 1px solid var(--border);
  font-size: var(--txt-sm);
}
.tb-hist li:last-child { border-bottom: none; }
.tb-hist-fecha { color: var(--ink-3); font-size: var(--txt-2xs); margin-left: auto; }
.tb-hist-quien { color: var(--ink-3); font-size: var(--txt-2xs); width: 100%; }

/* Los campos de la barra de periodo (fechas, buscador) se ven como los de `.filters`. */
.ts-periodo input:not([type="checkbox"]):not([type="radio"]),.ts-periodo select { min-height: 34px; padding: 6px 10px; border: 1px solid var(--border-2); border-radius: var(--radius-sm); background: var(--input-bg); color: var(--ink); }
.ts-periodo input::placeholder { color: var(--ink-4); }

/* ══════════════════════════════════════════════════════════════════════════
   Formularios configurables (modules/formularios.js, formularios_*.js)
   El constructor en tres columnas: estructura · propiedades · teléfono. Por
   debajo de 1280 px la vista previa baja; por debajo de 900, todo apila.
   ══════════════════════════════════════════════════════════════════════════ */
.fx-volver { font-size: var(--txt-sm); color: var(--ink-3); }
.fx-volver:hover { color: var(--accent-ink); }
.fx-meta { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; font-size: var(--txt-sm); color: var(--ink-2); margin-top: 4px; }
.fx-pestanas { display: flex; gap: 2px; border-bottom: 1px solid var(--border); margin-bottom: 14px; flex-wrap: wrap; }
.fx-pestanas button { background: none; border: 0; border-bottom: 2px solid transparent; border-radius: 0;
  padding: 8px 14px; color: var(--ink-3); font-weight: 600; font-size: var(--txt-md); margin-bottom: -1px; }
.fx-pestanas button:hover { color: var(--ink); }
.fx-pestanas button.on { color: var(--accent-ink); border-bottom-color: var(--accent); }
.fx-aviso-version { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; padding: 8px 12px;
  background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius); margin-bottom: 10px; font-size: var(--txt-md); }

.fx-barra { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 8px; font-size: var(--txt-md); }
.fx-guardado, .fx-contador { font-size: var(--txt-sm); color: var(--ink-3); }
.fx-guardado.ok, .fx-contador.ok { color: var(--good-ink); }
.fx-guardado.pend { color: var(--warn-ink); }
.fx-guardado.mal, .fx-contador.mal { color: var(--bad-ink); font-weight: 600; }
.fx-lista-errores { background: var(--bad-soft); border: 1px solid var(--bad); border-radius: var(--radius); padding: 8px 12px; margin-bottom: 10px; color: var(--bad-ink); font-size: var(--txt-md); }
.fx-lista-errores summary { cursor: pointer; font-weight: 600; }
.fx-lista-errores ul { margin: 6px 0 0; padding-left: 18px; }
.fx-enlace { background: none; border: 0; padding: 0; color: inherit; font-weight: 400; text-align: left; white-space: normal; text-decoration: underline; cursor: pointer; }

.fx-cuerpo { display: grid; grid-template-columns: minmax(230px, 290px) minmax(0, 1fr) 400px; gap: 14px; align-items: start; }
.fx-cuerpo.sin-previa { grid-template-columns: minmax(230px, 290px) minmax(0, 1fr); }
.fx-cuerpo.sin-previa .fx-previa { display: none; }
@media (max-width: 1280px) {
  .fx-cuerpo { grid-template-columns: minmax(220px, 270px) minmax(0, 1fr); }
  .fx-cuerpo .fx-previa { grid-column: 1 / -1; }
}
@media (max-width: 900px) { .fx-cuerpo, .fx-cuerpo.sin-previa { grid-template-columns: 1fr; } }

.fx-arbol { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); padding: 8px;
  display: flex; flex-direction: column; gap: 6px; position: sticky; top: 12px; max-height: calc(100vh - 24px); overflow-y: auto; }
.fx-grupo { display: flex; flex-direction: column; gap: 2px; padding-bottom: 4px; border-bottom: 1px solid var(--border); }
.fx-nodo { display: flex; align-items: center; gap: 6px; padding: 6px 8px; border-radius: var(--radius-sm);
  cursor: pointer; font-size: var(--txt-md); color: var(--ink-2); border: 1px solid transparent; background: none; text-align: left; width: 100%; font-weight: 400; }
.fx-nodo:hover { background: var(--panel-2); }
.fx-nodo.sel { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-ink); }
.fx-nodo.con-error { box-shadow: inset 3px 0 0 var(--bad); }
.fx-nodo.arrastrando { opacity: .45; }
.fx-nodo.soltar-antes { box-shadow: inset 0 2px 0 var(--focus); }
.fx-nodo.soltar-despues { box-shadow: inset 0 -2px 0 var(--focus); }
.fx-nodo-config { font-weight: 600; }
.fx-nodo-sec { background: var(--panel-2); }
.fx-nodo-campo { padding-left: 14px; }
.fx-nodo-txt { flex: 1; min-width: 0; display: flex; gap: 6px; align-items: center; flex-wrap: wrap; overflow-wrap: anywhere; }
.fx-asa { cursor: grab; color: var(--ink-4); font-size: var(--txt-xs); letter-spacing: -2px; user-select: none; }
.fx-flechas { display: inline-flex; gap: 2px; opacity: .35; }
.fx-nodo:hover .fx-flechas, .fx-nodo.sel .fx-flechas, .fx-nodo:focus-within .fx-flechas { opacity: 1; }
.fx-flechas button { background: none; border: 1px solid transparent; padding: 0 5px; min-height: 22px; font-size: var(--txt-sm); color: var(--ink-3); border-radius: var(--radius-sm); }
.fx-flechas button:hover { border-color: var(--border-2); color: var(--ink); }
.fx-flechas button.quitar:hover { color: var(--bad-ink); border-color: var(--bad); }
.fx-agregar { background: none; border: 1px dashed var(--border-2); color: var(--ink-3); padding: 4px 8px; margin: 4px 0 0 14px;
  border-radius: var(--radius-sm); font-size: var(--txt-sm); font-weight: 600; }
.fx-agregar:hover { color: var(--accent-ink); border-color: var(--accent); }
.fx-agregar-sec { align-self: flex-start; }
.fx-ico { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; flex-shrink: 0;
  border-radius: var(--radius-sm); background: var(--panel-2); color: var(--accent-ink); }
.fx-req { color: var(--bad-ink); font-weight: 700; }

.fx-props { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); padding: 14px 16px; min-width: 0; }
.fx-props-caja { border: 0; padding: 0; margin: 0; min-width: 0; }
.fx-props-titulo { display: flex; align-items: center; gap: 6px; font-size: var(--txt-lg); color: var(--titulo); margin-bottom: 4px; }
.fx-sub { font-size: var(--txt-2xs); text-transform: uppercase; letter-spacing: .08em; color: var(--ink-3); font-weight: 700;
  margin: 16px 0 8px; padding-top: 10px; border-top: 1px solid var(--border); }
.fx-ayuda { font-size: var(--txt-xs); color: var(--ink-3); line-height: 1.45; margin: 3px 0 8px; }
.fx-vacio { color: var(--ink-3); }
.fx-props-errores:empty { display: none; }
.fx-props-errores { margin-bottom: 10px; }
.fx-error { background: var(--bad-soft); color: var(--bad-ink); border-left: 3px solid var(--bad); padding: 5px 9px; border-radius: var(--radius-sm); font-size: var(--txt-sm); margin-bottom: 4px; }
.fx-casilla { display: flex; align-items: center; gap: 8px; font-size: var(--txt-md); color: var(--ink-2); margin: 6px 0; cursor: pointer; }
.fx-casilla input { accent-color: var(--primario); }
.fx-radios { display: flex; flex-direction: column; gap: 2px; }
.fx-props .field input:not([type="checkbox"]):not([type="radio"]), .fx-props .field select { min-height: 34px; }
.fx-tipos { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 8px; margin: 10px 0 12px; }
.fx-tipo { display: flex; gap: 10px; align-items: flex-start; text-align: left; white-space: normal; background: var(--panel);
  border: 1px solid var(--border); border-radius: var(--radius); padding: 10px; font-weight: 400; color: var(--ink-2); }
.fx-tipo:hover { border-color: var(--accent); background: var(--accent-soft); }
.fx-tipo b { display: block; color: var(--ink); font-size: var(--txt-md); }
.fx-tipo small { display: block; color: var(--ink-3); font-size: var(--txt-xs); line-height: 1.4; margin-top: 2px; }
.fx-opciones { display: flex; flex-direction: column; gap: 6px; }
.fx-opcion { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) auto auto auto; gap: 4px; align-items: center; }
.fx-opcion input { min-height: 32px; padding: 5px 8px; border: 1px solid var(--border-2); border-radius: var(--radius-sm); background: var(--input-bg); color: var(--ink); width: 100%; }
.fx-opcion-valor { color: var(--ink-3) !important; font-size: var(--txt-sm); }
.fx-condicion { display: flex; flex-direction: column; gap: 6px; }
.fx-modo { font-size: var(--txt-md); color: var(--ink-2); display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.fx-cond-fila { display: flex; gap: 4px; align-items: center; flex-wrap: wrap; padding: 6px; background: var(--panel-2); border-radius: var(--radius-sm); }
.fx-cond-fila select, .fx-cond-fila input:not([type="checkbox"]), .fx-modo select { min-height: 32px; padding: 4px 8px; border: 1px solid var(--border-2);
  border-radius: var(--radius-sm); background: var(--input-bg); color: var(--ink); max-width: 100%; }
.fx-cond-fila > select:first-child { flex: 1 1 160px; }
.fx-cond-propio { font-size: var(--txt-md); color: var(--ink-2); padding: 0 4px; }
.fx-cond-en { display: flex; flex-wrap: wrap; gap: 0 12px; }
.fx-codigo { display: block; background: var(--panel-2); padding: 6px; border-radius: var(--radius-sm); font-size: var(--txt-sm); overflow-wrap: anywhere; }
.fx-interruptor { display: inline-flex; align-items: center; gap: 6px; font-size: var(--txt-sm); cursor: pointer; }
.fx-interruptor input { accent-color: var(--primario); }

/* ── Vista previa de teléfono ── */
.fx-previa { position: sticky; top: 12px; display: flex; flex-direction: column; align-items: center; gap: 6px; }
.fx-previa.solo { position: static; }
.fx-previa-cab { font-size: var(--txt-2xs); text-transform: uppercase; letter-spacing: .08em; color: var(--ink-3); font-weight: 600; }
.fx-tel { width: 375px; max-width: 100%; height: min(760px, calc(100vh - 60px)); display: flex; flex-direction: column;
  border: 9px solid var(--border-2); border-radius: 34px; background: var(--bg); overflow: hidden; box-shadow: var(--shadow-lg); }
.fx-tel-barra { display: flex; align-items: center; gap: 8px; padding: 10px 14px; background: var(--primario); color: var(--on-accent); font-size: var(--txt-md); }
.fx-tel-barra b { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fx-tel-barra span { font-size: var(--txt-xs); opacity: .85; }
.fx-tel-reiniciar { background: none; border: 1px solid currentColor; color: inherit; padding: 2px 8px; min-height: 0; font-size: var(--txt-xs); border-radius: 999px; font-weight: 600; }
.fx-tel-pantalla { flex: 1; overflow-y: auto; padding: 10px; display: flex; flex-direction: column; gap: 10px; }
.fx-tel-sec { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 12px; }
.fx-tel-sec h4 { font-size: var(--txt-lg); color: var(--titulo); margin-bottom: 6px; }
.fx-tel-campo { padding: 8px 0; border-top: 1px solid var(--border); display: flex; flex-direction: column; gap: 5px; }
.fx-tel-campo:first-of-type { border-top: 0; }
.fx-tel-campo > label { font-size: var(--txt-md); font-weight: 600; color: var(--ink); }
.fx-tel-campo input:not([type="checkbox"]), .fx-tel-campo select, .fx-tel-campo textarea {
  width: 100%; min-height: 38px; padding: 6px 10px; border: 1px solid var(--border-2); border-radius: var(--radius-sm); background: var(--input-bg); color: var(--ink); }
.fx-tel-ayuda { font-size: var(--txt-xs); color: var(--ink-3); }
.fx-tel-nota { font-size: var(--txt-xs); color: var(--info-ink); background: var(--info-soft); padding: 6px 8px; border-radius: var(--radius-sm); }
.fx-tel-nota.bad { color: var(--bad-ink); background: var(--bad-soft); }
.fx-tel-rep { border: 1px dashed var(--border-2); border-radius: var(--radius-sm); padding: 6px 8px; margin-top: 6px; }
.fx-tel-rep-cab { display: flex; align-items: center; justify-content: space-between; font-size: var(--txt-sm); color: var(--ink-2); }
.fx-tel-agregar { margin-top: 8px; }
.fx-tel-pie { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 4px 2px 8px; }
.fx-tel-faltan { font-size: var(--txt-sm); font-weight: 600; }
.fx-tel-faltan.pend { color: var(--warn-ink); }
.fx-tel-faltan.ok { color: var(--good-ink); }
.fx-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.fx-chip { border: 1px solid var(--border-2); background: var(--panel); color: var(--ink-2); padding: 6px 12px; border-radius: 999px;
  font-size: var(--txt-sm); font-weight: 600; min-height: 34px; }
.fx-chip.on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-ink); }
.fx-chip.on.good { background: var(--good-soft); border-color: var(--good); color: var(--good-ink); }
.fx-chip.on.warn { background: var(--warn-soft); border-color: var(--warn); color: var(--warn-ink); }
.fx-chip.on.bad { background: var(--bad-soft); border-color: var(--bad); color: var(--bad-ink); }
.fx-fotos { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.fx-foto { width: 64px; height: 64px; justify-content: center; border: 1px solid var(--border-2); background: var(--panel-2); color: var(--ink-3);
  border-radius: var(--radius-sm); font-size: var(--txt-2xs); padding: 0; white-space: normal; text-align: center; }
.fx-foto.nueva { border-style: dashed; color: var(--accent-ink); }
.fx-fotos .fx-tel-ayuda { width: 100%; }
.fx-firma { width: 100%; height: 110px; background: var(--panel-2); border: 1px dashed var(--border-2); border-radius: var(--radius-sm); color: var(--ink); touch-action: none; cursor: crosshair; }
.fx-con-unidad { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.fx-con-unidad input { flex: 1 1 120px; width: auto !important; }
.fx-con-unidad > span { font-size: var(--txt-sm); color: var(--ink-3); }

/* ── Detalle de una respuesta (cajón) ── */
.rs-preguntas { display: flex; flex-direction: column; }
.rs-pregunta { padding: 8px 0; border-top: 1px solid var(--border); display: flex; flex-direction: column; gap: 4px; }
.rs-pregunta:first-child { border-top: 0; }
.rs-pregunta.con-aviso { box-shadow: inset 3px 0 0 var(--bad); padding-left: 8px; }
.rs-etiqueta { font-size: var(--txt-sm); color: var(--ink-3); display: flex; justify-content: space-between; gap: 8px; }
.rs-seg { font-variant-numeric: tabular-nums; color: var(--ink-4); font-size: var(--txt-xs); }
.rs-texto { color: var(--ink); white-space: pre-wrap; overflow-wrap: anywhere; }
.rs-nota { color: var(--ink-2); white-space: pre-wrap; margin-top: 4px; font-size: var(--txt-md); }
.rs-aviso { color: var(--bad-ink); font-size: var(--txt-sm); }
.rs-avisos { margin: 0; padding-left: 18px; color: var(--bad-ink); font-size: var(--txt-md); }
.rs-avisos li { margin-bottom: 3px; }
.rs-fotos { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
.rs-mini { font-size: var(--txt-sm); }
.rs-mini img { display: block; aspect-ratio: 4 / 3; object-fit: cover; border-radius: var(--radius-sm); border: 1px solid var(--border); background: var(--panel-2); }

/* `hidden` tiene que ganarle a un `display` de clase: la vista previa oculta
   campos por condición y la bandeja oculta insignias con ese atributo. */
.fx-tel [hidden], .status-badge[hidden], .fx-cuerpo [hidden] { display: none !important; }
/* Una casilla o un radio dentro de un `.field` no es un rótulo: sin esto
   heredaba la versalita del rótulo y el ancho completo del campo. */
.field .fx-casilla { display: flex; text-transform: none; letter-spacing: normal; font-size: var(--txt-md);
  font-weight: 400; color: var(--ink-2); margin: 4px 0; }
.field .fx-casilla input { width: auto; min-height: 0; box-shadow: none; }

/* ── Mapa (js/mapa.js, Leaflet local; docs/diseno-geolocalizacion.md) ── */
.mapa { height: 340px; width: 100%; border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--panel-2); overflow: hidden; z-index: 0; }
.mapa.chico { height: 240px; }
.mapa.grande { height: 440px; }
.mapa .leaflet-tile-pane { filter: var(--mapa-filtro); }
.mapa .leaflet-container, .mapa.leaflet-container { font-family: var(--display); font-size: var(--txt-sm); }
.mapa .leaflet-popup-content-wrapper, .mapa .leaflet-popup-tip { background: var(--panel); color: var(--ink);
  box-shadow: var(--shadow-lg); }
.mapa .leaflet-popup-content { margin: 10px 12px; line-height: 1.45; }
.mapa .leaflet-control-attribution { background: var(--panel); color: var(--ink-3); font-size: var(--txt-2xs); }
.mapa .leaflet-control-attribution a { color: var(--accent-ink); }
.mapa .leaflet-bar a { background: var(--panel); color: var(--ink); border-bottom-color: var(--border); }
.mapa-pie { display: flex; flex-wrap: wrap; gap: 6px 14px; align-items: center; margin-top: 6px;
  font-size: var(--txt-xs); color: var(--ink-3); }
.mapa-punto { display: inline-block; width: 10px; height: 10px; border-radius: 50%; margin-right: 4px;
  vertical-align: middle; border: 1px solid var(--punto-borde); }
.mapa-punto.bad { background: var(--bad); }
.mapa-punto.warn { background: var(--warn); }
.mapa-punto.good { background: var(--good); }
.mapa-punto.info { background: var(--info); }
.mapa-punto.muted { background: var(--ink-4); }
.ubic-linea { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.ubic-alerta { color: var(--bad-ink); font-weight: 600; }
.exc-tarjeta { border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 12px; margin-top: 8px;
  background: var(--panel); }
.exc-tarjeta.pendiente { box-shadow: inset 3px 0 0 var(--warn); }
.exc-tarjeta.autorizada { box-shadow: inset 3px 0 0 var(--good); }
.exc-tarjeta.rechazada { box-shadow: inset 3px 0 0 var(--bad); }

/* ── Liga segura de autorización (autorizar.html, js/autorizar.js) ──
   Página pública, sin menú: una tarjeta centrada que cabe en un teléfono. */
.liga-cuerpo { background: var(--bg); }
.liga-wrap { max-width: 860px; margin: 0 auto; padding: 20px 16px 40px; }
.liga-cab { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 14px; }
.liga-cab .marca-logo { height: 40px; width: auto; }
.liga-card { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-lg);
  box-shadow: var(--shadow); padding: 20px; }
.liga-card h1 { font-family: var(--display); font-size: var(--txt-2xl); color: var(--titulo); margin: 0 0 10px;
  font-weight: 600; }
.liga-card h2 { font-family: var(--display); font-size: var(--txt-lg); color: var(--ink); margin: 18px 0 8px; }
.liga-card .table-wrap { max-height: none; }
.liga-datos { margin-bottom: 12px; }
.liga-fotos { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 8px; }
.liga-fotos img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--panel-2); }
.liga-form { border-top: 1px solid var(--border); margin-top: 18px; padding-top: 4px; }
.liga-botones { display: flex; gap: 10px; justify-content: flex-end; flex-wrap: wrap; margin: 8px 0; }
.liga-pie { color: var(--ink-3); font-size: var(--txt-xs); margin-top: 14px; text-align: center; }
.liga-copiar { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.liga-copiar input { flex: 1 1 260px; font-family: var(--mono); font-size: var(--txt-xs); }
/* Pestañas de una bandeja (Integridad): el segmento de la barra de periodo. */
.bandeja-tabs { margin: 4px 0 12px; }
.liga-card .muted, .liga-wrap .muted { color: var(--ink-3); }
.liga-foto-falta { display: flex; align-items: center; justify-content: center; aspect-ratio: 4 / 3;
  border: 1px dashed var(--border-2); border-radius: var(--radius-sm); color: var(--ink-3);
  font-size: var(--txt-xs); background: var(--panel-2); }

/* ── Preventivo: calendario del mes (modules/preventivo.js) ─────────────── */
.cal-mes { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 4px; }
.cal-mes .cal-cab { font-size: var(--txt-2xs); color: var(--ink-3); text-transform: uppercase;
  letter-spacing: .06em; text-align: center; padding: 4px 0; }
.cal-dia { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-sm);
  min-height: 92px; padding: 4px; display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.cal-dia.vacio { background: transparent; border-style: dashed; }
.cal-dia.hoy { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.cal-num { font-size: var(--txt-xs); color: var(--ink-3); }
.cal-ev { font-size: var(--txt-2xs); border-radius: 4px; padding: 2px 4px; text-align: left;
  border: 0; border-left: 3px solid var(--ink-3); background: var(--panel-2); color: var(--ink-2);
  cursor: pointer; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-height: 0; }
.cal-ev.vencido { border-left-color: var(--bad); background: var(--bad-soft); color: var(--bad-ink); }
.cal-ev.proximo { border-left-color: var(--warn); background: var(--warn-soft); color: var(--warn-ink); }
.cal-ev.al_dia { border-left-color: var(--good); background: var(--good-soft); color: var(--good-ink); }
.cal-ev.estimada { border-left-style: dashed; }
.cal-ev.repeticion { opacity: .72; }
@media (max-width: 760px) {
  .cal-mes { grid-template-columns: minmax(0, 1fr); }
  .cal-mes .cal-cab, .cal-dia.vacio, .cal-dia.sin-eventos { display: none; }
}

/* ── Lecturas de la unidad: gráfica SVG (preventivo_ui.js) ───────────────── */
.graf-lecturas { width: 100%; height: auto; max-height: 260px; display: block; }
.graf-lecturas .eje { stroke: var(--border-2); stroke-width: 1; }
.graf-lecturas .rejilla { stroke: var(--border); stroke-width: 1; stroke-dasharray: 2 3; }
.graf-lecturas .linea { fill: none; stroke: var(--accent); stroke-width: 1.5; }
.graf-lecturas .pt { fill: var(--accent); stroke: var(--panel); stroke-width: 1; }
.graf-lecturas .pt.sin-verificar { fill: var(--panel); stroke: var(--ink-4); stroke-width: 1.5; }
.graf-lecturas .pt.rechazada { fill: var(--panel); stroke: var(--bad); stroke-width: 1.5; }
.graf-lecturas .pt.imposible { fill: var(--bad); stroke: var(--bad-ink); }
.graf-lecturas .aro-imposible { fill: none; stroke: var(--bad); stroke-width: 1.5; }
.graf-lecturas text { fill: var(--ink-3); font-size: var(--txt-2xs); }

/* ══════════════════════════════════════════════════════════════════════════
   Indicadores de mantenimiento (módulo 12): Pareto de fallas, comparación y
   edad de las abiertas. Un solo eje (0–100 %): la barra es el % de cada
   sistema y la línea el acumulado. Colores solo de los tokens.
   ══════════════════════════════════════════════════════════════════════════ */
.stat-card .kpi-delta { font-size: var(--txt-2xs); margin-top: 2px; font-variant-numeric: tabular-nums; }
.pareto-caja { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 460px), 1fr));
  gap: 16px; align-items: start; }
.pareto-grafica { position: relative; background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 8px 8px 4px; }
.graf-pareto { width: 100%; height: auto; max-height: 280px; display: block; }
.graf-pareto .eje { stroke: var(--border-2); stroke-width: 1; }
.graf-pareto .rejilla { stroke: var(--border); stroke-width: 1; }
.graf-pareto .corte80 { stroke: var(--ink-3); stroke-width: 1; stroke-dasharray: 4 3; }
.graf-pareto .barra { fill: var(--ink-4); }
.graf-pareto .barra.dentro { fill: var(--accent); }
.graf-pareto .acumulado { fill: none; stroke: var(--naranja); stroke-width: 2;
  stroke-linejoin: round; stroke-linecap: round; }
.graf-pareto .punto { fill: var(--naranja); stroke: var(--panel); stroke-width: 2; }
.graf-pareto .zona { fill: transparent; cursor: default; }
.graf-pareto .zona:hover { fill: var(--panel-2); fill-opacity: .35; }
.graf-pareto text { fill: var(--ink-3); font-size: var(--txt-2xs); }
.graf-pareto text.valor { fill: var(--ink-2); font-weight: 600; }
.graf-pareto text.etq-corte { fill: var(--ink-2); }
.pareto-globo { position: absolute; top: 8px; right: 12px; pointer-events: none;
  background: var(--panel); border: 1px solid var(--border-2); border-radius: var(--radius-sm);
  box-shadow: var(--shadow); padding: 6px 8px; font-size: var(--txt-xs); color: var(--ink);
  opacity: 0; transition: opacity .1s; white-space: nowrap; }
.pareto-globo.visible { opacity: 1; }
.leyenda-pareto { display: flex; flex-wrap: wrap; gap: 4px 14px; font-size: var(--txt-xs);
  color: var(--ink-3); margin: 4px 4px 2px; }
.leyenda-pareto i { display: inline-block; width: 10px; height: 10px; border-radius: 2px;
  background: var(--ink-4); margin-right: 5px; vertical-align: -1px; }
.leyenda-pareto i.dentro { background: var(--accent); }
.leyenda-pareto i.linea { height: 2px; width: 14px; border-radius: 1px; background: var(--naranja);
  vertical-align: 3px; }
.barras-edad { display: grid; grid-template-columns: minmax(120px, max-content) minmax(0, 1fr) 3.5em;
  gap: 6px 12px; align-items: center; max-width: 640px; font-size: var(--txt-sm); }
.barras-edad .num { text-align: right; font-weight: 600; }
.barra-pista { background: var(--panel-2); border-radius: 4px; height: 10px; }
.barra-relleno { background: var(--accent); border-radius: 4px; height: 10px; }
.como-se-calcula { margin: 24px 0 12px; background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 10px 14px; }
.como-se-calcula summary { cursor: pointer; font-weight: 600; color: var(--ink-2); }
.como-se-calcula dl { display: grid; grid-template-columns: minmax(160px, max-content) minmax(0, 1fr);
  gap: 6px 16px; margin: 10px 0 0; font-size: var(--txt-sm); }
.como-se-calcula dt { font-weight: 600; color: var(--ink); }
.como-se-calcula dd { margin: 0; color: var(--ink-2); }
@media (max-width: 640px) { .como-se-calcula dl { grid-template-columns: minmax(0, 1fr); } }
.leyenda-lecturas { display: flex; flex-wrap: wrap; gap: 4px 14px; font-size: var(--txt-xs);
  color: var(--ink-3); margin: 4px 0 8px; }
.leyenda-lecturas i { display: inline-block; width: 9px; height: 9px; border-radius: 50%;
  margin-right: 5px; vertical-align: -1px; background: var(--accent); }
.leyenda-lecturas i.sin-verificar { background: var(--panel); border: 1.5px solid var(--ink-4); }
.leyenda-lecturas i.imposible { background: var(--bad); }
.leyenda-lecturas i.rechazada { background: var(--panel); border: 1.5px solid var(--bad); }

/* ── Solicitudes de servicio (modules/solicitudes.js, solicitar.js) ─────── */
.sol-linea { list-style: none; margin: 0; padding: 0 0 0 14px; border-left: 2px solid var(--border-2); }
.sol-linea li { position: relative; margin: 0 0 10px; padding-left: 4px; font-size: var(--txt-sm); }
.sol-linea li::before { content: ""; position: absolute; left: -21px; top: 4px; width: 10px; height: 10px;
  border-radius: 50%; background: var(--panel); border: 2px solid var(--ink-4); }
.sol-linea li.actual::before { background: var(--accent); border-color: var(--accent); }
.sol-fotos { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 8px; }
.sol-fotos img { width: 100%; height: 96px; object-fit: cover; border-radius: var(--radius-sm);
  border: 1px solid var(--border); display: block; }
.solicitar-form .field { margin-bottom: 12px; }
.solicitar-form textarea, .solicitar-form select, .solicitar-form input[type="text"],
.solicitar-form input[type="tel"] { width: 100%; }
/* El campo trampa: fuera de la vista y del tabulador (un robot lo llena). */
.solicitar-trampa { position: absolute; left: -10000px; top: auto; width: 1px; height: 1px; overflow: hidden; }

/* La etiqueta del QR de la unidad (unidades.js): se ve en un diálogo y se
   imprime sola (`body.imprimiendo-etiqueta`). Tinta negra sobre blanco en los
   dos temas: es papel. */
.etiqueta-qr { width: 9cm; margin: 0 auto; padding: 0.5cm; border: 1px dashed var(--papel-corte);
  background: var(--papel); color: var(--papel-tinta); text-align: center; font-family: var(--display); }
.etiqueta-qr .eq-logo { height: 1.1cm; width: auto; display: block; margin: 0 auto 0.2cm; }
.etiqueta-qr .eq-titulo { font-size: 13pt; font-weight: 700; margin: 0 0 0.15cm; }
.etiqueta-qr .eq-qr { width: 6cm; height: 6cm; display: block; margin: 0.15cm auto; image-rendering: pixelated; }
.etiqueta-qr .eq-eco { font-size: 22pt; font-weight: 800; letter-spacing: .02em; margin: 0.1cm 0 0; }
.etiqueta-qr .eq-dato { font-size: 10pt; margin: 0.05cm 0; }
.etiqueta-qr .eq-pie { font-size: 8pt; margin-top: 0.2cm; }
@media print {
  body.imprimiendo-etiqueta > *:not(.modal-overlay) { display: none !important; }
  body.imprimiendo-etiqueta .modal-overlay { position: static; background: none; display: block; }
  body.imprimiendo-etiqueta .modal { box-shadow: none; border: 0; width: auto; max-width: none;
    max-height: none; overflow: visible; background: var(--papel); }
  body.imprimiendo-etiqueta .modal-header, body.imprimiendo-etiqueta .modal-footer,
  body.imprimiendo-etiqueta .no-imprimir { display: none !important; }
  body.imprimiendo-etiqueta .etiqueta-qr { border: 1px dashed var(--papel-corte); }
}

/* ── Firma de conformidad (js/firma_pad.js; cajón de la OS y firmar.html) ──
   El lienzo es siempre papel blanco con tinta negra: la firma se lee igual
   impresa, en el informe y en modo oscuro. `touch-action: none` deja que el
   dedo dibuje en vez de mover la página. */
.firma-pad { border: 1px solid var(--border-2); border-radius: var(--radius-sm); overflow: hidden;
  background: var(--papel); }
.firma-lienzo { display: block; width: 100%; touch-action: none; cursor: crosshair; background: var(--papel); }
.firma-pad-pie { display: flex; justify-content: space-between; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 6px 8px; border-top: 1px dashed var(--border-2); background: var(--panel-2); }
.firma-pad-linea { color: var(--ink-3); font-size: var(--txt-xs); }
.firma-img { max-width: 260px; width: 100%; height: auto; background: var(--papel);
  border: 1px solid var(--border); border-radius: var(--radius-sm); display: block; }

/* ── Informe de la OS (informe.html, js/informe.js) ─────────────────────────
   Página imprimible: «Imprimir / Guardar como PDF» del navegador. En pantalla,
   una hoja centrada; al imprimir, papel y tinta (tokens --papel*), sin barra. */
.inf-cuerpo { background: var(--bg); }
.inf-barra { position: sticky; top: 0; z-index: 5; display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
  justify-content: space-between; padding: 10px 16px; background: var(--panel); border-bottom: 1px solid var(--border); }
.inf-barra .inf-acciones { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.inf-hoja { max-width: 900px; margin: 16px auto 40px; padding: 28px 28px 36px; background: var(--panel);
  border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--shadow); }
.inf-cab { display: flex; justify-content: space-between; gap: 16px; align-items: flex-start; flex-wrap: wrap;
  border-bottom: 2px solid var(--titulo); padding-bottom: 12px; margin-bottom: 14px; }
.inf-cab .marca-logo { height: 46px; width: auto; }
.inf-cab h1 { font-family: var(--display); font-size: var(--txt-2xl); color: var(--titulo); margin: 0; }
.inf-cab .inf-sub { color: var(--ink-3); font-size: var(--txt-sm); }
.inf-version { display: inline-block; font-size: var(--txt-2xs); text-transform: uppercase; letter-spacing: .08em;
  padding: 2px 8px; border-radius: 999px; background: var(--accent-soft); color: var(--accent-ink); }
.inf-seccion { margin-top: 18px; break-inside: avoid-page; }
.inf-seccion h2 { font-family: var(--display); font-size: var(--txt-lg); color: var(--ink); margin: 0 0 8px;
  border-bottom: 1px solid var(--border); padding-bottom: 4px; }
.inf-kv { display: grid; grid-template-columns: max-content 1fr; gap: 4px 14px; font-size: var(--txt-sm); }
.inf-kv > span:nth-child(odd) { color: var(--ink-3); }
.inf-tabla { width: 100%; border-collapse: collapse; font-size: var(--txt-sm); }
.inf-tabla th, .inf-tabla td { text-align: left; padding: 5px 6px; border-bottom: 1px solid var(--border);
  vertical-align: top; }
.inf-tabla th { color: var(--ink-3); font-weight: 600; font-size: var(--txt-xs); }
.inf-tabla td.num, .inf-tabla th.num { text-align: right; font-family: var(--mono); white-space: nowrap; }
.inf-fotos { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px; }
.inf-foto { break-inside: avoid; margin: 0; }
.inf-foto img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--panel-2); display: block; }
.inf-foto figcaption { font-size: var(--txt-2xs); color: var(--ink-3); margin-top: 2px; line-height: 1.3; }
.inf-nota { color: var(--ink-3); font-size: var(--txt-xs); }
.inf-pie { margin-top: 24px; color: var(--ink-3); font-size: var(--txt-2xs); text-align: center; }
/* Reporte mensual (reporte.js, revisión 0019): cifras del mes en recuadros. */
.rep-cifras { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px; }
.rep-cifra { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 8px 10px;
  break-inside: avoid; }
.rep-cifra b { display: block; font-size: var(--txt-xl); font-variant-numeric: tabular-nums;
  color: var(--ink); }
.rep-cifra b.sin-dato { color: var(--ink-3); }
.rep-cifra span { color: var(--ink-3); font-size: var(--txt-xs); }
.rep-aviso { border-left: 3px solid var(--warn); padding: 6px 10px; background: var(--warn-soft);
  color: var(--warn-ink); font-size: var(--txt-sm); margin-top: 12px; }
@media (max-width: 640px) {
  .inf-hoja { padding: 16px; margin: 8px; }
  .inf-kv { grid-template-columns: 1fr; }
}
@media print {
  body.inf-cuerpo { background: var(--papel); color: var(--papel-tinta); }
  body.inf-cuerpo .inf-barra, body.inf-cuerpo .no-imprimir { display: none !important; }
  body.inf-cuerpo .inf-hoja { box-shadow: none; border: 0; margin: 0; max-width: none; padding: 0;
    background: var(--papel); color: var(--papel-tinta); }
  body.inf-cuerpo .inf-cab h1, body.inf-cuerpo .inf-seccion h2 { color: var(--papel-tinta); }
  body.inf-cuerpo .inf-cab { border-bottom-color: var(--papel-tinta); }
  body.inf-cuerpo .inf-kv > span:nth-child(odd), body.inf-cuerpo .inf-nota,
  body.inf-cuerpo .inf-foto figcaption, body.inf-cuerpo .inf-tabla th { color: var(--papel-corte); }
  body.inf-cuerpo .rep-cifra { border-color: var(--papel-corte); }
  body.inf-cuerpo .rep-cifra b { color: var(--papel-tinta); }
  body.inf-cuerpo .rep-cifra span, body.inf-cuerpo .rep-cifra b.sin-dato { color: var(--papel-corte); }
  @page { margin: 14mm; }
}

/* ── Etiquetas de refacciones (refacciones_os.js, manual 6.6 paso 12) ──────
   Una por renglón recibido: folio, QR, número de parte, cliente y orden. Se
   imprimen solas con la hoja de impresión (`body.imprimiendo-etiqueta`, la
   misma regla que la etiqueta del QR de la unidad). Tinta negra sobre blanco:
   es papel. */
.etiquetas-ref { display: grid; grid-template-columns: repeat(auto-fill, minmax(6.4cm, 1fr)); gap: 0.3cm; }
.etiqueta-ref { border: 1px dashed var(--papel-corte); background: var(--papel); color: var(--papel-tinta);
  padding: 0.25cm; text-align: center; font-family: var(--display); break-inside: avoid; }
.etiqueta-ref .er-cab { display: flex; justify-content: space-between; font-size: var(--txt-sm); }
.etiqueta-ref .er-qr { width: 3.2cm; height: 3.2cm; display: block; margin: 0.1cm auto; image-rendering: pixelated; }
.etiqueta-ref .er-np { font-size: var(--txt-lg); font-weight: 800; letter-spacing: .02em; }
.etiqueta-ref .er-dato { font-size: var(--txt-xs); margin: 0.03cm 0; }
.etiqueta-ref .er-pie { font-size: var(--txt-2xs); margin-top: 0.1cm; }
@media print {
  body.imprimiendo-etiqueta .etiquetas-ref { gap: 0.2cm; }
  body.imprimiendo-etiqueta .etiqueta-ref { border: 1px dashed var(--papel-corte); }
}
/* El asistente de recepción (modules/refacciones.js): los pasos del manual. */
.pasos-rec { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 10px; }
.pasos-rec span { font-size: var(--txt-xs); padding: 3px 8px; border-radius: 999px; background: var(--panel-2);
  color: var(--ink-3); }
.pasos-rec span.on { background: var(--accent-soft); color: var(--accent-ink); font-weight: 600; }
.renglones-rec input[type="text"], .renglones-rec input[type="number"] { width: 100%; min-width: 60px; }

/* ══════════════════════════════════════════════════════════════════════════
   Flota en el tiempo (foto diaria de la flota, modules/flota.js,
   docs/diseno-foto-flota.md): una medida por gráfica (un solo eje) y el
   calendario de estatus de una unidad (el texto dice el estatus: el color
   nunca va solo). Colores solo de los tokens.
   ══════════════════════════════════════════════════════════════════════════ */
.flota-graficas { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 420px), 1fr));
  gap: 16px; margin-top: 16px; }
.flota-graf { position: relative; background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 8px 8px 4px; }
.flota-graf h4 { margin: 0 0 2px 4px; font-size: var(--txt-sm); color: var(--ink-2); }
.flota-graf .graf-ultimo { font-weight: 400; color: var(--ink-3); font-variant-numeric: tabular-nums; }
.flota-graf .graf-sub { margin: 0 0 4px 4px; font-size: var(--txt-2xs); color: var(--ink-3); }
.graf-flota { width: 100%; height: auto; max-height: 210px; display: block; }
.graf-flota .eje { stroke: var(--border-2); stroke-width: 1; }
.graf-flota .rejilla { stroke: var(--border); stroke-width: 1; }
.graf-flota .linea { fill: none; stroke: var(--accent); stroke-width: 2; stroke-linejoin: round;
  stroke-linecap: round; }
.graf-flota .punto { fill: var(--accent); stroke: var(--panel); stroke-width: 2; }
.graf-flota .punto.tenue { fill: var(--panel); stroke: var(--ink-4); stroke-width: 1.5; }
.graf-flota .barra { fill: var(--accent); }
.graf-flota .barra.tenue { fill: var(--ink-4); }
.graf-flota .guia { stroke: var(--ink-3); stroke-width: 1; stroke-dasharray: 3 3; }
.graf-flota .zona { fill: transparent; }
.graf-flota text { fill: var(--ink-3); font-size: var(--txt-2xs); font-variant-numeric: tabular-nums; }
.cal-estatus { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 3px; }
.cal-estatus .cab { font-size: var(--txt-2xs); color: var(--ink-3); text-align: center; }
.cal-estatus .dia { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 2px 4px;
  min-height: 40px; font-size: var(--txt-2xs); background: var(--panel); line-height: 1.25; }
.cal-estatus .dia .n { color: var(--ink-3); }
.cal-estatus .dia .e { display: block; font-weight: 600; }
.cal-estatus .dia .falla { display: block; color: var(--bad-ink); font-weight: 600; }
.dia.operando { background: var(--good-soft); color: var(--good-ink); border-color: transparent; }
.dia.detenida { background: var(--bad-soft); color: var(--bad-ink); border-color: transparent; }
.dia.en_taller { background: var(--warn-soft); color: var(--warn-ink); border-color: transparent; }
.dia.baja { background: var(--panel-2); color: var(--ink-3); }
.dia.sin { background: repeating-linear-gradient(135deg, var(--panel) 0 4px, var(--panel-2) 4px 8px);
  color: var(--ink-3); }
.dia.vacio { background: transparent; border-style: dashed; }
.leyenda-estatus { display: flex; flex-wrap: wrap; gap: 4px 12px; font-size: var(--txt-2xs);
  color: var(--ink-3); margin-top: 6px; }
.leyenda-estatus i { display: inline-block; width: 12px; height: 12px; min-height: 0; padding: 0;
  border-radius: 3px; margin-right: 4px; vertical-align: -2px; border: 1px solid var(--border); }

/* Editor de unidades y días de un programa (programas.js `editorUnidades`).
   Va dentro de un `.field`, cuyo estilo general pone cada input a todo lo ancho con relleno y
   cada label en bloque y en mayúsculas: las casillas de los días se encimaban con el económico.
   Aquí cada renglón es una rejilla de tres columnas y las casillas vuelven a su tamaño. */
.editor-dias .editor-dias-barra { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin: 4px 0; }
.editor-dias .editor-dias-barra input[type="text"] { width: auto; flex: 1 1 200px; }
.editor-dias-lista { max-height: 42vh; overflow: auto; border: 1px solid var(--border); border-radius: 6px; padding: 4px 8px; }
.editor-dias-fila { display: grid; grid-template-columns: minmax(140px, 1fr) auto auto; gap: 10px; align-items: center;
  padding: 6px 0; border-bottom: 1px solid var(--border); }
.editor-dias-fila:last-child { border-bottom: 0; }
.editor-dias label.unidad, .editor-dias label.dia { display: inline-flex; align-items: center; gap: 6px; margin: 0;
  font-family: inherit; font-size: var(--txt-sm); font-weight: 400; text-transform: none; letter-spacing: normal; color: var(--ink); }
.editor-dias label.dia { gap: 2px; margin-right: 2px; }
.editor-dias .unidad .nombre { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.editor-dias .dias { display: inline-flex; gap: 2px; align-items: center; }
.editor-dias input[type="checkbox"] { width: auto; min-height: 0; padding: 0; margin: 0; border: 0; box-shadow: none; flex: none; }
.editor-dias .editor-dias-fila select { width: auto; min-height: 0; padding: 4px 8px; }
@media (max-width: 640px) {
  .editor-dias-fila { grid-template-columns: 1fr auto; gap: 6px; }
  .editor-dias-fila .dias { grid-column: 1 / -1; }
}

