/* ============================================================
   PRAEVIS — Seguridad y Salud en el Trabajo
   Grafito + amarillo de seguridad · mobile-first
   ============================================================ */

/* Archivo, del linaje DIN (la norma alemana de rotulación industrial).
   Va como archivo propio y no desde un servicio ajeno: en planta la señal es
   mala y una fuente que no baja cambia cómo se ve todo. `swap` deja leer con
   la del sistema mientras llega. */
@font-face {
  font-family: Archivo;
  src: url('/fuentes/Archivo-latin.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
:root {
  /* Cuánto mide la franja de abajo del iPhone. Vale 0 en todo lo demás.
     > Va detrás de una variable y no escrita con `env()` en cada lugar por una
     > razón práctica: **así se puede comprobar**. `env()` no se puede simular
     > desde una prueba —no hay iPhone del otro lado— así que el arreglo se
     > escribía y nadie podía medir si funcionaba. Con la variable,
     > `pruebas/movil.mjs` le pone 34 px y mide que los botones queden arriba. */
  --safe-bottom: env(safe-area-inset-bottom, 0px);

  /* La paleta de la aplicación no es la del logotipo, y es a propósito. El
     ámbar de marca (#C07A15) funciona impreso y en el pico del logo, pero
     llevado a toda la interfaz —botones, elegidos, gráficos— apaga la pantalla:
     leído en planta, de día y con el brillo del celular peleando contra el sol,
     lo apagado se pierde. Acá manda el amarillo de seguridad, el mismo de los
     carteles, y los estados van vivos. El logotipo conserva su ámbar. */
  --cement:     #ECEAE3;   /* fondo */
  --graphite:   #1A1C20;   /* tinta / bordes / barra */
  --yellow:     #F4B400;   /* acento (seguridad) */
  --yellow-dk:  #d89e00;
  --obs:        #0E7A5F;   /* observación */
  --inc:        #D0392B;   /* incidente */
  --warn:       #E08600;
  --surface:    #FFFFFF;
  --muted:      #6b7078;
  --muted-2:    #4b5058;
  --line-soft:  #c9c6bd;
  /* Los obturadores son otro mundo: control interno, otro equipo, otra pantalla.
     Un acero que no se parece a ninguno de los tres estados de un reporte —ni
     amarillo, ni verde, ni rojo— para que nadie se confunda de sección. */
  --obt:        #2F6FA8;
  --obt-dk:     #245781;

  --radius:     4px;
  --radius-sm:  3px;
  --font: Archivo, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", "Cascadia Mono", Consolas, "Liberation Mono", monospace;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }

/* El atributo hidden siempre gana: las clases con display (flex/grid/inline-block)
   pisarían la regla del navegador y el elemento quedaría visible. */
[hidden] { display: none !important; }
body {
  font-family: var(--font);
  color: var(--graphite);
  background: var(--cement);
  -webkit-font-smoothing: antialiased;
  line-height: 1.5;
  overflow-x: clip;               /* evita scroll horizontal accidental */
}

/* ── Barra superior ──────────────────────────────────────── */
.appbar {
  background: var(--graphite);
  color: #fff;
  padding: 14px 18px;
  position: sticky;
  top: 0;
  z-index: 50;
  border-bottom: 3px solid var(--yellow);
}
.appbar-inner {
  max-width: 1180px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: 12px;
}
.appbar h1 {
  font-size: .92rem; font-weight: 700; margin: 0;
  text-transform: uppercase; letter-spacing: .04em;
}
.appbar .sub {
  font-size: .64rem; margin: 2px 0 0; color: #9aa0ab;
  text-transform: uppercase; letter-spacing: .04em;
  display: flex; align-items: center; gap: 7px;
}
/* El logotipo en la barra: versión corta en negativo, que el fondo es
   grafito. Va contenido en altura porque Archivo es más ancha que la fuente
   del sistema y, sin techo, el encabezado se comía media pantalla de celular. */
.appbar .logo {
  height: 20px; width: auto; flex-shrink: 0;
  display: block;
}
@media (min-width: 720px) { .appbar .logo { height: 26px; } }
.appbar .logo + .titulos { border-left: 1px solid rgba(255,255,255,.22); padding-left: 12px; }
.appbar .spacer { flex: 1; }
.badge {
  font-size: .68rem; font-weight: 700; padding: 5px 10px; border-radius: var(--radius-sm);
  background: rgba(244,180,0,.16); color: var(--yellow); white-space: nowrap;
  text-transform: uppercase; letter-spacing: .06em; font-family: var(--mono);
}

/* ── Layout ──────────────────────────────────────────────── */
/* ── La franja de abajo del iPhone ───────────────────────────
   > ⚠️ **El viewport va con `viewport-fit=cover`**, que extiende la página por
   > debajo de la barra de inicio del iPhone —la rayita de abajo— a propósito:
   > es lo que hace que la barra de arriba llegue hasta el borde. Pero lo que se
   > apoya en el **fondo** queda debajo de esa franja, y ahí se perdían los
   > botones de «Volver» y «Confirmar y enviar» del cartel de revisión: se veían
   > cortados por una banda clara y no se podían tocar.
   >
   > No estaba contemplada **en ningún lado** del archivo, así que le pasaba a
   > todo lo que se apoya abajo: el cartel, el aviso que aparece y desaparece, el
   > pie del menú con «Salir» y el final de cualquier pantalla larga.
   >
   > `env(safe-area-inset-bottom)` vale 0 en todo lo que no sea un iPhone con esa
   > barra, así que no cambia nada en el resto — el fallback es por si el
   > navegador no conoce la función. */
.container {
  max-width: 1180px; margin: 0 auto; padding: 18px;
  padding-bottom: calc(18px + var(--safe-bottom));
}
.narrow { max-width: 640px; }

/* ── El menú lateral ─────────────────────────────────────────
   En el escritorio es una columna fija a la izquierda; en el celular, el mismo
   bloque entra como cajón. Es la estructura del otro módulo de la plataforma,
   a propósito: quien use los dos no tiene que aprender dos navegaciones.

   > ⚠️ **Cerrado va con `display: none`, no corrido con `translateX(-100%)`.**
   > Un cajón corrido sigue teniendo caja —`getBoundingClientRect` lo devuelve en
   > `left: -248`— y `pruebas/movil.mjs`, que busca lo que se sale de la pantalla
   > midiendo y no leyendo el CSS, lo contaría como desborde en las nueve
   > pantallas a la vez. La entrada se anima con `@keyframes`, que arranca sola
   > cuando el elemento aparece. */
.lateral {
  display: none;
  position: fixed; top: 0; left: 0; bottom: 0;
  /* «Salir» está al pie del cajón: sin esto queda bajo la franja del iPhone. */
  padding-bottom: var(--safe-bottom);
  width: 248px; max-width: 86vw;
  background: var(--graphite); color: #fff;
  z-index: 210;                       /* por encima del velo, que es 200 */
  flex-direction: column;
  overflow-y: auto; overscroll-behavior: contain;
}
.lateral.abierto { display: flex; animation: cajon-entra .22s cubic-bezier(.2,.8,.2,1); }
@keyframes cajon-entra { from { transform: translateX(-100%); } to { transform: none; } }
@media (prefers-reduced-motion: reduce) { .lateral.abierto { animation: none; } }

/* El velo lleva `.overlay` a propósito: `modal.js` ya sabe frenar el fondo
   cuando aparece uno, que es la mitad que el CSS no puede hacer en iPhone. */
.lateral-velo { z-index: 200; }

.lat-top { padding: 16px 18px 14px; border-bottom: 1px solid rgba(255,255,255,.14); }
.lat-logo { display: block; height: 22px; width: auto; }
.lat-parada {
  margin: 10px 0 0; font-size: .68rem; color: #9aa0ab;
  text-transform: uppercase; letter-spacing: .05em; line-height: 1.35;
}
.lat-parada.sin-parada { color: var(--warn); }

.lat-items { flex: 1; padding: 8px 0; }
.lat-item {
  display: block; padding: 11px 18px; text-decoration: none;
  color: #d7dae0; border-left: 3px solid transparent;
  min-height: 44px;                   /* se toca con guante */
}
.lat-item b {
  display: block; font-size: .86rem; font-weight: 600; color: #fff;
  letter-spacing: .01em;
}
.lat-item span { display: block; font-size: .72rem; color: #9aa0ab; line-height: 1.3; }
.lat-item:hover { background: rgba(255,255,255,.06); }
.lat-item:focus-visible { outline: 2px solid var(--yellow); outline-offset: -2px; }
/* La que se está mirando. El amarillo de seguridad y no el ámbar del logotipo:
   en pantalla lo apagado se pierde, que es la decisión del `:root` de acá
   arriba aplicada a un caso más. */
.lat-item.actual { background: rgba(244,180,0,.12); border-left-color: var(--yellow); }
.lat-item.actual b { color: var(--yellow); }

/* ── El módulo interno, aparte ────────────────────────────────
   Los obturadores son otro equipo y otro trabajo, y nada de eso va a ningún
   tablero del cliente: van al final y separados, para que quien entra a mirar
   reportes no los lea como una sección más de lo mismo.

   El acero es `--obt`, el mismo que ya lleva la barra de esas tres pantallas
   (`.appbar.obt`). Si el menú los pintara de otro color, el color dejaría de
   decir «esto es el módulo interno» y pasaría a ser decoración. */
.lat-sep {
  margin: 10px 0 2px; padding: 10px 18px 0;
  border-top: 1px solid rgba(255,255,255,.14);
}
.lat-sep span {
  font-size: .62rem; color: #8d94a0; font-family: var(--mono);
  text-transform: uppercase; letter-spacing: .12em;
}
/* El acero puro (#2F6FA8) sobre grafito queda por debajo del contraste que se
   lee en planta con el sol pegando en la pantalla, así que el texto va en un
   acero aclarado y el color pleno queda para el borde y el fondo del elegido. */
/* > ⚠️ **No reusar la clase `obt` acá.** `.obt` ya existe y es **la tarjeta de
   > un obturador** en la lista de trabajo —fondo blanco, borde y padding—, así
   > que un `<a class="lat-item obt">` heredaba la tarjeta entera y el ítem del
   > menú salía blanco sobre el grafito. En el CSS las dos reglas se leen
   > perfectas por separado; esto solo se ve mirando la pantalla. */
.lat-item.interno b { color: #8FBCE4; }
.lat-item.interno.actual { background: rgba(47,111,168,.20); border-left-color: var(--obt); }
.lat-item.interno.actual b { color: #BBD9F2; }

/* El pie: quién sos y por dónde se sale.

   «Salir» va con forma de botón y no como un ítem más de la lista, a propósito:
   todo lo de arriba lleva a algún lado y esto **hace algo**. Con la misma forma
   que los demás, se toca creyendo que es otra pantalla —y lo que abre es el
   cartel que pregunta si se pierde el reporte a medias—. */
.lat-pie {
  padding: 14px 18px; border-top: 1px solid rgba(255,255,255,.14);
  font-size: .74rem; color: #9aa0ab;
  display: flex; align-items: center; gap: 10px;
}
.lat-quien { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lat-salir {
  flex-shrink: 0; min-height: 40px; padding: 0 14px;   /* 40 px: se toca con guante */
  background: transparent; color: #d7dae0;
  border: 1px solid rgba(255,255,255,.28); border-radius: var(--radius-sm);
  font-family: var(--mono); font-size: .72rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .08em; cursor: pointer;
}
/* Al pasar por encima sí se pone rojo: ahí ya no hay que llamar la atención de
   nadie —el dedo o el puntero ya está encima—, y sirve para confirmar que esto
   no es una pantalla más antes de soltar el toque. */
.lat-salir:hover { background: rgba(208,57,43,.18); border-color: var(--inc); color: #fff; }
.lat-salir:focus-visible { outline: 2px solid var(--yellow); outline-offset: 2px; }

/* Quien entra con contraseña propia tiene además «Cambiar mi contraseña», al
   lado de «Salir» y antes que él: «Salir» queda último, que es donde se lo
   busca. El nombre sube a su propio renglón y entero —en una sola línea con
   dos botones, «Valentina Rodríguez» se cortaba con puntos suspensivos—. Tiene
   la forma de «Salir» porque también hace algo en vez de llevar a una
   pantalla, pero no se pone rojo: no es algo de lo que haya que cuidarse. */
.lat-pie.con-clave { flex-wrap: wrap; }
.lat-pie.con-clave .lat-quien { flex-basis: 100%; white-space: normal; }
.lat-clave {
  flex: 1; min-width: 0; min-height: 40px; padding: 0 10px;
  background: transparent; color: #d7dae0;
  border: 1px solid rgba(255,255,255,.28); border-radius: var(--radius-sm);
  font-family: var(--mono); font-size: .72rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .06em; cursor: pointer; line-height: 1.25;
}
.lat-clave:hover { background: rgba(255,255,255,.08); border-color: rgba(255,255,255,.5); color: #fff; }
.lat-clave:focus-visible { outline: 2px solid var(--yellow); outline-offset: 2px; }

/* La hamburguesa. Sólo en el celular: en el escritorio el menú ya está a la
   vista y un botón para abrir lo que está abierto es ruido. */
.menu-btn {
  display: flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; flex-shrink: 0;
  background: transparent; border: 1px solid rgba(255,255,255,.24);
  border-radius: var(--radius-sm); color: #fff;
  cursor: pointer; padding: 0;
}
.menu-btn:hover { background: rgba(255,255,255,.08); }
.menu-btn svg { display: block; }

/* ── En el celular ───────────────────────────────────────── */
/* El logotipo sale de la barra: vive arriba del menú, a un toque. La
   hamburguesa ocupa 40 px —el mínimo para tocar con guante— y sin devolver ese
   lugar los títulos largos se cortaban: «Administración» y «Reporte del día»
   quedaban con puntos suspensivos, que es la pantalla diciendo su propio nombre
   a medias. Sólo donde hay menú: las pantallas que todavía no lo tienen siguen
   con el logotipo en la barra, que es su única marca. */
@media (max-width: 559px) {
  .marco .appbar .logo { display: none; }
  .marco .appbar .logo + .titulos { border-left: 0; padding-left: 0; }
}

/* ── En el escritorio ────────────────────────────────────── */
@media (min-width: 1024px) {
  .lateral { display: flex; }
  .lateral.abierto { animation: none; }   /* nunca se abre: ya está abierto */
  .lateral-velo { display: none !important; }
  .menu-btn { display: none; }
  .marco { margin-left: 248px; }
  /* El logotipo vive arriba del menú: repetirlo en la barra es escribir dos
     veces lo mismo a diez centímetros de distancia. */
  .marco .appbar .logo { display: none; }
  .marco .appbar .logo + .titulos { border-left: 0; padding-left: 0; }
  /* Con la columna de la izquierda ocupada, el contenido gana ancho —era lo que
     dejaba dos tercios de pantalla vacíos en el panel—, pero con techo: una
     tabla de 2.500 px no se lee, se recorre. */
  .marco .container { max-width: 1400px; }
  /* El panel y Listas son columnas de lectura y no tableros, así que no van a
     todo el ancho; pero 640 px al lado de una columna de menú dejaban un tercio
     de pantalla vacío. 980 px aprovecha el lugar sin que los renglones de ayuda
     se vuelvan largos de leer. En el celular siguen siendo 640, que ahí nunca
     fue el techo que manda. */
  .marco .container.narrow { max-width: 980px; }
  .marco .appbar-inner { max-width: 1400px; }
}

/* Impreso no hay por dónde navegar: el documento del día es una hoja. */
@media print {
  .lateral, .lateral-velo, .menu-btn { display: none !important; }
  .marco { margin-left: 0; }
}


/* ── Tarjetas ────────────────────────────────────────────── */
.card {
  background: var(--surface);
  border: 1px solid var(--graphite);
  border-radius: var(--radius);
  padding: 18px;
  margin-bottom: 14px;
}
.card h2 { font-size: .82rem; margin: 0; text-transform: uppercase; letter-spacing: .08em; }
.card-title { display: flex; align-items: center; gap: 8px; margin-bottom: 16px; padding-bottom: 12px; border-bottom: 1px solid var(--cement); }
.card-title h2 { margin: 0; }

/* ── Formulario ──────────────────────────────────────────── */
.field { margin-bottom: 16px; }
.field > label {
  display: block; font-size: .72rem; font-weight: 700;
  color: var(--muted-2); margin-bottom: 8px;
  text-transform: uppercase; letter-spacing: .06em;
}
.field .hint { font-weight: 400; color: var(--muted); text-transform: none; letter-spacing: 0; font-size: .74rem; }

/* `password` va en la lista desde que existe la pantalla de ingreso: sin él,
   el campo del PIN se dibujaba con su ancho intrínseco —veinte caracteres— y
   se salía de la tarjeta. */
input[type="text"], input[type="datetime-local"], input[type="date"],
input[type="password"], select, textarea {
  width: 100%;
  min-width: 0;
  max-width: 100%;
  font: inherit;
  color: var(--graphite);
  background: #fff;
  border: 1.5px solid var(--line-soft);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  min-height: 48px;
  transition: border-color .12s, box-shadow .12s;
}
textarea { min-height: 92px; resize: vertical; line-height: 1.5; }

/* Fecha y hora en iPhone. Es el mismo problema del PIN, un escalón más arriba:
   `width: 100%` no alcanza porque Safari le da al campo **apariencia nativa**, y
   con eso se dibuja con su ancho intrínseco —más que la tarjeta— y el texto
   centrado adentro. En la pantalla se ve como si el campo se hubiera salido del
   bloque, y solo pasa en el celular: en el escritorio no se nota.
   Apagar la apariencia nativa lo deja como cualquier otro campo. El selector de
   ruedas se sigue abriendo igual al tocarlo, que es lo único que importa. */
input[type="datetime-local"], input[type="date"], input[type="time"] {
  -webkit-appearance: none;
  appearance: none;
  display: block;
  text-align: left;
}
/* Y el texto de adentro, que iOS centra por su cuenta aunque el campo ya esté
   alineado. Son las dos partes del mismo campo: la caja y su contenido. */
input[type="datetime-local"]::-webkit-date-and-time-value,
input[type="date"]::-webkit-date-and-time-value {
  text-align: left;
  margin: 0;
}
input[type="datetime-local"]::-webkit-datetime-edit,
input[type="date"]::-webkit-datetime-edit { padding: 0; }
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--yellow);
  box-shadow: 0 0 0 3px rgba(244,180,0,.25);
}

/* Tipo de reporte — selector destacado (dato clave) */
.card-tipo { border-left: 5px solid var(--yellow); }
.card-tipo .field > label { display: flex; align-items: center; }
.tipo-select {
  font-size: 1.1rem;
  font-weight: 700;
  min-height: 58px;
  padding: 14px 16px;
}
.tipo-select:invalid { color: var(--muted); font-weight: 600; }
.req {
  font-size: .6rem; font-weight: 800; letter-spacing: .08em;
  color: #7a5b00; background: #fbf3d8; border: 1px solid var(--warn);
  padding: 3px 8px; border-radius: 999px; margin-left: 8px;
}

/* Identidad / historial */
.ident-bar {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  background: var(--surface); border: 1px solid var(--graphite); border-radius: var(--radius);
  padding: 8px 8px 8px 14px; margin-bottom: 14px; font-size: .85rem;
}
.ident-who strong { text-transform: none; }
/* Mientras se carga un reporte, solo el nombre. El recuadro con «Reportes de…»
   y «Salir» ocupaba ~85 px arriba de cada paso, y los dos botones ya están en
   el menú ☰. El nombre queda: los teléfonos se comparten entre turnos y quien
   carga tiene que ver a nombre de quién queda. Al elegir el tipo y al terminar,
   el recuadro sigue entero.
   Va con `:has()` y no con una clase que ponga form.js: el formulario muestra y
   esconde los pasos desde seis lugares, y esto tiene que valer en todos. Un
   navegador sin `:has()` (iOS anterior a 15.4) muestra el recuadro de siempre. */
main:has(#wizard:not([hidden])) .ident-bar {
  background: none; border: none; padding: 0 2px; margin-bottom: 10px;
  color: var(--muted-2); font-size: .84rem;
}
main:has(#wizard:not([hidden])) .ident-bar strong { color: var(--graphite); }
main:has(#wizard:not([hidden])) .ident-bar .btn { display: none; }
.edit-banner {
  background: #fbf3d8; border: 1px solid var(--warn); color: #7a5b00;
  border-radius: var(--radius); padding: 11px 14px; margin-bottom: 14px; font-size: .86rem; font-weight: 600;
}
/* El velo. **Scrollea él**, no la página de atrás: un modal más alto que la
   pantalla —el de colocar un obturador lo es, con el aviso de la cámara y el
   campo de fotos— dejaba lo de abajo fuera de alcance, y el dedo movía el fondo.
   `overscroll-behavior: contain` es lo que corta esa herencia. */
.overlay {
  position: fixed; inset: 0; background: rgba(15,17,20,.6);
  display: grid; place-items: center; z-index: 200; padding: 16px;
  /* Los botones del cartel viven al final: sin esto quedan bajo la franja del
     iPhone (ver la nota arriba de `.container`). */
  padding-bottom: calc(16px + var(--safe-bottom));
  overflow-y: auto; overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
.modal {
  background: var(--surface); border: 1px solid var(--graphite); border-top: 4px solid var(--yellow);
  border-radius: var(--radius); padding: 22px; max-width: 420px; width: 100%;
  /* Red de seguridad para todos los modales de la app: ninguno puede crecer más
     que la pantalla sin poder recorrerse. `dvh` es lo que descuenta la barra de
     direcciones del celular, que con `vh` queda tapando el final. */
  max-height: calc(100vh - 32px);
  /* `dvh` descuenta la barra de direcciones, pero **no** la franja de abajo del
     iPhone: con el cartel a la altura máxima, sus botones caían igual debajo. */
  max-height: calc(100dvh - 32px - var(--safe-bottom));
  overflow-y: auto; overscroll-behavior: contain;
}

/* Y el patrón bueno para los modales con formulario adentro: el título arriba,
   el cuerpo scrolleando en el medio, y **los botones siempre a la vista**. En un
   celular, tener que recorrer el modal entero para encontrar «Guardar» es la
   forma más segura de que alguien lo abandone a la mitad. */
.modal.modal-scroll { display: flex; flex-direction: column; overflow: hidden; padding: 0; }
.modal.modal-scroll > h3 { flex: 0 0 auto; margin: 0; padding: 20px 22px 12px; }
.modal.modal-scroll > .modal-cuerpo {
  flex: 1 1 auto; min-height: 0; padding: 0 22px;
  overflow-y: auto; overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
.modal.modal-scroll > .modal-acciones {
  flex: 0 0 auto; margin: 0; padding: 14px 22px 18px;
  border-top: 1px solid var(--cement); background: var(--surface);
}
.modal.modal-scroll > .modal-error { flex: 0 0 auto; margin: 0; }
/* La advertencia del obturador va adentro del cuerpo y scrollea con él: está
   arriba de todo, así que es lo primero que se lee al abrir el cartel. Fijarla
   le robaría al cuerpo la altura que hace falta para escribir. */

/* Lo que falta, dicho adentro del modal y pegado a los botones. Antes esto era
   un `toast` con z-index 100 contra un velo con 200: **se dibujaba debajo del
   velo** y no lo veía nadie. Un error que no se ve se lee como un botón roto. */
.modal-error {
  display: flex; align-items: baseline; gap: 8px;
  background: #fdeceb; border-left: 4px solid var(--inc);
  color: #8f251b; font-size: .86rem; font-weight: 600; line-height: 1.45;
  padding: 10px 14px; margin: 0 0 12px;
}
.modal-error::before { content: "!"; font-weight: 800; }
/* Y el campo que falta, marcado: el texto dice qué pasa, el borde dice dónde. */
.field.falta > label { color: var(--inc); }
.field.falta input, .field.falta select, .field.falta textarea,
.field.falta .file-drop { border-color: var(--inc); }
.modal h2 { margin: 0 0 6px; font-size: 1.05rem; text-transform: uppercase; letter-spacing: .03em; }
.modal-sub { margin: 0 0 16px; color: var(--muted); font-size: .88rem; }
.ident-label { display: block; font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; color: var(--muted-2); margin-bottom: 7px; }
.ident-error { margin-top: 9px; font-size: .84rem; color: var(--inc); font-weight: 600; }
.modal-lg { max-width: 520px; max-height: 88vh; overflow-y: auto; }

/* Resumen de revisión previa al envío */
.resumen { border-top: 1px solid var(--cement); }
.resumen .fila {
  display: grid; grid-template-columns: 118px 1fr; gap: 6px 12px;
  padding: 9px 0; border-bottom: 1px solid var(--cement); align-items: baseline;
}
.resumen .k {
  font-size: .64rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--muted-2);
}
.resumen .v { font-size: .9rem; }
.resumen .v.fuerte { font-weight: 700; }
.resumen .v.vacio { color: var(--muted); font-style: italic; font-size: .84rem; }
.modal-acciones { display: flex; gap: 10px; margin-top: 18px; }
.modal-acciones .btn { flex: 1; width: auto; }

@media (max-width: 420px) {
  .resumen .fila { grid-template-columns: 1fr; gap: 2px; }
}

/* Administración de listas */
/* ── Índice de secciones (administración) ────────────────── */
/* Todas las listas a la vista con su cuenta y sus pendientes; abajo se abre
   solo la elegida. Antes era un scroll largo donde no se veía qué había. */
/* > ⚠️ **El nombre y el número no pueden tocarse.** Con botones de 136 px y una
   > grilla de dos columnas, «OBTURADORES» se metía debajo de su «25», y
   > «CONTRATISTAS» y «SOLICITANTES» quedaban pegados al suyo entre 768 y 1100
   > px. Ahora son dos defensas: botones de 168 px, donde los nombres de hoy
   > entran con su número, y si igual no entra —un nombre más largo, un número
   > de tres cifras—, **el número baja de renglón** en vez de pisar el nombre.
   > `admin-secciones.mjs` lo mide en ocho anchos, sobre lo dibujado. */
.secciones {
  /* En el celular, siempre dos columnas: `min()` no deja que el mínimo pase de la
     mitad. Nueve botones en una sola columna empujan la lista fuera de la pantalla. */
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(168px, calc(50% - 4.5px)), 1fr));
  gap: 9px; margin-bottom: 18px;
}
.seccion {
  cursor: pointer; font: inherit; text-align: left;
  background: var(--surface); border: 1px solid var(--graphite);
  border-radius: var(--radius); padding: 11px 13px;
  display: grid; gap: 2px; align-content: start;
  transition: background .12s, border-color .12s;
}
.seccion:hover { background: #fffdf6; border-color: var(--yellow); }
.seccion-cab { display: flex; flex-wrap: wrap; align-items: baseline; column-gap: 10px; row-gap: 2px; }
.seccion b {
  flex: 1 1 auto; font-size: .8rem; text-transform: uppercase; letter-spacing: .04em;
  line-height: 1.2;
}
.seccion .n {
  margin-left: auto; font-family: var(--mono); font-size: .78rem; font-weight: 700;
  color: var(--muted); font-variant-numeric: tabular-nums;
}
.seccion .pend {
  font-size: .64rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .05em; color: #7a5b00;
  background: #fbf3d8; border: 1px solid var(--warn);
  border-radius: 999px; padding: 2px 7px; justify-self: start; margin-top: 3px;
}
/* La elegida se pinta llena, como el resto de lo seleccionable en la app. */
.seccion.on { background: var(--graphite); border-color: var(--graphite); color: #fff; }
.seccion.on:hover { background: var(--graphite); }
.seccion.on .n { color: rgba(255,255,255,.7); }
.seccion.on .pend { background: var(--yellow); border-color: var(--yellow); color: var(--graphite); }

/* Las listas de UPM, en modo consulta */
.fija { padding: 11px 0; border-top: 1px solid var(--cement); }
.fija:first-child { border-top: none; padding-top: 0; }
.fija h3 {
  margin: 0 0 4px; font-size: .68rem; text-transform: uppercase;
  letter-spacing: .07em; color: var(--muted-2);
}
.fija h3 span { font-family: var(--mono); color: var(--muted); margin-left: 5px; }
.fija p { margin: 0; font-size: .84rem; color: var(--graphite); line-height: 1.5; }

/* El stock de obturadores: agrupado por tipo, como en la planilla. Cada fila
   dice qué es y cuántos están en uso a la izquierda, y el número que se cambia
   a la derecha, con − y + de 44 px: se toca con el pulgar, no se tipea. */
.stock-grupo + .stock-grupo { margin-top: 18px; }
.stock-grupo h3 {
  margin: 0 0 2px; font-family: var(--mono); font-size: .8rem;
  letter-spacing: .06em; color: var(--muted-2);
}
.stock-fila {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 2px 12px;
  align-items: center; padding: 11px 0; border-top: 1px solid var(--cement);
}
/* El nombre entero en un renglón: «100–200 / mm» partido no se lee como una
   medida. La presión va abajo, con lo que está en uso, para dejarle lugar. */
.stock-que b { font-size: .96rem; white-space: nowrap; }
.stock-bar { font-family: var(--mono); font-size: .8rem; font-weight: 400; color: var(--muted); white-space: nowrap; }
.stock-uso { grid-column: 1 / -1; font-size: .82rem; color: var(--muted-2); line-height: 1.4; }
.stock-uso.sin { color: #7a5b00; font-weight: 700; }
.stock-cant { grid-column: 2; grid-row: 1; display: flex; align-items: center; gap: 4px; }
/* Con las tres clases: `.btn.small` está más abajo en este archivo y, con dos,
   le ganaba por orden y les devolvía el alto mínimo en cero. */
.stock-cant .btn.small { min-width: 44px; min-height: 44px; padding: 0; font-size: 1.1rem; }
.stock-cant input[type="text"] {
  width: 58px; text-align: center; padding: 10px 4px;
  font-family: var(--mono); font-weight: 700; font-variant-numeric: tabular-nums;
}
.stock-fila .stock-guardar { grid-column: 1 / -1; margin-top: 8px; }
.stock-nota { grid-column: 1 / -1; margin: 2px 0 0; font-size: .8rem; color: var(--muted); line-height: 1.4; }

.cat-help { margin: -4px 0 14px; color: var(--muted); font-size: .84rem; }
/* En el celular no entran tres controles en una línea: que bajen solos. */
.cat-add { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.cat-add input { flex: 1; min-width: 150px; }
/* También el desplegable: sin un mínimo se comprime hasta mostrar una letra. */
.cat-add select { flex: 1; min-width: 165px; }
.cat-add .btn { flex-shrink: 0; }
/* Resultado de la búsqueda: dice si lo escrito ya está antes de agregarlo. */
.busq-aviso {
  font-size: .8rem; color: var(--muted-2); margin: -6px 0 12px;
  padding: 7px 10px; border-radius: var(--radius-sm);
  background: #f4f2ec; border: 1px solid var(--line-soft);
}
.busq-aviso.ya { background: #fbf3d8; border-color: var(--warn); color: #7a5b00; font-weight: 600; }
/* Cuando el aviso pregunta algo, las dos respuestas van con él. Antes la
   pregunta llegaba en un aviso pasajero y no había dónde contestar. */
.aviso-acciones { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
.aviso-acciones .btn { min-height: 42px; }

.cat-list { display: flex; flex-direction: column; }
.cat-row {
  display: grid; grid-template-columns: 1fr auto; gap: 4px 10px; align-items: center;
  padding: 11px 0; border-top: 1px solid var(--cement);
}
.cat-row.off { opacity: .55; }

/* Personas va en un grupo por rol, en el orden del alta. Cada grupo con su
   nombre, cuántas personas tiene y una marca del color de ese rol: el mismo
   azul de obturadores que el resto del módulo, el amarillo para los
   supervisores y el grafito para Gerencia. El rol no se repite en cada fila:
   lo dice el grupo. */
.cat-grupo + .cat-grupo { margin-top: 22px; }
/* Al ir a un grupo desde su botón, que no quede tapado por la barra de arriba,
   que es fija. */
.cat-grupo { scroll-margin-top: 90px; }
/* De a dos, como las cuatro opciones del alta: sueltos ocupaban tres renglones
   en un celular, porque «Equipo de Reportes 13» no entra al lado de nada. */
.chips.cat-saltos { margin: -4px 0 16px; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.cat-saltos .chip { padding: 8px 10px; line-height: 1.2; }
@media (min-width: 760px) {
  .chips.cat-saltos { display: flex; }
}
.cat-saltos .chip .n { font-family: var(--mono); font-size: .76rem; color: var(--muted); margin-left: 4px; }
.cat-grupo-cab { display: flex; align-items: center; gap: 8px; padding-bottom: 8px; }
.cat-grupo-cab::before {
  content: ''; flex-shrink: 0; width: 6px; height: 18px; border-radius: 2px;
  background: var(--muted);
}
.cat-grupo[data-grupo="obturador"] .cat-grupo-cab::before { background: var(--obt); }
.cat-grupo[data-grupo="supervisor"] .cat-grupo-cab::before { background: var(--yellow); }
.cat-grupo[data-grupo="gerencia"] .cat-grupo-cab::before { background: var(--graphite); }
.cat-grupo-cab h3 {
  margin: 0; font-size: .8rem; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: var(--graphite);
}
.cat-grupo-cab .n { font-family: var(--mono); font-size: .76rem; color: var(--muted); }
.cat-name { font-weight: 700; font-size: 1.02rem; letter-spacing: -.01em; }
/* Lo que describe a la fila, debajo del nombre: qué hace, de qué contratista y
   cuánto cargó. Junto al nombre, en 390 px, la cédula se partía al medio. */
.cat-meta {
  grid-column: 1 / -1; display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
}
.cat-usos {
  font-family: var(--mono); font-size: .7rem; color: var(--muted);
  white-space: nowrap; margin-left: auto;
}

/* Las acciones acompañan: el que manda visualmente es el nombre. */
.cat-actions { display: flex; gap: 14px; flex-wrap: wrap; align-items: center; }
.cat-actions .btn {
  padding: 0; min-height: 0; font-size: .78rem; font-weight: 500;
  text-transform: none; letter-spacing: 0; color: var(--muted);
  background: none; border: none; border-radius: 0;
}
.cat-actions .btn:hover { background: none; color: var(--graphite); text-decoration: underline; }
.cat-actions .btn.danger-text:hover { color: var(--inc); }
/* Qué se puede hacer con esta fila. Eran seis o siete textos sueltos repetidos
   en cada una: la lista se leía como un muro y lo que uno viene a mirar —quién
   está, de qué contratista, cuánto cargó— quedaba abajo de los botones. */
/* El de las tarjetas de «Reportes de…» es el mismo: ahí adentro va «Eliminar»,
   que en cada tarjeta era un botón rojo y lo que más se veía de la lista. */
.cat-actions .btn.cat-mas, .rep-actions .btn.rep-mas {
  font-size: 1.1rem; line-height: 1; letter-spacing: .06em;
  min-width: 44px; min-height: 40px; color: var(--muted-2);
  display: grid; place-items: center;
}
.cat-actions .btn.cat-mas:hover, .rep-actions .btn.rep-mas:hover { color: var(--graphite); text-decoration: none; }
.cat-actions .btn.cat-mas[aria-expanded="true"],
.rep-actions .btn.rep-mas[aria-expanded="true"] { color: var(--graphite); background: var(--cement); border-radius: var(--radius-sm); }
.rep .rep-menu { margin-top: 10px; }

/* El menú baja debajo de su fila y ocupa el ancho entero: en un celular, un
   menú flotante pegado al borde derecho deja las opciones a 30 px del pulgar. */
.cat-menu {
  grid-column: 1 / -1; margin-top: 8px;
  border: 1px solid var(--graphite); border-radius: var(--radius-sm);
  background: var(--cement); overflow: hidden;
}
.cat-op {
  display: block; width: 100%; text-align: left;
  min-height: 44px; padding: 10px 14px; font-size: .88rem;
  background: none; border: none; border-bottom: 1px solid var(--cement);
  color: var(--graphite); cursor: pointer; font-family: inherit;
}
.cat-op:last-child { border-bottom: none; }
.cat-op:hover { background: var(--surface); }
.cat-op.danger-text { color: var(--inc); }
/* El que ya está puesto se marca en vez de esconderse: se ve cuál es sin tener
   que acordarse de lo que decía la fila. */
.cat-op.puesto { font-weight: 700; }
.cat-op.puesto::after { content: ' ·  puesto'; font-weight: 400; color: var(--muted); font-size: .78rem; }
.cat-op.volver { color: var(--muted); font-size: .8rem; min-height: 38px; }

/* De qué equipo es cada persona y de qué contratista, en la propia fila. */
.tag.rol-reportes { background: var(--cement); color: var(--muted-2); }
.tag.contratista { background: var(--surface); border: 1px solid var(--graphite); color: var(--graphite); }
/* La cédula pegada a la etiqueta se leía como una sola cosa. */
.cat-cedula { margin-right: 6px; }

/* Unir dos que son la misma. Se abre dentro de la fila —no en una ventana que
   tape la lista— y ocupa el ancho entero: en el celular, elegir contra cuál se
   une con un desplegable apretado en una esquina es pedir un error. */
.cat-fusion {
  grid-column: 1 / -1; margin-top: 8px; padding: 12px;
  background: var(--paper-2, #f6f4ef); border: 1px solid var(--cement); border-radius: 8px;
  display: flex; flex-direction: column; gap: 10px;
}
.cat-fusion-linea { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.cat-fusion-linea label { font-size: .84rem; color: var(--muted); }
.cat-fusion select {
  flex: 1 1 200px; min-width: 0; min-height: 40px;
  font-size: .9rem; padding: 6px 10px;
  border: 1px solid var(--cement); border-radius: 6px; background: #fff;
}
/* Estos dos sí son botones de verdad y no texto: uno mueve reportes. */
.cat-fusion .btn { min-height: 40px; padding: 8px 14px; font-size: .8rem; }
.cat-fusion-resumen { font-size: .86rem; line-height: 1.45; }
.cat-fusion-resumen p { margin: 0 0 6px; }
.cat-fusion-resumen p:last-child { margin-bottom: 0; }
.cat-fusion-resumen b { color: var(--inc); }

.tag.soft.warn { background: #fbf3d8; color: #7a5b00; border-color: var(--warn); }
.danger-text { color: var(--inc); }
.cat-empty { color: var(--muted); font-size: .86rem; padding: 12px 0; }
.cat-cedula { font-family: var(--mono); font-size: .74rem; color: var(--muted); font-weight: 400; }

/* ── Panel del administrador ─────────────────────────────── */
.barra { height: 6px; background: var(--cement); border-radius: 3px; overflow: hidden; margin-bottom: 6px; }
.barra i { display: block; height: 100%; background: var(--yellow); transition: width .3s; }

.paso { display: grid; grid-template-columns: 30px 1fr auto; gap: 14px; align-items: center; padding: 14px 0; border-top: 1px solid var(--cement); }
.paso:first-of-type { border-top: none; }
.paso .n { width: 28px; height: 28px; border-radius: var(--radius-sm); display: grid; place-items: center; font-family: var(--mono); font-weight: 800; font-size: .78rem; background: var(--cement); color: var(--muted-2); }
.paso.hecho .n { background: var(--obs); color: #fff; }
.paso.activo .n { background: var(--yellow); color: var(--graphite); }
.paso b { display: block; font-size: .94rem; }
.paso span { font-size: .82rem; color: var(--muted); }
.paso.hecho b { color: var(--muted-2); }

.jornada { display: flex; align-items: center; gap: 13px; flex-wrap: wrap; background: var(--surface); border: 1px solid var(--graphite); border-left: 5px solid var(--obs); border-radius: var(--radius); padding: 14px 16px; margin-bottom: 14px; }
.jornada.cerrada { border-left-color: var(--inc); }
.jornada .estado { font-family: var(--mono); font-size: .66rem; font-weight: 800; letter-spacing: .07em; text-transform: uppercase; color: var(--obs); }
.jornada.cerrada .estado { color: var(--inc); }
.jornada b { display: block; font-size: .98rem; margin-top: 2px; }
/* La parada usa la misma forma que la jornada porque es lo mismo un nivel más
   arriba —una parada agrupa días—, con el borde en gris para que no compitan:
   el día se cierra todos los días y la parada dos veces por obra. */
.parada-panel { border-left-color: var(--graphite); }
.parada-panel .estado { color: var(--muted-2); }
.parada-fechas { display: block; font-size: .76rem; color: var(--muted); margin-top: 3px; }
/* Los botones juntos y no sueltos en la fila: en un celular, tres controles
   sueltos con `flex-wrap` se reparten mal y uno queda solo abajo a la derecha,
   lejos de lo que nombra. */
.parada-botones { display: flex; gap: 8px; flex-wrap: wrap; }

.aviso-fila { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.aviso-fila .btn { flex-shrink: 0; }

/* Quién está reportando: dos números por persona, hoy y en toda la parada. */
.eq-encabezado, .eq-fila {
  display: grid; grid-template-columns: 1fr 54px 64px; gap: 10px;
  align-items: center; padding: 8px 0;
}
.eq-encabezado {
  border-bottom: 1px solid var(--graphite); padding-bottom: 6px;
  font-size: .6rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .07em; color: var(--muted);
}
.eq-fila { border-top: 1px solid var(--cement); font-size: .9rem; }
.eq-encabezado .eq-n, .eq-fila .eq-n { text-align: right; }
.eq-fila .eq-n {
  font-family: var(--mono); font-weight: 700; font-variant-numeric: tabular-nums;
  color: var(--muted-2);
}
.eq-fila .eq-n.total { color: var(--graphite); }
/* Quien no cargó nada en toda la parada se marca: es el dato accionable. */
.eq-fila.sin .eq-nombre { color: var(--muted); }
.eq-fila.sin .eq-n { color: var(--warn); }

/* El equipo va agrupado por contratista. El contratista se escribe una vez
   arriba del grupo y no al lado de cada nombre: en un celular «Valentina
   Rodríguez (Az Service)» no entra en una línea, y repetirlo catorce veces gasta
   el ancho que necesita lo que sí cambia. */
.eq-grupo + .eq-grupo { margin-top: 16px; }
.eq-grupo-tit {
  display: grid; grid-template-columns: 1fr 54px 64px; gap: 10px;
  align-items: baseline; padding: 6px 0;
  border-bottom: 2px solid var(--graphite);
}
.eq-grupo-nombre {
  font-size: .74rem; font-weight: 800; text-transform: uppercase; letter-spacing: .06em;
}
/* La cuenta de gente, adentro del nombre: si no entra, cae al renglón de abajo
   en vez de empujar a los números fuera de la tarjeta. */
.eq-grupo-cuenta {
  font-style: normal; font-weight: 400; text-transform: none; letter-spacing: 0;
  font-size: .66rem; color: var(--muted); margin-left: 7px; white-space: nowrap;
}
.eq-grupo-tit .eq-n {
  font-family: var(--mono); font-weight: 700; text-align: right;
  font-variant-numeric: tabular-nums; font-size: .8rem;
}
/* Un contratista entero sin cargar nada es otra cosa que una persona sin
   cargar: el subtotal en cero se marca igual que la fila. */
.eq-grupo-tit .eq-n.cero { color: var(--warn); }
/* Los que quedaron sin asignar. No es un contratista más: es lo que falta
   completar, y por eso va último y marcado. */
.eq-grupo.sin-contratista .eq-grupo-nombre { color: var(--warn); }
/* El equipo de obturadores: mismo azul que la etiqueta de Listas, que es lo que
   ya quiere decir «esto es del otro módulo». */
.eq-grupo.obturadores .eq-grupo-nombre { color: var(--obt); }
.eq-grupo.obturadores .eq-grupo-tit { border-bottom-color: var(--obt); }
/* Y qué cuentan sus dos números, que no son reportes. */
.eq-grupo-nota {
  font-size: .64rem; color: var(--muted); padding: 4px 0 2px;
  border-bottom: 1px solid var(--cement);
}
.eq-hoy { font-size: .68rem; color: var(--muted); font-family: var(--mono); }
.cat-help.aviso-suave {
  background: #fbf3d8; border: 1px solid var(--warn); color: #7a5b00;
  border-radius: var(--radius-sm); padding: 8px 10px;
}

.pendiente { display: flex; align-items: center; gap: 12px; padding: 12px 0; border-top: 1px solid var(--cement); flex-wrap: wrap; }
.pendiente:first-child { border-top: none; }
.pendiente b { display: block; font-size: .94rem; }
.pendiente span { font-size: .78rem; color: var(--muted); font-family: var(--mono); }

.mini-fila { display: flex; align-items: center; gap: 9px; padding: 10px 0; border-top: 1px solid var(--cement); font-size: .85rem; flex-wrap: wrap; }
.mini-fila:first-child { border-top: none; }
.mf-txt { flex: 1; min-width: 170px; }
.mf-meta { font-family: var(--mono); font-size: .7rem; color: var(--muted); }


/* ── Accesos ─────────────────────────────────────────────── */
.acceso { padding: 15px 0; border-top: 1px solid var(--cement); }
.acceso:first-of-type { border-top: none; padding-top: 0; }
.acceso-top { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; margin-bottom: 4px; }
.acceso-top b { font-size: .95rem; }
.link-row { display: flex; gap: 8px; align-items: stretch; flex-wrap: wrap; margin: 10px 0 8px; }
.link-row code {
  flex: 1; min-width: 200px; font-family: var(--mono); font-size: .74rem;
  background: var(--cement); border: 1px solid var(--line-soft); border-radius: var(--radius-sm);
  padding: 10px 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mini { font-size: .78rem; color: var(--muted); margin: 6px 0 0; }

@media (min-width: 720px) {
}
@media (max-width: 520px) {
  .paso { grid-template-columns: 30px 1fr; gap: 10px; }
  .paso .btn { grid-column: 2; justify-self: start; }
}

/* Selector segmentado */
.segmented {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 6px;
  background: #e5e3db;
  padding: 5px;
  border-radius: var(--radius);
  border: 1px solid var(--line-soft);
}
.segmented.cols-1 { grid-template-columns: minmax(0, 1fr); }
.segmented input { position: absolute; opacity: 0; pointer-events: none; }
.segmented label {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 13px 10px; border-radius: var(--radius-sm); cursor: pointer;
  font-weight: 700; font-size: .84rem; color: var(--muted-2);
  text-align: center; user-select: none; text-transform: uppercase; letter-spacing: .04em;
  min-width: 0; transition: all .12s;
  /* Cada opción con su propio fondo y su borde. Sin esto son transparentes
     sobre el gris del contenedor, y cuatro opciones se leen como un bloque gris
     con cuatro palabras adentro en vez de como cuatro cosas que se pueden
     tocar. Con guantes y a un brazo de distancia, eso importa. */
  background: var(--surface);
  border: 1px solid var(--line-soft);
}
.segmented label:hover { background: #f4f2ec; border-color: var(--muted); }

/* Lo elegido se pinta lleno, no con un borde: en el celular, a un brazo de
   distancia y con guantes, un contorno fino no alcanza para ver qué se marcó. */
/* Lo elegido lleva también el borde de su color: con el borde claro de las
   demás, un fondo oscuro queda como recortado con tijera. */
.segmented input:checked + label { background: var(--graphite); border-color: var(--graphite); color: #fff; box-shadow: none; }
.segmented input:checked + label.tono-inc  { background: var(--inc); border-color: var(--inc); color: #fff; }
.segmented input:checked + label.tono-obs  { background: var(--obs); border-color: var(--obs); color: #fff; }
.segmented input:checked + label.tono-warn { background: var(--warn); border-color: var(--warn); color: var(--graphite); }
.segmented input:checked + label.tono-gris { background: var(--muted); border-color: var(--muted); color: #fff; }
.segmented input:focus-visible + label { outline: 3px solid rgba(244,180,0,.55); outline-offset: 1px; }

/* ── Formulario por pasos ────────────────────────────────── */

/* Antes de todo: qué se va a reportar */
.tipos { display: grid; gap: 12px; }
.tipo-card {
  width: 100%; text-align: left; cursor: pointer; font: inherit;
  background: var(--surface); border: 1px solid var(--graphite);
  border-left: 5px solid var(--graphite); border-radius: var(--radius);
  padding: 16px; transition: border-color .12s, background .12s;
}
.tipo-card:hover { background: #fffdf6; }
.tipo-card.inc { border-left-color: var(--inc); }
.tipo-card.obs { border-left-color: var(--obs); }
.tipo-card.reu { border-left-color: var(--yellow); }
/* La actividad preventiva va en grafito y no en un color de estado: en esta app
   el rojo, el verde y el amarillo dicen **gravedad**, y una charla de seguridad
   no tiene ninguna. Pintarla de verde la haría leer como 'observación positiva',
   que es otra cosa y ya existe. */
.tipo-card.act { border-left-color: var(--graphite); }
.tipo-card b { display: block; font-size: 1rem; text-transform: uppercase; letter-spacing: .03em; }
.tipo-card span { display: block; color: var(--muted); font-size: .84rem; margin-top: 4px; }

/* Listo, quedó cargado */
.card-listo { text-align: center; border-left: 5px solid var(--obs); }
.listo-tick {
  width: 52px; height: 52px; margin: 4px auto 12px; border-radius: 50%;
  background: var(--obs); color: #fff; display: grid; place-items: center;
  font-size: 1.6rem; font-weight: 800;
}
.card-listo h2 { font-size: .82rem; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 6px; }
.listo-sub { color: var(--muted); font-size: .86rem; margin: 0 0 14px; }
.listo-cod {
  font-family: var(--mono); font-size: 1.35rem; font-weight: 800; letter-spacing: .02em;
  background: var(--cement); border: 1px solid var(--line-soft); border-radius: var(--radius);
  padding: 14px 10px; margin-bottom: 14px; user-select: all; word-break: break-all;
}

/* Dónde estoy del recorrido */
.pasos-head { margin-bottom: 14px; }
.pasos-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 8px; }
.pasos-cuenta {
  font-family: var(--mono); font-size: .72rem; font-weight: 800;
  letter-spacing: .08em; text-transform: uppercase; color: var(--muted-2);
}
.paso-nav { display: flex; gap: 10px; }
.paso-nav .btn { flex: 1; width: auto; }

/* Listas largas: una opción por fila, tocable entera */
.opc-lista {
  border: 1.5px solid var(--line-soft); border-radius: var(--radius-sm);
  background: var(--surface); overflow: hidden;
}
.opc { display: flex; align-items: center; border-top: 1px solid var(--cement); }
.opc:first-child { border-top: none; }
.opc input { position: absolute; opacity: 0; pointer-events: none; }
.opc label {
  flex: 1; display: flex; align-items: center; gap: 10px; min-width: 0;
  padding: 13px 14px; cursor: pointer; font-size: .92rem; line-height: 1.35;
}
.opc label::before {
  content: ''; flex-shrink: 0; width: 18px; height: 18px; border-radius: 50%;
  border: 2px solid var(--line-soft); background: #fff;
}
.opc input:checked + label { background: #fffdf6; font-weight: 700; }
.opc input:checked + label::before { border-color: var(--graphite); border-width: 5px; }
.opc input:focus-visible + label { box-shadow: inset 0 0 0 2px var(--yellow); }
.opc label em { font-family: var(--mono); font-style: normal; font-size: .76rem; color: var(--muted); }

/* ── Cuántos obturadores quedan ──────────────────────────────
   Al costado de cada diámetro, contra el borde: es lo que se mira antes de
   tocar. «Sin stock» va en ámbar y no en rojo —se puede cargar igual, es un
   aviso y no un error—, el mismo de las advertencias del módulo. */
.opc label .stock {
  margin-left: auto; flex-shrink: 0; white-space: nowrap;
  font-family: var(--mono); font-size: .8rem; font-weight: 700; color: var(--muted-2);
}
.opc label .stock.sin {
  color: #7a5b00; background: #fbf3d8; border: 1px solid var(--warn);
  border-radius: 999px; padding: 2px 9px;
}
.aviso-stock {
  margin-top: 10px; padding: 10px 12px; text-align: left;
  background: #fbf3d8; border: 1px solid var(--warn); border-left-width: 4px;
  border-radius: var(--radius-sm); color: #7a5b00; font-size: .86rem; line-height: 1.45;
}
.aviso-stock b { display: block; color: #5c4400; margin-bottom: 2px; }
.listo-stock { margin: -4px 0 14px; color: var(--muted-2); font-size: .86rem; line-height: 1.45; }
.listo-stock.aviso-stock { margin-top: 0; }

.ayuda {
  flex-shrink: 0; width: 30px; height: 30px; margin-right: 11px; cursor: pointer;
  border-radius: 50%; border: 1.5px solid var(--line-soft); background: #fff;
  font-weight: 800; font-size: .82rem; color: var(--muted-2);
}
.ayuda.on { background: var(--graphite); color: #fff; border-color: var(--graphite); }
.opc-ayuda {
  margin: 0; padding: 0 14px 13px 46px; font-size: .84rem;
  color: var(--muted-2); background: #fffdf6; line-height: 1.45;
}

/* ── Buscador de empresa ──────────────────────────────────────
   El campo y sus resultados tienen que leerse como una sola cosa. Separados
   —el campo arriba, una caja con bordes propios más abajo— una empresa
   encontrada parece otro campo del formulario en vez de la respuesta a lo que
   se buscó. Por eso la lista sale pegada al campo, sin margen, compartiendo
   borde: es el desplegable del campo, no un vecino. */
.busq { margin-bottom: 8px; }
.buscador { position: relative; }
.buscador .busq { margin-bottom: 0; }

/* Abierto, los dos se funden: el campo pierde las esquinas de abajo, la lista
   pierde las de arriba y su borde superior, y quedan como una pieza. */
.buscador.abierto .busq { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.buscador.abierto .opc-lista {
  border-top: none; border-top-left-radius: 0; border-top-right-radius: 0;
  border-color: var(--graphite);
}
.buscador.abierto .busq { border-color: var(--graphite); }

/* Qué se está mirando: "27 empresas · escribí para filtrar", o cuántas
   quedaron al filtrar. Es lo que convierte una lista de nombres en el
   resultado visible de una búsqueda. */
.opc-lista-nota {
  margin: 0; padding: 8px 14px; background: var(--cement);
  font-size: .72rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--muted); position: sticky; top: 0;
}

/* La lista de empresas crece: se le pone techo y se navega adentro, para que
   no empuje el resto del paso fuera de la pantalla. */
.opc-lista.scroll { max-height: 46vh; overflow-y: auto; -webkit-overflow-scrolling: touch; }
/* Va con las dos clases —`.btn.otra-empresa`— y no con una sola, y eso no es
   capricho: `.btn` está definida más abajo en este archivo y con un selector
   igual de específico, así que le ganaba por orden y le devolvía las
   mayúsculas. El comentario de acá decía "sin mayúsculas" mientras la pantalla
   mostraba "+ LA EMPRESA NO ESTÁ EN LA LISTA" partido en dos renglones. */
.btn.otra-empresa {
  width: 100%; margin-top: 10px; text-align: center;
  border: 1.5px dashed var(--line-soft); border-radius: var(--radius-sm);
  color: var(--muted-2); font-size: .86rem; font-weight: 600;
  /* Es una frase, no un rótulo: en mayúsculas no entra en un renglón. */
  text-transform: none; letter-spacing: 0;
}
.btn.otra-empresa:hover { border-color: var(--yellow); background: #fffdf6; color: var(--graphite); }
.opc-btn {
  display: block; width: 100%; text-align: left; cursor: pointer; font: inherit;
  background: #fff; border: none; border-top: 1px solid var(--cement); padding: 12px 14px;
}
.opc-lista .opc-btn:first-child { border-top: none; }
.opc-btn:hover { background: #fffdf6; }
.opc-btn b { display: block; font-size: .92rem; }
.opc-btn span { display: block; font-size: .76rem; color: var(--muted); margin-top: 2px; }
.opc-btn.nueva b { color: var(--warn); }

/* ── Los tres modelos de obturador, en fila ──────────────────
   ULK, U y UK son pocos y cortos, así que entran los tres a lo ancho de un
   celular sin apretarse. En columna —como las diez medidas— ocuparían tres
   filas para decir tres palabras, y el diámetro, que es el campo de al lado,
   quedaría fuera de la pantalla.

   La foto es lo que de verdad los distingue: con guantes y apurado, reconocer
   el obturador por la forma es inmediato, y «U» contra «UK» contra «ULK» leído
   rápido es justo lo que se elige mal. */
.opc-fila { display: flex; gap: 8px; }
.opc-modelo { flex: 1; }
.opc-modelo input { position: absolute; opacity: 0; pointer-events: none; }
.opc-modelo label {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 8px; min-height: 96px; padding: 10px 6px; cursor: pointer;
  border: 1px solid var(--line-soft); border-radius: var(--radius); background: #fff;
}

/* > ⚠️ **Las tres fotos vienen con fondo blanco y en proporciones distintas**
   > —la U es vertical, las otras dos apaisadas—, así que puestas al aire se
   > verían de tamaños dispares y, con el botón elegido en acero, dejarían un
   > rectángulo blanco flotando encima.
   >
   > Por eso la foto va en **su propio recuadro**, del mismo tamaño para las
   > tres: el blanco pasa a ser parte del diseño en vez de un resto de la imagen,
   > y `object-fit: contain` deja que cada una se acomode adentro sin recortarse
   > ni estirarse. Recortar los fondos a mano habría sido más prolijo todavía,
   > pero son las fotos del fabricante y retocarlas es empezar a mantener una
   > versión propia de algo que UPM puede cambiar. */
.opc-modelo .foto {
  width: 100%; height: 46px; background: #fff; border-radius: var(--radius-sm);
  display: flex; align-items: center; justify-content: center; overflow: hidden;
}
.opc-modelo img { max-width: 100%; max-height: 100%; object-fit: contain; display: block; }
.opc-modelo label b { font-family: var(--mono); font-size: .92rem; letter-spacing: .04em; }
.opc-modelo label:hover { border-color: var(--obt); }
/* El acero del módulo, el mismo de la barra de estas pantallas. */
.opc-modelo input:checked + label { background: var(--obt); border-color: var(--obt); }
.opc-modelo input:checked + label b { color: #fff; }
.opc-modelo input:focus-visible + label { outline: 2px solid var(--yellow); outline-offset: 2px; }

/* Lo ya elegido, con el contratista que se deriva solo */
.elegida {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  background: var(--surface); border: 1.5px solid var(--graphite);
  border-radius: var(--radius-sm); padding: 12px 14px;
}
.elegida > div { flex: 1; min-width: 140px; }
.elegida b { display: block; font-size: .96rem; }
.elegida span { display: block; font-size: .78rem; color: var(--muted); margin-top: 2px; }

/* Selección múltiple */
.chips { display: flex; flex-wrap: wrap; gap: 8px; }
.chip {
  cursor: pointer; font: inherit; font-size: .84rem; font-weight: 600;
  background: #fff; color: var(--muted-2); border: 1.5px solid var(--line-soft);
  border-radius: 999px; padding: 10px 15px; min-height: 44px; transition: all .12s;
}
.chip:hover { border-color: var(--muted); }
.chip.on { background: var(--graphite); color: #fff; border-color: var(--graphite); }

/* Ayudas del paso */
.campo-nota { margin: -2px 0 12px; color: var(--muted); font-size: .82rem; line-height: 1.45; }
.sub-campo { margin-bottom: 14px; }
.sub-campo:last-child { margin-bottom: 0; }
.sub-campo > label {
  display: block; font-size: .72rem; font-weight: 700; color: var(--muted-2);
  margin-bottom: 8px; text-transform: uppercase; letter-spacing: .06em;
}

/* Fotos */
.file-drop {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  border: 2px dashed var(--line-soft);
  border-radius: var(--radius);
  padding: 22px 16px;
  text-align: center;
  color: var(--muted);
  cursor: pointer;
  background: #f4f2ec;
  transition: all .12s;
}
.file-drop:hover { border-color: var(--yellow); background: #fbf3d8; color: var(--graphite); }
.file-drop .big { font-size: 1.6rem; font-weight: 800; color: var(--muted-2); }
.file-drop strong { text-transform: uppercase; letter-spacing: .05em; font-size: .82rem; }
/* Reportes que llegaron después del cierre de su día. Ámbar: acá sí hay algo
   que decidir —si se vuelve a emitir el documento de ese día—, y esa decisión
   es del líder. */
.aviso-tardios {
  background: #fbf3d8; border: 1px solid var(--warn);
  border-radius: var(--radius-sm); padding: 11px 13px; margin-bottom: 12px;
  font-size: .82rem; line-height: 1.5; color: #7a5b00;
}
.aviso-tardios b { color: var(--graphite); }
.tag.tarde { background: var(--warn); color: var(--graphite); }

/* Lo que ya está terminado y espera señal. Verde y no ámbar: no hay nada roto
   ni nada que decidir —el reporte está guardado y va a subir solo—, es
   información. El ámbar queda para lo que pide una decisión. */
.espera-aviso {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  background: #e6f2ee; border: 1.5px solid var(--obs);
  border-radius: var(--radius); padding: 12px 14px; margin-bottom: 14px;
}
.espera-texto { flex: 1; min-width: 180px; font-size: .84rem; line-height: 1.5; color: #14503f; }
.espera-texto b { color: var(--graphite); }
.espera-aviso .btn { min-height: 42px; }
/* La tarjeta de un reporte que todavía no subió: el mismo lenguaje que las
   demás, para que se lea igual, con el borde punteado que dice que no está
   cerrado del todo. */
.rep-espera { border-style: dashed; }
.tag.espera { background: var(--obs); color: #fff; }
/* El que quedó a medias pide algo —terminarlo—, así que se distingue del que
   solo espera señal: ámbar, como todo lo que necesita una decisión. */
.tag.medias { background: var(--warn); color: var(--graphite); }
.rep-medias { border-left-color: var(--warn) !important; }
/* El que quedó sin dueño tampoco «espera señal»: espera que alguien diga que es
   suyo, y hasta que eso pase no llega a ninguna parte. Ámbar como el de arriba,
   porque pide una decisión, y con el motivo escrito donde va el código —que es
   lo que este no tiene—. */
.tag.huerfano { background: var(--warn); color: var(--graphite); }
.rep-huerfano { border-left-color: var(--warn) !important; }
.rep-huerfano .rep-cod {
  font-family: inherit; font-size: .78rem; font-weight: 600; letter-spacing: 0;
  color: #7a5b00; text-transform: none; line-height: 1.45;
}
.espera-error {
  margin-top: 9px; font-size: .78rem; color: var(--inc); font-weight: 600;
}

/* Lo que quedó a medias, ofrecido al volver. Va arriba de todo y con el
   amarillo de seguridad: no es un error —no se perdió nada—, es algo que hay
   que decidir antes de empezar a cargar otra cosa. */
.borrador-aviso {
  background: #fbf3d8; border: 1.5px solid var(--warn);
  border-radius: var(--radius); padding: 13px 14px; margin-bottom: 16px;
}
.borrador-texto { font-size: .86rem; line-height: 1.5; color: #7a5b00; }
.borrador-texto b { color: var(--graphite); }
.borrador-acciones { display: flex; gap: 8px; margin-top: 11px; flex-wrap: wrap; }
.borrador-acciones .btn { flex: 1; min-width: 130px; min-height: 44px; }

/* La galería, debajo de la cámara: son dos caminos al mismo lugar, pero el
   normal es sacar la foto ahí mismo. Va como botón y no como otro recuadro
   punteado para que se lea la jerarquía de un vistazo. */
.foto-galeria { margin-top: 9px; min-height: 46px; font-size: .8rem; }
.foto-ayuda {
  margin: 10px 0 0; font-size: .78rem; line-height: 1.45; color: var(--muted);
}
.foto-ayuda b { color: var(--muted-2); }
/* El aviso de estar adentro de otra app. Amarillo de seguridad: no es un error
   —todo funciona menos la cámara—, es algo que conviene saber antes. */
.aviso-webview {
  margin-bottom: 11px; padding: 11px 13px; border-radius: var(--radius-sm);
  background: #fbf3d8; border: 1px solid var(--warn);
  font-size: .82rem; line-height: 1.5; color: #7a5b00;
}
.aviso-webview b { color: var(--graphite); }

.thumbs { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 12px; }
.thumb {
  position: relative; width: 78px; height: 78px; border-radius: var(--radius-sm);
  overflow: hidden; border: 1px solid var(--graphite); background: #e2e0d8;
}
.thumb img { width: 100%; height: 100%; object-fit: cover; }
.fotos-info { margin-top: 10px; font-size: .76rem; color: var(--muted-2); font-family: var(--mono); }
.thumb button {
  position: absolute; top: 3px; right: 3px; width: 22px; height: 22px;
  border: none; border-radius: 50%; background: var(--graphite); color: #fff;
  cursor: pointer; font-size: .8rem; line-height: 1; display: grid; place-items: center;
}

/* Botones */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  font: inherit; font-weight: 800; cursor: pointer;
  padding: 14px 20px; border-radius: var(--radius-sm); min-height: 50px;
  background: var(--yellow); color: var(--graphite); border: 1.5px solid var(--graphite);
  width: 100%; text-transform: uppercase; letter-spacing: .05em;
  transition: transform .05s, background .12s;
}
.btn:hover { background: var(--yellow-dk); }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: .55; cursor: not-allowed; }
.btn.secondary { background: #fff; color: var(--graphite); }
.btn.secondary:hover { background: #f4f2ec; }
.btn.ghost { background: transparent; color: var(--muted-2); border: none; width: auto; padding: 8px 12px; min-height: 0; letter-spacing: .04em; }
.btn.ghost:hover { background: #e5e3db; }
.btn.small { width: auto; padding: 9px 14px; min-height: 0; font-size: .76rem; }
.btn.danger { background: var(--inc); color: #fff; }
.btn.danger:hover { background: #b32d20; }

/* Mensajes */
.toast {
  position: fixed; left: 50%;
  /* Se apoya abajo, así que sube lo que mida la franja del iPhone. */
  bottom: calc(22px + var(--safe-bottom));
  /* Escondido, baja todo lo que subió y un poco más, y además no se ve.
     > ⚠️ **Estaba en `translateY(140%)`**, que lo baja un 40% de su propio alto
     > por debajo de donde se apoya: solo alcanza si mide más de 55 px. Vacío mide
     > 26, así que **asomaba 12 px en todas las pantallas** —un rectángulo negro
     > con el borde amarillo, abajo al centro—. Y cuando el aviso subió para
     > esquivar la franja del iPhone, el escondite no subió con él: en un iPhone
     > con barra de inicio quedaba **entero a la vista**. Ahora se corre lo que
     > hace falta según dónde se apoya, y `visibility` lo cubre aunque algún día
     > cambie el alto. */
  transform: translateX(-50%) translateY(calc(100% + 30px + var(--safe-bottom)));
  visibility: hidden;
  background: var(--graphite); color: #fff; padding: 13px 20px; border-radius: var(--radius-sm);
  border-left: 4px solid var(--yellow);
  font-weight: 700; font-size: .85rem; text-transform: uppercase; letter-spacing: .04em;
  /* Por encima del velo (200), no debajo: lo que el servidor contesta con un
     modal abierto —«se te adelantó Carla», «sin señal»— tiene que verse. */
  /* `max-content` y no el ancho que le toque: con `left: 50%` el navegador le
     deja **la mitad derecha de la pantalla** para estirarse, así que un aviso
     largo salía en una columna de 190 px y seis renglones aunque el tope dijera
     90%. Ahora ocupa lo que necesita, hasta el tope. */
  width: max-content;
  z-index: 300; max-width: min(90vw, 440px);
  /* Al irse, `visibility` espera a que termine de bajar: si cambiara de golpe,
     desaparecería en el lugar en vez de deslizarse. */
  transition: transform .3s cubic-bezier(.2,.8,.2,1), visibility 0s linear .3s;
}

/* Un aviso que nombra a una persona se escribe como se escribe un nombre.
   > En mayúsculas el nombre propio **pierde su forma** —«AGREGASTE A PRUEBA SIN
   > CÉDULA»— y justamente el nombre es el dato: confirma que se tocó al que se
   > quería y no al de al lado, que está a un dedo de distancia. */
.toast.con-nombre { text-transform: none; letter-spacing: .01em; font-size: .9rem; }
.toast.show {
  transform: translateX(-50%) translateY(0); visibility: visible;
  transition: transform .3s cubic-bezier(.2,.8,.2,1);
}
.toast.ok  { border-left-color: var(--obs); }
.toast.err { border-left-color: var(--inc); }

.alert {
  padding: 14px 16px; border-radius: var(--radius); font-size: .9rem;
  background: #fbf3d8; color: #7a5b00; border: 1px solid var(--warn); margin-bottom: 16px;
}
.alert.error { background: #fbe7e4; color: #8a1f16; border-color: var(--inc); }

/* ── Landing ─────────────────────────────────────────────── */
.hero { padding: 26px 0 12px; }
.hero h1 { font-size: 1.35rem; margin: 0 0 4px; text-transform: uppercase; letter-spacing: .03em; }
.hero p { color: var(--muted); margin: 0; }
.tiles { display: grid; gap: 12px; margin-top: 8px; }
.tile {
  display: flex; align-items: center; gap: 16px; text-decoration: none; color: inherit;
  background: var(--surface); border: 1px solid var(--graphite); border-radius: var(--radius);
  padding: 18px; transition: border-color .12s, background .12s;
}
.tile:hover { border-color: var(--yellow); background: #fffdf6; }
.tile .ic {
  width: 48px; height: 48px; border-radius: var(--radius-sm); display: grid; place-items: center;
  font-size: 1.5rem; font-weight: 800; flex-shrink: 0; background: var(--graphite); color: var(--yellow);
}
.tile h3 { margin: 0 0 3px; font-size: .95rem; text-transform: uppercase; letter-spacing: .04em; }
.tile p { margin: 0; color: var(--muted); font-size: .85rem; }

/* ── Dashboard ───────────────────────────────────────────── */
/* Los KPIs ya no son cuatro fijos: que se acomoden solos según entren. */
.kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(132px, 1fr)); gap: 12px; margin-bottom: 16px; }
.kpi {
  background: var(--surface); border: 1px solid var(--graphite); border-radius: var(--radius);
  padding: 15px 16px;
}
.kpi .num { font-family: var(--mono); font-size: 1.9rem; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; }
.kpi .lbl { font-size: .66rem; color: var(--muted-2); margin-top: 8px; font-weight: 700; text-transform: uppercase; letter-spacing: .09em; }
.kpi.inc { border-color: var(--inc); }
.kpi.inc .num { color: var(--inc); }
.kpi.obs .num { color: var(--obs); }

.filters { display: grid; grid-template-columns: repeat(auto-fit, minmax(132px, 1fr)); gap: 10px; }
.filters .full { grid-column: 1 / -1; }
.filters label { font-size: .64rem; font-weight: 700; color: var(--muted-2); display: block; margin-bottom: 5px; text-transform: uppercase; letter-spacing: .07em; }
.filters input, .filters select { min-height: 42px; padding: 8px 11px; font-size: .86rem; }

.charts { display: grid; grid-template-columns: 1fr; gap: 14px; }
.chart-card { background: var(--surface); border: 1px solid var(--graphite); border-radius: var(--radius); padding: 16px; }
.chart-card h3 { margin: 0 0 12px; font-size: .68rem; color: var(--muted-2); text-transform: uppercase; letter-spacing: .1em; }
.chart-wrap { position: relative; height: 220px; }

/* Los de etiquetas largas (causas, días) piden el ancho completo. */
.charts .chart-card.wide { grid-column: 1 / -1; }
.chart-card.wide .chart-wrap { height: 260px; }

/* Los cinco gráficos que no se ven de entrada, y el botón que los trae.
   El botón es una fila entera y no un enlace chiquito al costado: es la única
   forma de que se toque con el pulgar, con guantes y sin apuntar. */
.ver-mas-graficos, .ver-reportes {
  width: 100%; margin-top: 14px; min-height: 46px;
  display: flex; align-items: center; justify-content: center; gap: 8px;
}
.ver-mas-graficos .vmg-flecha, .ver-reportes .vmg-flecha { transition: transform .18s ease; font-size: .9em; }
.ver-mas-graficos.abierto .vmg-flecha, .ver-reportes.abierto .vmg-flecha { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) { .ver-mas-graficos .vmg-flecha, .ver-reportes .vmg-flecha { transition: none; } }
.charts.mas { margin-top: 14px; }

/* El contratista de cada persona, en su fila de Personas. Va en la fila y no
   detrás de un botón: es el dato que hay que poder revisar de un vistazo para
   todo el equipo junto. */
.cat-contratista { min-height: 34px; padding: 4px 8px; font-size: .78rem; max-width: 150px; }

/* El contratista que ya viene puesto porque es el de quien reporta. Se muestra
   como dato, no como pregunta: no hay nada que elegir. */
.contratista-fijo { background: var(--cement); }

/* Quién cargó cada reporte, al pie de la tarjeta. Estuvo arriba, entre las
   etiquetas, y ahí se mezclaba con el área, la empresa y el estándar —que son
   datos del trabajo— compitiendo por lugar y por lectura. */
.rep-quien { font-size: .74rem; color: var(--muted); margin-right: auto; }
.rep-quien b { color: var(--muted-2); font-weight: 700; }

/* Sin reportes para el período: el botón lo dice y no se puede tocar. */
.ver-reportes[disabled] { opacity: .55; cursor: default; }
.ver-reportes[disabled] .vmg-flecha { display: none; }
/* El título de adentro repite el número, pero acá ya no molesta: se llega
   habiéndolo abierto a propósito. */
#seccionReportes .section-title { margin-top: 16px; }

/* Aclaraciones al costado del título: de qué es cada color, sobre qué se cuenta. */
.ch-nota { font-weight: 400; letter-spacing: .02em; text-transform: none; color: var(--muted); }
.ch-nota .pt { display: inline-block; width: 9px; height: 9px; border-radius: 2px; vertical-align: baseline; }
.ch-nota .pt.inc { background: var(--inc); }
.ch-nota .pt.am { background: var(--yellow); }

/* Un gráfico sin datos no se deja vacío: lo dice. */
.chart-wrap.sin-datos::after {
  content: 'Sin datos para estos filtros';
  position: absolute; inset: 0; display: grid; place-items: center;
  background: var(--surface); color: var(--muted); font-size: .8rem;
}

/* ── Silueta del cuerpo ──────────────────────────────────── */
.silueta { display: flex; gap: 20px; flex-wrap: wrap; align-items: flex-start; }
.sil-dibujo { flex: 0 0 auto; width: 190px; }
.sil-dibujo svg { width: 100%; height: auto; display: block; }
/* El contorno se dibuja siempre, aunque la zona no tenga casos: la figura
   tiene que leerse como un cuerpo, no como manchas sueltas.
   El relleno NO se define acá: lo pone el SVG según los datos, y una regla de
   CSS le ganaría al atributo del dibujo. */
.sil-dibujo svg [data-parte] { stroke: var(--graphite); stroke-width: 1.2; stroke-linejoin: round; }
.sil-dibujo svg [data-parte]:hover { stroke-width: 2; }

.sil-escala { display: flex; align-items: center; gap: 3px; margin-top: 10px; justify-content: center; }
.sil-escala i { width: 17px; height: 9px; border: 1px solid var(--line-soft); }
.sil-escala span { font-size: .6rem; color: var(--muted); text-transform: uppercase; letter-spacing: .06em; }

.sil-datos { flex: 1; min-width: 190px; }
.sil-lista { list-style: none; margin: 0; padding: 0; }
.sil-lista li { display: flex; align-items: center; gap: 8px; padding: 5px 0; font-size: .84rem; border-bottom: 1px solid var(--cement); }
.sil-lista li.cero { color: var(--muted); }
.sil-lista i { width: 12px; height: 12px; border: 1px solid var(--line-soft); border-radius: 2px; flex-shrink: 0; }
.sil-nom { flex: 1; }
.sil-n { font-family: var(--mono); font-weight: 700; font-variant-numeric: tabular-nums; }

/* Múltiples, lesiones internas y otras: no son un lugar del cuerpo, así que
   se cuentan al costado en vez de pintarse sobre el dibujo. */
.sil-aparte { margin-top: 14px; padding-top: 12px; border-top: 1px dashed var(--line-soft); }
.sil-aparte h4 {
  margin: 0 0 6px; font-size: .62rem; text-transform: uppercase;
  letter-spacing: .07em; color: var(--muted); font-weight: 700;
}
.silueta-vacia { color: var(--muted); font-size: .86rem; padding: 20px 0; text-align: center; }
.sil-nota {
  margin: 0 0 10px; font-size: .8rem; color: #7a5b00; line-height: 1.45;
  background: #fbf3d8; border: 1px solid var(--warn); border-radius: var(--radius-sm); padding: 8px 10px;
}

/* Filtro por tipo (historial propio): botones con la cuenta de cada uno. */
.filtro-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.filtro-chips .chip { min-height: 40px; padding: 8px 14px; }
.filtro-chips .chip b {
  font-family: var(--mono); font-size: .78rem; margin-left: 5px;
  opacity: .65; font-variant-numeric: tabular-nums;
}
.filtro-chips .chip.on b { opacity: .85; }

/* Listado de reportes */
/* Completar el contratista de varios reportes de una. Va pegado a la lista y
   no en una ventana aparte: lo que se ve abajo es exactamente lo que se toca. */
.completar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  background: var(--surface); border: 1px solid var(--graphite);
  border-left: 5px solid var(--warn); border-radius: var(--radius);
  padding: 12px 14px; margin-bottom: 12px;
}
.completar > div { flex: 1; min-width: 150px; }
.completar b { display: block; font-size: .94rem; }
.completar span { font-size: .82rem; color: var(--muted); }
.completar select { max-width: 200px; }

.rep-list { display: grid; gap: 12px; }
.rep {
  background: var(--surface); border: 1px solid var(--graphite); border-radius: var(--radius);
  padding: 14px; border-left: 6px solid var(--obs);
}
.rep.inc { border-left-color: var(--inc); }
.rep.reu { border-left-color: var(--graphite); }
/* Como en el documento del día (`.doc-item.act`). Sin esto la tarjeta de una
   actividad preventiva se quedaba con el verde de base, el de la observación. */
.rep.act { border-left-color: var(--muted); }
/* El código es como se llama el reporte en la conversación con UPM. */
.rep-cod { font-family: var(--mono); font-size: .72rem; font-weight: 700; color: var(--muted-2); margin-bottom: 6px; }

/* Registro interno: el número que carga el cliente, no nosotros. */
.reg-int {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  margin-top: 11px; padding-top: 10px; border-top: 1px dashed var(--line-soft);
}
.reg-lbl {
  font-size: .64rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--muted);
}
.reg-val { font-family: var(--mono); font-size: .82rem; font-weight: 700; }
.reg-val.vacio { font-weight: 400; font-style: italic; color: var(--muted); }
.reg-input { flex: 1; min-width: 120px; max-width: 200px; min-height: 38px; padding: 7px 10px; font-family: var(--mono); font-size: .84rem; }
.reg-int .btn.small { min-height: 38px; }
.rep-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 9px; }
.tag { font-size: .64rem; font-weight: 700; padding: 3px 9px; border-radius: var(--radius-sm); text-transform: uppercase; letter-spacing: .05em; }
.tag.obs { background: var(--obs); color: #fff; }
.tag.inc { background: var(--inc); color: #fff; }
.tag.reu { background: var(--graphite); color: #fff; }
/* La actividad preventiva, en la misma familia que la reunión pero en contorno:
   el grafito sólido ya está tomado, y los tres colores de estado —rojo, verde,
   amarillo— dicen gravedad, que una charla de seguridad no tiene. */
.tag.act { background: #fff; color: var(--graphite); box-shadow: inset 0 0 0 1px var(--graphite); }
.tag.soft { background: #e5e3db; color: var(--muted-2); border: 1px solid var(--line-soft); }
.rep-time { font-size: .72rem; color: var(--muted); margin-left: auto; font-family: var(--mono); }
.rep-body { font-size: .9rem; color: #24272c; }
.rep-body .row { display: flex; gap: 8px; margin-top: 5px; }
/* Los participantes de una reunión, uno por renglón y numerados, como en el
   acta (vista-reporte.js). Vale para el tablero, «Reportes de…» y el reporte
   del día. */
.row ol.renglones { flex: 1; min-width: 0; margin: 0; padding-left: 1.6em; }
.row ol.renglones li { margin: 0 0 2px; }
.rep-body .k { color: var(--muted-2); min-width: 108px; font-size: .66rem; text-transform: uppercase; letter-spacing: .06em; font-weight: 700; padding-top: 2px; }
/* «Ver detalle» en Mis reportes: el reporte entero adentro de la tarjeta.
   El resumen se va cuando el detalle se abre —la descripción está en los dos—
   y así la lista se sigue recorriendo con el pulgar. */
.rep-det > summary { list-style: none; cursor: pointer; }
.rep-det > summary::-webkit-details-marker { display: none; }
.rep-resumen { display: block; font-size: .9rem; color: #24272c; margin-bottom: 11px; }
.rep-det[open] .rep-resumen { display: none; }

/* Una barra de ancho completo, y no un texto con una flechita: escrito chico y
   en gris no se leía como algo que se toca. No es un `.btn` porque la tarjeta ya
   tiene dos —Editar y Eliminar— y un tercero igual competiría con ellos; este
   abre algo, no hace nada. El objetivo táctil es toda la barra. */
.rep-ver {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  min-height: 44px; padding: 10px 12px;
  background: #f4f2ec; border: 1.5px solid var(--line-soft);
  border-radius: var(--radius-sm);
  font-size: .74rem; font-weight: 800; text-transform: uppercase;
  letter-spacing: .06em; color: var(--graphite);
  transition: background .12s, border-color .12s;
}
.rep-det > summary:hover .rep-ver { background: #fffdf6; border-color: var(--yellow); }
/* El chevron, en un círculo oscuro: es lo que dice de un vistazo que esto se
   despliega, y sobrevive a mirar la pantalla al sol y de reojo. */
.rep-ver::after {
  content: '▾'; flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; border-radius: 50%;
  background: var(--graphite); color: #fff;
  font-size: .8rem; line-height: 1; padding-bottom: 2px;
}
.rep-det[open] .rep-ver::after { content: '▴'; padding: 2px 0 0; }
.rep-det > .rep-body { margin-top: 11px; }

.rep-fotos { display: flex; gap: 8px; margin-top: 11px; flex-wrap: wrap; }
.rep-fotos a { display: block; width: 62px; height: 62px; border-radius: var(--radius-sm); overflow: hidden; border: 1px solid var(--graphite); }
.rep-fotos img { width: 100%; height: 100%; object-fit: cover; }
/* El pie de una tarjeta: quién la cargó a la izquierda y qué se puede hacer a
   la derecha. En un celular angosto la línea envuelve sola. */
.rep-actions { margin-top: 11px; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }

/* El pie de la lista: qué se está viendo de cuánto, y cómo traer el resto. */
.lista-pie {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  flex-wrap: wrap; margin-top: 14px; padding: 12px 14px;
  background: var(--surface); border: 1px dashed var(--line-soft);
  border-radius: var(--radius); font-size: .82rem; color: var(--muted-2);
}
.lista-pie .btn { min-height: 42px; }

.empty { text-align: center; color: var(--muted); padding: 40px 20px; border: 1px dashed var(--line-soft); border-radius: var(--radius); }
.empty .big { font-size: 1.4rem; font-weight: 800; color: var(--muted-2); text-transform: uppercase; letter-spacing: .06em; }

.section-title { display: flex; align-items: center; gap: 10px; margin: 22px 0 12px; font-size: .82rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; }
.section-title .count { font-size: .74rem; color: var(--muted); font-weight: 700; font-family: var(--mono); }

.live-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--yellow); box-shadow: 0 0 0 0 rgba(244,180,0,.6); animation: pulse 2s infinite; display: inline-block; }
@keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(244,180,0,.5); } 70% { box-shadow: 0 0 0 7px rgba(244,180,0,0); } 100% { box-shadow: 0 0 0 0 rgba(244,180,0,0); } }

@media (prefers-reduced-motion: reduce) { .live-dot { animation: none; } }
/* Sin señal el tablero sigue mostrando lo último que llegó: el punto se apaga
   para que «en vivo» no diga algo que en ese momento no es cierto. */
.live-dot.apagado { background: var(--muted); animation: none; box-shadow: none; }

/* ── Responsive (desktop) ────────────────────────────────── */
/* > ⚠️ **Los grid con gráficos van con `minmax(0, …)`, nunca con `1fr` pelado.**
   Un `1fr` no baja del ancho mínimo de su contenido, y el canvas de Chart.js
   tiene uno: con la columna del menú ocupando 248 px, las tarjetas de la
   derecha del tablero se salían de la pantalla entre 1024 y 1440 px de ancho.
   Estaba escrito así desde antes y no se veía porque sobraba lugar —el mismo
   error ya estaba corregido en `.doc-charts`, con esta misma nota—. */
@media (min-width: 720px) {
  .charts { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .tiles { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}
@media (min-width: 980px) {
  .dash-grid { display: grid; grid-template-columns: 320px minmax(0, 1fr); gap: 16px; align-items: start; }
  .dash-grid .side { position: sticky; top: 80px; }
}

/* ── Informes: la pantalla para elegir ───────────────────── */
/* Una sola tarjeta con tres grupos separados por una línea, y no tres tarjetas:
   en el celular entra casi entera en una pantalla, que es donde se arma. */
.inf-intro { margin: 0 0 12px; font-size: .9rem; color: var(--muted-2); line-height: 1.35; }
.inf {
  background: var(--surface); border: 1px solid var(--graphite); border-radius: var(--radius);
  padding: 14px; margin-bottom: 14px;
}
.inf-grupo + .inf-grupo { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--cement); }
.inf-grupo > h2, .inf .casillas legend {
  margin: 0 0 8px; padding: 0; font-size: .64rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .07em; color: var(--muted-2);
}
/* Los atajos son los del período del tablero, a lo ancho. */
.periodo-atajos.inf-atajos .chip { flex: 1; padding: 8px 4px; }
.inf-fechas { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; margin-top: 10px; }
.inf-fechas label {
  display: block; font-size: .64rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .07em; color: var(--muted-2);
}
.inf-fechas input { display: block; width: 100%; margin-top: 4px; min-height: 44px; }
/* Lo que va adentro de cada reporte, colgado del detalle con una línea: sin el
   detalle se apaga, y la línea dice de qué depende. */
.inf-dentro { margin: 2px 0 0 12px; padding-left: 10px; border-left: 2px solid var(--cement); }
.inf-dentro > p { margin: 0 0 6px; font-size: .74rem; color: var(--muted); }
.inf-dentro.apagado > p, .inf-dentro.apagado label { opacity: .45; }
/* Con lugar, de a cuatro, como los tipos del reporte del día. */
@media (min-width: 760px) {
  .inf #tipos, .inf-dentro > .casillas { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
.inf-nota { margin: 2px 0 0; font-size: .8rem; color: var(--inc); font-weight: 600; }
.inf-cuenta { margin: 0 0 8px; text-align: center; font-size: .84rem; color: var(--muted-2); }
.inf-cuenta b { color: var(--graphite); }
.inf-aviso { margin: 0 0 10px; }

/* ── Reporte del día (documento imprimible) ──────────────── */
.toolbar {
  display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap;
  background: var(--surface); border: 1px solid var(--graphite); border-radius: var(--radius);
  padding: 14px; margin-bottom: 16px;
}
.toolbar .tb-field label { display: block; font-size: .64rem; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; color: var(--muted-2); margin-bottom: 5px; }
.toolbar .tb-field input { min-height: 44px; }
.toolbar .tb-spacer { flex: 1; }
.toolbar .btn { width: auto; }
/* Las casillas: una grilla de recuadros iguales, de dos en el celular, con la
   cuenta a la derecha. Las usan los tipos del reporte del día —ahí, en fila
   cuando hay lugar— y la pantalla de Informes.
   > ⚠️ Fueron primero botones redondos y oscuros, sueltos: en el celular
   > quedaban tres renglones desparejos —«Actividades preventivas» sola en el
   > suyo— y, con todo tildado, cuatro manchas negras que le robaban la vista al
   > documento («quedó feísimo», 16/10/2026). Ahora es liviano: tildado lleva
   > borde y una tilde chica; destildado, texto apagado y borde punteado. */
.casillas {
  border: none; margin: 0; padding: 0; min-width: 0;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px;
}
.toolbar .tb-tipos { flex-basis: 100%; }
@media (min-width: 760px) { .toolbar .tb-tipos { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.casillas legend { padding: 0; font-size: .64rem; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; color: var(--muted-2); margin-bottom: 5px; }
.casillas .tb-nota, .casillas .inf-nota, .casillas .ancho { grid-column: 1 / -1; }
/* La casilla de verdad cubre el recuadro entero y no se ve: así se toca en todo
   el recuadro (44 px; movil.mjs mide 40 como mínimo) y no en un cuadradito. */
.casillas label {
  position: relative; display: flex; align-items: center; gap: 8px; min-height: 44px;
  padding: 6px 10px; border: 1px solid var(--graphite); border-radius: 8px;
  font-size: .8rem; line-height: 1.15; cursor: pointer; background: var(--surface); color: var(--graphite);
}
.casillas input {
  position: absolute; inset: 0; width: 100%; height: 100%; margin: 0;
  opacity: 0; cursor: pointer;
}
.casillas label::before {
  content: ''; flex: none; width: 15px; height: 15px; border: 1.5px solid #9aa0a6;
  border-radius: 4px; display: grid; place-items: center; font-size: .62rem; line-height: 1;
}
.casillas label:has(input:checked)::before { content: '✓'; background: var(--graphite); color: #fff; border-color: var(--graphite); font-weight: 800; }
.casillas label:not(:has(input:checked)) { color: var(--muted); border: 1px dashed #b9bcc2; background: transparent; }
.casillas label:has(input:focus-visible) { outline: 3px solid var(--yellow); outline-offset: 2px; }
/* Apagada: no se puede tocar, pero se ve cómo quedó, porque vuelve así. */
.casillas label:has(input:disabled), .casillas label:has(input:disabled) input { cursor: not-allowed; }
.casillas b { margin-left: auto; color: var(--muted-2); font-family: var(--mono); font-size: .78rem; }
.toolbar .tb-nota { margin: 2px 0 0; font-size: .8rem; color: var(--inc); font-weight: 600; }
/* Cómo van las fotos: «Preparando fotos 3 de 12…». En ámbar si alguna no llegó,
   que es algo que hay que mirar antes de descargar. */
.toolbar .tb-estado { flex-basis: 100%; margin: 0; font-size: .82rem; color: var(--muted-2); }
.toolbar .tb-estado.alerta { color: #7a5b00; font-weight: 700; }
/* En el documento, lo que se dejó afuera: «Incluye solo: Observaciones y
   Reuniones». Va impreso, pegado a la fecha. */
.doc-incluye { margin: 6px 0 0; font-size: .86rem; font-weight: 700; color: var(--graphite);
  border-left: 3px solid #C07A15; padding-left: 8px; }

/* El ancho de una hoja A4 menos sus márgenes (210 − 14 − 14), que es
   exactamente el que va a tener al imprimirse. Estuvo en 900 px, y esa
   diferencia era la que rompía los gráficos en el PDF: al imprimir, el
   documento se reacomodaba a la hoja y **el canvas no** —un canvas guarda el
   tamaño con el que se dibujó—, así que el gráfico se salía de su recuadro y
   las etiquetas quedaban cortadas contra el borde de la página.
   Con el mismo ancho de los dos lados no hay nada que reacomodar: lo que se ve
   en pantalla es lo que sale en papel. */
.doc {
  background: #fff; border: 1px solid var(--graphite); border-radius: var(--radius);
  padding: 30px; max-width: 182mm; margin: 0 auto;
}
.doc-head { border-bottom: 3px solid var(--yellow); padding-bottom: 16px; margin-bottom: 22px; }
.doc-brand { display: flex; align-items: center; gap: 14px; font-size: .7rem; text-transform: uppercase; letter-spacing: .08em; color: var(--muted-2); font-weight: 700; flex-wrap: wrap; }
.doc-logo { height: 62px; width: auto; display: block; }
.doc-brand span { padding-left: 14px; border-left: 1px solid var(--line-soft); }
.doc-title { font-size: 1.55rem; margin: 12px 0 6px; text-transform: uppercase; letter-spacing: .02em; }
.doc-sub { font-size: 1rem; font-weight: 700; }
.doc-meta { font-size: .74rem; color: var(--muted); font-family: var(--mono); margin-top: 4px; }
.doc-cerrado {
  display: inline-block; margin-top: 8px; font-size: .68rem; font-weight: 800;
  text-transform: uppercase; letter-spacing: .07em; color: #fff; background: var(--inc);
  padding: 4px 10px; border-radius: 999px;
  -webkit-print-color-adjust: exact; print-color-adjust: exact;
}

.doc-kpis { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; margin-bottom: 24px; }
.doc-section { margin-bottom: 26px; }
/* El título de una lista puede vivir adentro de `.doc-arranque`, pegado a su
   primer elemento (ver la impresión, más abajo): se ve igual en los dos lados. */
.doc-section > h2, .doc-arranque > h2 { font-size: .82rem; text-transform: uppercase; letter-spacing: .08em; border-bottom: 1px solid var(--line-soft); padding-bottom: 8px; margin: 0 0 16px; }
.doc-count { color: var(--muted); font-family: var(--mono); font-weight: 700; }
/* Los gráficos van en filas de a dos, y las filas, una abajo de la otra como
   bloques —no en una grilla—: es lo que deja que Safari no las parta al
   imprimir (ver `.doc-fila` en la impresión). */
.doc-charts { display: block; }
.doc-fila { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.doc-fila + .doc-fila { margin-top: 16px; }
.doc-chart { border: 1px solid var(--line-soft); border-radius: var(--radius-sm); padding: 12px; }
.doc-chart h3 { font-size: .64rem; text-transform: uppercase; letter-spacing: .09em; color: var(--muted-2); margin: 0 0 8px; }
/* La misma altura que en la hoja impresa: si fueran distintas, el canvas
   —que guarda el tamaño con el que se dibujó— sobresaldría del recuadro al
   imprimir, y los ejes asomarían por debajo del cartel de «sin datos». Es el
   mismo motivo por el que `.doc` mide lo que mide. */
.doc .chart-wrap { height: 172px; }

.doc-list { display: flex; flex-direction: column; gap: 10px; }
.doc-item { border: 1px solid var(--line-soft); border-left: 5px solid var(--obs); border-radius: var(--radius-sm); padding: 12px 14px; break-inside: avoid; }
.doc-item.inc { border-left-color: var(--inc); }
.doc-item.reu { border-left-color: var(--graphite); }
/* Las actividades van en su propia sección del documento, así que acá el borde
   solo tiene que distinguirlas de una reunión que quedó cerca: mismo grafito,
   más claro. */
.doc-item.act { border-left-color: var(--muted); }
.di-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; }
.di-meta { font-size: .78rem; color: var(--muted-2); }
.di-time { margin-left: auto; font-family: var(--mono); font-size: .74rem; color: var(--muted); }
.di-codes { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 7px; }
.di-cod { font-family: var(--mono); font-size: .7rem; font-weight: 700; color: var(--muted-2); }
.di-cod.interno { font-weight: 400; color: var(--muted); }
.di-body .row { display: flex; gap: 8px; margin-top: 4px; font-size: .86rem; }
.di-body .k { color: var(--muted-2); min-width: 108px; font-size: .64rem; text-transform: uppercase; letter-spacing: .06em; font-weight: 700; padding-top: 2px; }
.di-fotos { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
.di-fotos a { display: block; width: 60px; height: 60px; border-radius: var(--radius-sm); overflow: hidden; border: 1px solid var(--graphite); }
.di-fotos img { width: 100%; height: 100%; object-fit: cover; }
/* En el informe las fotos van enteras —sin recortar— y de a tres por renglón,
   hasta 60 mm de alto: en el reporte del día son miniaturas y alcanzan, pero
   un informe se lee después y lejos de la planta, y ahí la foto es el dato.
   Recortadas con `cover`, una foto de celular parada perdía más de la mitad
   (le pasó a Control Operacional con una tarjeta de vencimiento). */
.modo-informe .di-fotos { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; align-items: start; }
.modo-informe .di-fotos a { width: auto; height: auto; border-color: var(--line-soft); background: #f4f2ec; }
.modo-informe .di-fotos img { display: block; width: 100%; height: auto; max-height: 60mm; object-fit: contain; }
.doc-foot { border-top: 1px solid var(--line-soft); margin-top: 24px; padding-top: 12px; font-size: .72rem; color: var(--muted); text-align: center; }
.doc-foot b { color: var(--muted-2); letter-spacing: .04em; }
.doc-firma { display: block; margin-top: 3px; font-size: .66rem; letter-spacing: .05em; text-transform: uppercase; }

@media (min-width: 600px) { .doc-kpis { grid-template-columns: repeat(auto-fit, minmax(118px, 1fr)); } }

/* Los cinco números de arriba —el total y uno por tipo— en el tablero y en el
   reporte del día (decisión 12). Cinco no entran en una fila de celular, y esa
   era la razón de que fueran cuatro: ahí el total va arriba, a lo ancho, y los
   cuatro tipos debajo, de a dos. Con lugar van los cinco en fila, con menos
   relleno: «OBSERVACIONES» es una sola palabra y no se puede partir, así que
   cada recuadro necesita ~115 px. Por eso la fila arranca en 760 —el menú fijo
   aparece en 1024 y ahí también le deja lugar— y no antes: a 600 px, con
   `auto-fit`, el rótulo ya se salía del recuadro. `numeros-cinco.mjs` lo mide
   en ocho anchos y en la hoja impresa. */
.kpis.kpis-cinco, .doc-kpis.kpis-cinco { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.kpis-cinco > .kpi:first-child { grid-column: 1 / -1; }
@media screen and (min-width: 760px) {
  .kpis.kpis-cinco, .doc-kpis.kpis-cinco { grid-template-columns: repeat(var(--n, 5), minmax(0, 1fr)); }
  .kpis-cinco > .kpi:first-child { grid-column: auto; }
  .kpis-cinco .kpi { padding: 13px 12px; }
  .kpis-cinco .kpi .lbl { letter-spacing: .05em; }
  /* El documento mide una hoja A4 (182 mm) también en pantalla: es más angosto
     que el tablero y el rótulo necesita la letra y el relleno de la hoja. */
  .doc-kpis.kpis-cinco .kpi { padding: 12px 9px; }
  .doc-kpis.kpis-cinco .kpi .lbl { font-size: .6rem; letter-spacing: .03em; }
}
/* En la hoja, los cinco de punta a punta. La grilla de la hoja estaba armada
   para siete —de cuando arriba había siete números— y los cuatro que quedaban
   ocupaban la mitad. */
@media print {
  .doc-kpis.kpis-cinco { grid-template-columns: repeat(var(--n, 5), minmax(0, 1fr)); }
  .kpis-cinco > .kpi:first-child { grid-column: auto; }
}
/* En el celular, compactos: el número y el rótulo uno al lado del otro. Con el
   número arriba y el rótulo abajo, los cinco recuadros se llevaban más de un
   tercio de la pantalla antes del primer gráfico (17/10/2026); así ocupan la
   mitad. «OBSERVACIONES» entra justo en medio ancho: por eso el espaciado de
   letras más chico (numeros-cinco.mjs mide que nada se salga). Y el que queda
   solo en su renglón —cuando se destilda un tipo— ocupa el ancho entero, en vez
   de dejar medio recuadro vacío al lado. Solo en pantalla: la hoja va en fila. */
@media screen and (max-width: 759px) {
  .kpis.kpis-cinco, .doc-kpis.kpis-cinco { gap: 8px; }
  .kpis-cinco .kpi { display: flex; align-items: center; gap: 10px; padding: 10px 12px; min-height: 58px; }
  .kpis-cinco .kpi .num { font-size: 1.6rem; flex: none; }
  .kpis-cinco .kpi .lbl { margin-top: 0; line-height: 1.25; letter-spacing: .03em; font-size: .64rem; }
  .kpis-cinco > .kpi:first-child .num { font-size: 2rem; }
  .kpis-cinco > .kpi:nth-child(even):last-child { grid-column: 1 / -1; }
}

/* El PIN recién creado: se lee en voz alta y se pasa. Grande y espaciado
   porque se dicta de a un número. */
.pin-grande {
  font-family: var(--mono); font-size: 2.6rem; font-weight: 700;
  letter-spacing: .35em; text-align: center; padding: 18px 0 18px .35em;
  background: var(--cement); border-radius: var(--radius); margin-top: 6px;
}
/* La contraseña provisoria: nueve caracteres en vez de cuatro, así que más
   chica y menos espaciada para que entre en un renglón del celular. */
.pin-grande.clave { font-size: 1.9rem; letter-spacing: .12em; padding: 16px 0 16px .12em; }
/* El usuario, arriba de la contraseña: son las dos cosas que hay que pasarle. */
.usuario-dato {
  display: flex; justify-content: space-between; align-items: baseline; gap: 10px;
  padding: 12px 14px; background: var(--surface); border: 1px solid var(--line-soft);
  border-radius: var(--radius); margin-top: 4px;
}
.usuario-dato span {
  font-size: .74rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--muted);
}
.usuario-dato b { font-family: var(--mono); font-size: 1.05rem; overflow-wrap: anywhere; }
.pin-rotulo {
  margin: 14px 2px 0; font-size: .74rem; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted-2);
}
.entra-por { margin: 12px 0 0; font-size: .84rem; color: var(--muted-2); line-height: 1.4; }

/* Las cuatro opciones del alta, de a dos: en un renglón no entran en un
   celular, y «Equipo de Reportes» se lee en dos líneas sin cortarse. */
.segmented.rol-nuevo { margin-bottom: 8px; }
.segmented.rol-nuevo label { padding: 11px 8px; line-height: 1.2; min-height: 48px; }
/* Qué hace lo elegido y con qué entra, en una línea. */
.rol-bajada { margin: 0 2px 14px; font-size: .84rem; line-height: 1.4; color: var(--muted-2); }
.rol-bajada b { color: var(--graphite); }
.alta-campo { margin-bottom: 10px; }
/* Con qué contratista queda el supervisor: no lo elige, pero lo tiene que ver. */
.alta-nota {
  margin: 0 0 10px; padding: 10px 12px; border-radius: var(--radius-sm);
  background: var(--cement); font-size: .84rem; line-height: 1.4; color: var(--graphite);
}

/* Qué período se está mirando. Va arriba de los números y no en la columna de
   filtros: es la respuesta a "¿de cuándo son estos 1335?", y esa pregunta se
   hace mirando los números, no los filtros. */
.periodo {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  background: var(--surface); border: 1px solid var(--graphite);
  border-left: 5px solid var(--yellow); border-radius: var(--radius);
  padding: 12px 14px; margin-bottom: 14px;
}
.periodo-que { flex: 1; min-width: 0; }
.periodo-que b {
  display: block; font-size: .82rem; text-transform: uppercase; letter-spacing: .05em;
}
.periodo-que span {
  display: block; font-size: .78rem; color: var(--muted); margin-top: 2px;
  font-family: var(--mono);
}
.periodo-atajos { display: flex; gap: 6px; flex-shrink: 0; }
.periodo-atajos .chip {
  border: 1px solid var(--line-soft); background: var(--surface);
  border-radius: 999px; padding: 8px 14px; min-height: 40px;
  font: inherit; font-size: .78rem; font-weight: 700; cursor: pointer;
  color: var(--muted-2); white-space: nowrap;
}
.periodo-atajos .chip:hover { border-color: var(--yellow); }
.periodo-atajos .chip.on {
  background: var(--graphite); color: #fff; border-color: var(--graphite);
}

/* En qué parada se está mirando. Misma forma que el período —lo contiene— pero
   en gris y no en amarillo: el período se toca todo el tiempo y esto casi
   nunca. Una parada cerrada se dice, porque sus números no van a cambiar más. */
.parada-barra {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  background: var(--surface); border: 1px solid var(--line-soft);
  border-left: 5px solid var(--graphite); border-radius: var(--radius);
  padding: 10px 14px; margin-bottom: 10px;
}
.parada-que { flex: 1; min-width: 0; }
.parada-que b {
  display: block; font-size: .82rem; text-transform: uppercase; letter-spacing: .05em;
}
.parada-que span { display: block; font-size: .76rem; color: var(--muted); margin-top: 2px; }
.parada-barra select {
  min-height: 40px; max-width: 100%; border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm); background: var(--surface);
  font: inherit; font-size: .8rem; padding: 6px 10px; color: var(--graphite);
}
/* Sin ninguna abierta, el subtítulo de la barra lo dice en vez de mentir con el
   nombre de la anterior. Es un problema del responsable, no de quien carga. */
.sub.sin-parada { color: var(--warn); }

/* Participantes de una reunión: una lista de personas, no un texto. Cada uno
   con su nombre grande y la empresa debajo, que es como se los reconoce, y la
   cruz de quitar a un pulgar de distancia. */
.parti-lista { display: flex; flex-direction: column; margin-bottom: 12px; }
.parti {
  display: flex; align-items: center; gap: 10px;
  background: var(--surface); border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm); padding: 10px 6px 10px 12px; margin-bottom: 6px;
}
.parti-quien { flex: 1; min-width: 0; }
.parti-quien b { display: block; font-size: .92rem; }
.parti-quien span { display: block; font-size: .76rem; color: var(--muted); margin-top: 1px; }
.parti-quien .parti-sin { color: var(--warn); }
.parti-x {
  flex-shrink: 0; width: 44px; min-height: 44px; border: none; background: none;
  color: var(--muted); font-size: 1.1rem; cursor: pointer; border-radius: var(--radius-sm);
}
.parti-x:hover { background: var(--cement); color: var(--inc); }
.parti-sug { max-height: 260px; overflow-y: auto; }

/* Dar de alta a alguien que no estaba: se le pregunta la empresa antes de
   sumarlo. Va enmarcado porque es una pregunta, no un campo más. */
.parti-alta {
  border: 1.5px solid var(--yellow); border-radius: var(--radius);
  background: #fffdf6; padding: 12px; margin-bottom: 12px;
}
.parti-alta-q { margin: 0 0 10px; font-size: .88rem; }
.parti-alta-q b { text-transform: none; }
.parti-alta [data-parti-emp-otra] { margin-top: 8px; }
.parti-alta .paso-nav { margin-top: 12px; }
.parti-alta [data-parti-sin-empresa] { margin-top: 8px; width: 100%; }

/* Traer los de la reunión anterior: es el atajo que evita reescribir a diez
   personas, así que va arriba de todo y se lee de un vistazo. */
.traer-ultima {
  width: 100%; margin-bottom: 12px; text-align: left;
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
}
.traer-ultima span {
  font-family: var(--mono); font-size: .7rem; color: var(--muted);
  text-transform: none; letter-spacing: 0; font-weight: 400;
}

/* El texto que acompaña a un "Otro": va pegado a la lista de opciones, con la
   misma sangría, para que se lea como parte de lo elegido y no como un campo
   más del paso. */
.otro-detalle { margin-top: 10px; }
.otro-detalle label {
  display: block; font-size: .72rem; font-weight: 700; color: var(--muted-2);
  margin-bottom: 6px; text-transform: uppercase; letter-spacing: .06em;
}

/* Cuando lo escrito se parece a algo que ya está. Las candidatas van dentro
   de un marco propio para que se lean como una pregunta y no como la lista
   de siempre: acá hay que decidir, no elegir. */
.dudosa {
  border: 1.5px solid var(--warn); border-radius: var(--radius);
  background: #fffdf6; padding: 10px; margin-bottom: 12px;
}
.dudosa-q {
  margin: 0 0 8px; font-size: .78rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .05em; color: #7a5b00;
}
.dudosa .opc-btn { border: 1px solid var(--line-soft); border-radius: var(--radius-sm); margin-bottom: 6px; }
.dudosa .opc-btn:last-child { margin-bottom: 0; }
.dudosa + .btn { margin-bottom: 4px; }

/* ── Pantalla de ingreso ──────────────────────────────────
   Fondo grafito y el logotipo grande: es lo primero que ve alguien de la
   aplicación, y la única pantalla donde la marca ocupa el lugar principal.
   Todo lo demás está a un pulgar de distancia. */
/* Flex y no grid con place-items: centrado así, el bloque se resuelve contra
   el ancho real —el de la pantalla menos el margen— y no contra su contenido.
   Con grid, los 380 px de máximo ganaban al margen y la pantalla se corría
   ocho píxeles a lo ancho. */
.entrar-body {
  background: var(--graphite); min-height: 100dvh; padding: 24px 18px;
  display: flex; align-items: center; justify-content: center;
}
.entrar { width: 100%; max-width: 380px; }
.entrar-logo { display: block; width: 100%; max-width: 300px; margin: 0 auto 10px; }
.entrar-contexto {
  text-align: center; color: #9aa0ab; font-size: .68rem; margin: 0 0 26px;
  text-transform: uppercase; letter-spacing: .12em;
}
.entrar form { background: var(--surface); border-radius: var(--radius); padding: 20px 18px 22px; }
.entrar-modo { margin-bottom: 14px; }
.entrar .field:last-of-type { margin-bottom: 20px; }
.entrar-pie { color: var(--muted); font-size: .78rem; text-align: center; margin: 18px 0 0; line-height: 1.5; }
.entrar .ident-error { margin: 0 0 12px; }
/* El PIN son cuatro dígitos: se escriben grandes y separados, que es como se
   leen de un vistazo cuando la pantalla tiene sol encima. */
.pin-input {
  font-size: 1.5rem; letter-spacing: .5em; text-align: center;
  font-family: var(--mono); padding-left: .5em;
}

/* ── El celular manda ─────────────────────────────────────
   Todo lo de acá abajo asume 390 px y una mano con guante. No es un ajuste
   para que "entre igual": es la forma en que se usa la aplicación. El tablero
   y el reporte del día se miran desde el celular tanto como desde el
   escritorio, y hasta acá desbordaban la pantalla.

   Los dos grid de gráficos van con minmax(0, …) siempre: sin eso el canvas
   impone su ancho, el grid se ensancha con él y la página entera se corre a
   lo ancho. Es la causa de que el reporte midiera 718 px en una pantalla de
   390. (La grilla es `.doc-fila` desde que los gráficos van en filas.) */
.doc-fila { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }

@media (max-width: 559px) {
  /* La barra. El título y el contexto van a una línea cada uno y se recortan
     si no entran; antes se partían en tres renglones y empujaban las acciones
     fuera de la pantalla. */
  .appbar { padding: 11px 14px; }
  .appbar-inner { gap: 10px; }
  /* El logotipo cede altura para que el nombre de la pantalla entre entero:
     con 20 px se comía 104 px de ancho y "Tablero de Seguridad" quedaba
     cortado. El mínimo de marca son 90 px de ancho — 17 px de alto da 88, así
     que acá va la versión corta, que no tiene descriptor y aguanta menos. */
  .appbar .logo { height: 17px; }
  .appbar h1 { font-size: .82rem; letter-spacing: .03em; }
  /* Lo que sobra del nombre de la pantalla cuando hay ancho. En el celular se
     va entero: vale más "Tablero" completo que "Tablero de Segu…" cortado. */
  .en-ancho { display: none; }
  .appbar .titulos { flex: 1; min-width: 0; }
  .appbar h1, .appbar .sub { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .appbar .sub { display: block; }
  .appbar .sub .live-dot { margin-right: 6px; }
  .appbar .logo + .titulos { padding-left: 10px; }
  .appbar .spacer { display: none; }
  /* El rol (`span.badge`) es informativo y se comía media barra: en el celular
     se va. La navegación (`a.badge`) queda, porque no hay otra forma de salir
     de la pantalla. */
  /* El período, en dos renglones: el texto a lo ancho y los atajos abajo. En
     una sola fila, "Todos los reportes cargados" se parte en tres líneas y los
     botones quedan apretados contra el borde. */
  .periodo { flex-direction: column; align-items: stretch; gap: 10px; }
  .periodo-atajos { justify-content: stretch; }
  .periodo-atajos .chip { flex: 1; padding: 8px 4px; }

  .appbar span.badge { display: none; }
  /* Dos acciones en la barra no entran en 390 px. Si la pantalla tiene por
     dónde volver, eso gana: salir es cosa de una vez cada noventa días, y
     desde el panel —la única sin "volver"— sigue estando. */
  .appbar-inner:has(a.badge) .appbar-salir { display: none; }
  /* El enlace de la barra es la única salida de la pantalla y medía 34 px:
     va a 40 como todo lo demás, centrado en vez de crecido por el padding. */
  .appbar a.badge {
    flex-shrink: 0; min-height: 40px; padding: 0 12px;
    display: inline-flex; align-items: center;
  }

  /* Nada por debajo de 40 px de alto. Es el mínimo que se acierta con guante
     puesto, y hasta acá los botones de cada fila median 28. */
  .btn.small, .cat-actions .btn, .rep-actions .btn,
  .reg-int .btn.small { min-height: 40px; padding: 10px 14px; }
  .cat-actions { gap: 4px; }
  .cat-row select, .reg-input { min-height: 44px; }

  /* Los encabezados de sección llevan acciones al costado (reporte del día,
     actualizar): en el celular no entran en la misma línea, así que bajan
     enteras en vez de asomar por el borde. */
  .section-title { flex-wrap: wrap; }
  .section-title .btn { flex: 1 1 auto; }

  /* Un gráfico por pantalla: dos de 170 px no se leen, y los números que
     tienen adentro dejan de entrar. Cada fila pasa a ser un bloque con sus dos
     gráficos uno abajo del otro. */
  .doc-fila { display: block; }
  .doc-fila > .doc-chart + .doc-chart { margin-top: 16px; }
  .doc { padding: 18px 14px; }
  /* La barra de herramientas del reporte, en columna y a lo ancho: son las
     acciones más gordas de la pantalla (imprimir, cerrar el día). */
  .toolbar { flex-wrap: wrap; }
  .toolbar .btn { flex: 1 1 100%; }
  /* El separador empuja los botones a la derecha en la compu; en columna
     quedaba solo en su renglón y dejaba un hueco arriba de «Volver al tablero». */
  .toolbar .tb-spacer { display: none; }
}

/* ── Impresión / PDF ─────────────────────────────────────── */
@media print {
  @page { size: A4; margin: 16mm 14mm; }
  body { background: #fff; }
  body, .doc { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  .no-print { display: none !important; }
  .container { padding: 0; max-width: none; }
  .doc { border: none; border-radius: 0; padding: 0; max-width: none; }

  /* El detalle debe poder repartirse entre páginas: si se lo trata como un
     bloque indivisible, se va entero a la hoja siguiente y deja un hueco. */
  .doc-section { break-inside: auto; margin-bottom: 18px; }
  .doc-section > h2 { break-after: avoid; break-inside: avoid; }
  .doc-head { break-after: avoid; }

  /* Lo que sí no conviene partir al medio */
  .doc-kpis, .kpi, .doc-chart, .doc-item, .doc-foot { break-inside: avoid; }

  /* > ⚠️ **Safari (el del iPhone) no respeta todo lo de arriba**, y es desde
     > donde más se saca este PDF. Dos cosas que Chrome hace y Safari no
     > (14/10/2026, con un reporte real de UPM):
     >
     > · **«No cortar» en los elementos de una grilla.** Los cuatro gráficos
     >   eran una grilla y Potencialidad salió partido entre dos hojas. Ahora
     >   van en filas que son bloques: la fila entera no se corta, y en el
     >   celular —un gráfico por renglón— cada gráfico por su lado.
     > · **`break-after: avoid`.** «Detalle de reportes» quedó solo al pie de
     >   una hoja y sus reportes en la siguiente. Ahora el título y el primer
     >   elemento van en una misma caja que no se corta (`.doc-arranque`, la
     >   arma `reporte.js`), así que bajan juntos de hoja.
     >
     > El costo es el de siempre: a veces queda espacio en blanco al final de
     > una hoja, en vez de un corte. */
  .doc-fila, .doc-arranque { break-inside: avoid; }

  /* Los indicadores entran todos en una fila: uno suelto abajo se lee como si
     fuera otra cosa. Van más chicos para que quepan sin apretarse. */
  .doc-kpis { grid-template-columns: repeat(7, 1fr); gap: 6px; }
  .doc-kpis .kpi { padding: 10px 8px; }
  .doc-kpis .num { font-size: 1.4rem; }
  .doc-kpis .lbl { font-size: .54rem; letter-spacing: .05em; margin-top: 5px; }

  /* Reportes largos: la primera hoja queda como resumen y el detalle arranca limpio */
  .doc-section.salto-pagina { break-before: page; }

  /* En flex el corte entre páginas es irregular: en papel va como bloques */
  .doc-list { display: block; }
  .doc-item { margin-bottom: 8px; }

  /* La altura ya es la misma en pantalla (172 px): con menos, la leyenda de las
     donas se sale de su recuadro. Queda dicho acá porque es el valor que manda
     —lo que entra en la hoja— y el de pantalla es el que lo copia. */

  /* La silueta va compacta: a tamaño de pantalla se lleva una hoja entera
     para mostrar una figura y nueve renglones. */
  .sil-dibujo { width: 118px; }
  .silueta { gap: 14px; }
  .sil-lista li { padding: 3px 0; font-size: .78rem; }
  .sil-aparte { margin-top: 9px; padding-top: 8px; }
  .silueta, .sil-dibujo, .sil-datos { break-inside: avoid; }
  .di-fotos a { width: 54px; height: 54px; }
  .doc-title { font-size: 1.4rem; }

  a { text-decoration: none; color: inherit; }
}
/* Con un gráfico por renglón (el celular), la fila son dos gráficos uno abajo
   del otro: mantenerla entera mandaría los dos a la hoja siguiente cuando el
   primero entraba. Ahí cada gráfico se cuida solo. */
@media print and (max-width: 559px) {
  .doc-fila { break-inside: auto; }
}

/* ══════════════════════════════════════════════════════════════════
   OBTURADORES · el módulo interno

   Se ve distinto a propósito. Es otro equipo, otro trabajo y otra lista;
   que se parezca a la pantalla de reportes es exactamente lo que hay que
   evitar. El acero de --obt no compite con ninguno de los tres estados de
   un reporte, así que una tarjeta de obturador nunca se lee como un
   incidente.
   ══════════════════════════════════════════════════════════════════ */

/* La barra de arriba, que es lo primero que se mira al entrar */
/* Solo el color y el subtítulo: el título tiene que caber entero en 390 px, y
   una coletilla («· interno») lo cortaba justo en los teléfonos donde se usa. */
.appbar.obt { background: var(--obt-dk); }

/* ── La tarjeta de un obturador ────────────────────────────────── */
.obt {
  background: var(--surface);
  border: 1px solid var(--graphite);
  border-left: 5px solid var(--obt);
  border-radius: var(--radius);
  padding: 12px 14px;
  margin-bottom: 10px;
}
.obt.colocado { border-left-color: var(--obs); }
.obt.retirado { border-left-color: var(--muted); opacity: .82; }
.obt.vencido  { border-left-color: var(--warn); }

.obt-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.obt-cod {
  font-family: var(--mono); font-size: .82rem; font-weight: 700;
  letter-spacing: .02em;
}
.obt-medida { font-weight: 700; }
.obt-donde { margin: 6px 0 0; font-size: .92rem; }
.obt-donde .k { color: var(--muted); }
.obt-cuando { margin: 4px 0 0; font-size: .84rem; color: var(--muted-2); }
.obt-cuando.tarde { color: var(--warn); font-weight: 700; }

/* Las etiquetas de estado. Nunca dos: un obturador está en un solo lugar. */
.tag.obt-pedido   { background: var(--obt); color: #fff; }
.tag.obt-colocado { background: var(--obs); color: #fff; }
.tag.obt-retirado { background: var(--muted); color: #fff; }

/* Las acciones: el botón de lo que toca hacer ahora va primero y entero. */
.obt-acciones { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
.obt-acciones .btn { flex: 1 1 130px; width: auto; min-height: 44px; }
.obt-acciones .btn.principal { background: var(--obt); color: #fff; border-color: var(--obt-dk); }

/* ── El historial: la línea de tiempo de las cuatro etapas ─────── */
.obt-det { margin-top: 10px; }
.obt-det > summary {
  list-style: none; cursor: pointer;
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  min-height: 44px; padding: 0 2px;
  border-top: 1px dashed var(--line-soft);
  font-size: .86rem; font-weight: 700; color: var(--obt-dk);
}
.obt-det > summary::-webkit-details-marker { display: none; }
.obt-det > summary::after {
  content: "▾";
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--graphite); color: #fff; font-size: .7rem;
}
.obt-det[open] > summary::after { content: "▴"; }

.linea { margin: 8px 0 2px; padding: 0; list-style: none; }
.linea li {
  position: relative; padding: 0 0 12px 20px;
  border-left: 2px solid var(--line-soft); margin-left: 5px;
}
.linea li:last-child { border-left-color: transparent; padding-bottom: 0; }
.linea li::before {
  content: ""; position: absolute; left: -7px; top: 3px;
  width: 12px; height: 12px; border-radius: 50%;
  background: var(--surface); border: 2px solid var(--obt);
}
.linea li.control::before { border-color: var(--obs); }
.linea li.retiro::before  { border-color: var(--muted); }
.linea li.advertencia::before { border-color: var(--warn); background: var(--warn); }
/* La advertencia, adentro del historial. Ámbar y con el punto lleno: en una
   línea de tiempo de ocho etapas tiene que saltar sin leerla. */
.linea li.advertencia > b { color: #7a5b00; }
/* 40 px, como todo lo que se toca: se aprieta con guantes y parado. Estuvo en
   34 y no lo agarraba nadie, porque sin ninguna advertencia cargada este botón
   no existe y `movil.mjs` mide lo que está dibujado, no lo que podría estarlo. */
.linea li.advertencia .btn { margin-top: 4px; min-height: 40px; padding: 0 12px; }
/* Cada dato en su renglón. Como spans en línea, «12/08 15:33 · Carla Méndez» y
   «Inicial 2,4 bar» se pegaban en una sola frase ilegible. */
.linea b, .linea .cuando, .linea .dato { display: block; }
.linea b { font-size: .88rem; }
.linea .cuando { font-size: .78rem; color: var(--muted); margin-bottom: 2px; }
.linea .dato { font-size: .86rem; }
.linea .dato.k { color: var(--muted); font-style: italic; }
.obt-fotos { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
.obt-fotos img {
  width: 74px; height: 74px; object-fit: cover;
  border: 1px solid var(--graphite); border-radius: var(--radius-sm);
}

/* ── Lo que hay que saber antes de ir hasta ahí ────────────────── */
/* Ámbar, como todo lo que pide una decisión: la decisión acá es con qué se sale
   —el detector de gases, el arnés, ir de a dos—, y se toma antes de caminar.
   Por eso esto vive arriba de los botones de acción y arriba de los carteles, y
   no adentro del historial: ahí se lee cuando ya se llegó. */
.obt-avisos {
  display: block;
  background: #fbf3d8; border: 1px solid var(--warn);
  border-left: 4px solid var(--warn);
  border-radius: var(--radius-sm);
  padding: 8px 10px; margin: 10px 0 0;
}
.obt-aviso {
  margin: 0; font-size: .86rem; line-height: 1.45; color: #7a5b00;
}
.obt-aviso + .obt-aviso { margin-top: 6px; border-top: 1px dashed var(--warn); padding-top: 6px; }
.obt-aviso .ic { font-weight: 700; margin-right: 4px; }
.obt-aviso b { color: var(--graphite); }
.obt-aviso .quien { color: var(--muted-2); font-size: .78rem; white-space: nowrap; }
/* Arriba de todo en los tres carteles y antes de los campos: es lo último que
   se ve antes de confirmar. */
.modal .obt-avisos { margin: 0 0 12px; }

/* ── El manómetro ──────────────────────────────────────────────── */
.mano-caja {
  margin-top: 10px; padding: 10px;
  background: var(--surface); border: 1px solid var(--line-soft);
  border-radius: var(--radius); text-align: center;
}
.manometro { width: 100%; max-width: 240px; height: auto; }
.mano-arco { fill: none; stroke: var(--line-soft); stroke-width: 14; stroke-linecap: butt; }
.mano-rojo  { stroke: var(--inc); }
.mano-verde { stroke: var(--obs); }
.mano-marca { stroke: var(--muted-2); stroke-width: 2; }
.mano-num   { fill: var(--muted-2); font-size: 11px; font-family: var(--mono); text-anchor: middle; }
.mano-aguja { stroke: var(--graphite); stroke-width: 3.5; stroke-linecap: round; }
.mano-eje   { fill: var(--graphite); }
.mano-valor {
  fill: var(--graphite); font-size: 15px; font-weight: 700;
  text-anchor: middle; font-family: var(--mono);
}
.mano-pie { margin: 6px 0 0; font-size: .84rem; color: var(--muted-2); }

/* ── El tablero interno ────────────────────────────────────────── */
.obt-barras { margin-top: 6px; }
.obt-barra { display: grid; grid-template-columns: 1fr auto; gap: 4px 10px; margin-bottom: 9px; }
.obt-barra .nom { font-size: .88rem; }
.obt-barra .n { font-family: var(--mono); font-size: .88rem; font-weight: 700; }
.obt-barra .riel {
  grid-column: 1 / -1; height: 9px; background: var(--cement);
  border: 1px solid var(--line-soft); border-radius: 5px; overflow: hidden;
}
.obt-barra .riel i { display: block; height: 100%; background: var(--obt); }

/* Hace cuánto que nadie lo mira. Es el único número que pide hacer algo. */
.obt-hace { font-family: var(--mono); font-size: .82rem; font-weight: 700; }
.obt-hace.frio  { color: var(--warn); }
.obt-hace.nunca { color: var(--inc); }

@media (max-width: 559px) {
  .obt-acciones .btn { flex: 1 1 100%; }
  .obt-fotos img { width: 64px; height: 64px; }
}

/* El botón que abre una solicitud. Va entero y arriba de todo, no como pastilla
   en la esquina de la barra: es la única entrada a la lista y ahí no se veía.
   Con el texto completo tampoco entraba sin cortar el título de la pantalla. */
#btnSolicitud { background: var(--obt); color: #fff; border-color: var(--obt-dk); margin-bottom: 14px; }

/* ── La fila del tablero interno ────────────────────────────────
   Dos renglones y se abre al tocar. La tarjeta completa ocupaba media pantalla
   por obturador: con veinte, la lista era una pared y el que supervisa no podía
   recorrerla antes de decidir qué mirar. */
#filtros { margin-bottom: 16px; }

.obt-fila {
  background: var(--surface);
  border: 1px solid var(--graphite);
  border-left: 5px solid var(--obt);
  border-radius: var(--radius);
  margin-bottom: 8px;
}
.obt-fila.colocado { border-left-color: var(--obs); }
.obt-fila.retirado { border-left-color: var(--muted); }
.obt-fila.vencido  { border-left-color: var(--warn); }

.obt-fila > summary {
  list-style: none; cursor: pointer;
  display: grid; grid-template-columns: 1fr 26px; align-items: center;
  gap: 4px 10px; padding: 10px 12px; min-height: 56px;
}
.obt-fila > summary::-webkit-details-marker { display: none; }
.obt-fila > summary::after {
  content: "▾"; grid-column: 2; grid-row: 1 / span 2;
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--graphite); color: #fff; font-size: .7rem;
}
.obt-fila[open] > summary::after { content: "▴"; }
.obt-fila[open] > summary { border-bottom: 1px dashed var(--line-soft); }

/* Renglón 1: en qué estado está, cuál es y de qué medida. */
.obt-fila .f1 {
  grid-column: 1; display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
}
.obt-fila .f1 .obt-medida { margin-left: auto; }

/* Renglón 2: dónde está y la fecha que importa según su estado. El «dónde»
   cede espacio primero —se corta con puntos— porque la fecha es la que decide
   si hay que hacer algo. */
.obt-fila .f2 {
  grid-column: 1; display: flex; align-items: baseline; gap: 10px;
  font-size: .88rem; min-width: 0;
}
.obt-fila .f2 .donde {
  flex: 1 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.obt-fila .f2 .cuando { flex: 0 0 auto; color: var(--muted-2); font-size: .82rem; }
.obt-fila .f2 .cuando.tarde { color: var(--warn); font-weight: 700; }

/* Renglón 3, y solo cuando hay algo que advertir: el ⚠ y el principio de lo que
   dice. Acá se corta con puntos —en el resto de la app se lee entera— porque
   esta lista es del que supervisa y se recorre de veinte en veinte: si cada
   advertencia larga sumara tres renglones, volveríamos a la pared de tarjetas
   que la fila compacta vino a sacar. Entera está a un toque, en el historial. */
.obt-fila > summary .obt-avisos {
  grid-column: 1; margin: 2px 0 0; padding: 5px 8px; min-width: 0;
}
.obt-fila > summary .obt-aviso {
  display: flex; align-items: baseline; gap: 6px; font-size: .82rem;
}
/* El texto cede y se corta; el ⚠ y el «+2» no, que son lo que dice que hay algo
   y cuánto. Todo en un renglón: si el contador bajara, la fila crecería igual
   que dibujándolas todas. */
.obt-fila > summary .obt-aviso b {
  flex: 1 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.obt-fila > summary .obt-aviso .ic,
.obt-fila > summary .obt-aviso .mas { flex: 0 0 auto; }
.obt-fila > summary .obt-aviso .quien { display: none; }
.obt-aviso .mas { font-size: .78rem; font-style: italic; color: var(--muted-2); }

.obt-fila-cuerpo { padding: 10px 12px 12px; }
.obt-fila-cuerpo .obt-acciones { margin-top: 4px; }
.obt-fila-cuerpo .obt-acciones .btn { flex: 0 0 auto; }

@media (max-width: 559px) {
  /* En el celular la medida no entra al lado del código: baja de renglón. */
  .obt-fila .f1 .obt-medida { margin-left: 0; }
}

/* El rótulo de una elección obligatoria dentro de una sección de Listas. */
.cat-sub {
  display: block; margin-bottom: 6px;
  font-size: .72rem; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted-2);
}

/* El pie de una lista paginada de obturadores. Dice siempre cuántos se ven de
   cuántos: una lista que se corta sin avisar se lee como que faltan. */
.obt-pie {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; flex-wrap: wrap;
  margin: 10px 0 4px; font-size: .84rem; color: var(--muted-2);
}
.obt-pie .btn { flex: 0 0 auto; }
