/* ⚠️ Acá vivían las reglas de `facturas/list.html` (`.facturas-panel`,
   `.facturas-head`, `.facturas-search*`, `.dots-btn`, la leyenda de colores y
   las franjas `.fila-nacional` / `.fila-exportacion`). Se borraron el
   2026-08-21 con esa plantilla: eran su único usuario, comprobado con grep
   sobre plantillas, JSX y JS antes de tocar nada.
   Las franjas eran la última excepción viva de `FRANJAS_PERMITIDAS` en el
   guardián de estilo; el tinte de fila que las reemplaza está en el bloque de
   `.tema-bento`. `.dots-panel` sí sigue vivo: lo usa `ui/Menu.jsx`. */
/* Fuente cargada desde las plantillas base: Manrope (familia única, ver DESIGN.md) */
:root{
  /* --vino/--vino-h/--vino-tinte, --fondo/--panel/--texto, --tenue,
     --borde, --ok, --peligro y --ease-spring viven en tokens.css (Fase 3
     rediseño, 2026-07-13) — cargado antes que este archivo en las 4
     plantillas base que lo usan. Fuente única de verdad compartida
     también con Siigo. */
  /* Tokens que ya documentaba DESIGN.md pero nunca se declaraban aquí. */
  --alerta:#8A4F00;
  --home-bg:#1F2430;
  /* Tono neutro de hover/encabezado de tabla — usado con este mismo
     fallback en 8 lugares de Compras sin declararse nunca aquí. */
  --fondo-alt:#F7F8FA;
  /* Colores de grupo botanico ICA, centralizados (Fase 2 rediseño 2026-07-13)
     — antes hardcodeados en .badge-grupo.*, duplicando --alerta y un verde
     casi-igual a --ok. Aves reusa --alerta (mismo valor exacto); Ruscus se
     unifica con --ok (eran 2 tonos de verde casi idénticos, decisión del
     usuario de dejar uno solo); Hydrangea es su primer uso como variable. */
  --grupo-aves:var(--alerta); --grupo-ruscus:var(--ok); --grupo-hydrangea:#1558AC;
}
*{box-sizing:border-box;margin:0;padding:0}
.skip-link{position:absolute;left:-999px;top:auto;width:1px;height:1px;overflow:hidden;
  z-index:300;background:var(--vino);color:#fff;padding:10px 16px;border-radius:0 0 8px 0;
  font-size:13px;font-weight:700;text-decoration:none}
.skip-link:focus{left:0;top:0;width:auto;height:auto;overflow:visible}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0,0,0,0);white-space:nowrap;border:0}
body{font-family:'Manrope',"Segoe UI",system-ui,Arial,sans-serif;background:var(--fondo);
  color:var(--texto);min-height:100vh;padding-left:58px}

/* ── Sidebar ──────────────────────────────────────────────────── */
/* Mini-expandible (rediseño Fase 1, 2026-07-12): colapsado a un riel de
   58px con solo íconos; se expande a 230px con :hover (mouse) o
   :focus-within (teclado — sin esto, un usuario de teclado nunca podría
   expandirlo, ver spec de la Fase 1). */
.sidebar{width:58px;background:#fff;border-right:0.5px solid var(--borde);display:flex;
  flex-direction:column;padding:0;position:fixed;left:0;top:0;height:100vh;flex-shrink:0;
  overflow-y:auto;overflow-x:hidden;transition:width .38s var(--ease-spring);z-index:40}
.sidebar:hover,.sidebar:focus-within{width:230px}
.brand{padding:22px 18px 16px;border-bottom:0.5px solid var(--borde);text-decoration:none;display:block}
.sidebar nav{display:flex;flex-direction:column;gap:1px;padding:14px 10px;flex:1}
.sidebar nav a,.sidebar nav .nav-accion{color:var(--tenue);text-decoration:none;padding:8px 9px;border-radius:8px;
  font-size:13.5px;display:flex;gap:0;align-items:center;
  transition:padding .38s var(--ease-spring), gap .38s var(--ease-spring), background .12s ease, color .12s ease, border-left-color .12s ease;
  border-left:2.5px solid transparent;letter-spacing:0.01em}
.sidebar:hover nav a,.sidebar:focus-within nav a,
.sidebar:hover nav .nav-accion,.sidebar:focus-within nav .nav-accion{padding:8px 12px;gap:11px}
.sidebar nav a .navicon,.sidebar nav .nav-accion .navicon{width:17px;height:17px;flex-shrink:0;opacity:.8}
.sidebar nav a:hover,.sidebar nav .nav-accion:hover{background:var(--fondo);color:var(--texto)}
.sidebar nav a:hover .navicon,.sidebar nav .nav-accion:hover .navicon{opacity:1}
.sidebar nav a.on{color:var(--vino);border-left-color:var(--vino);font-weight:700;background:var(--vino-tinte)}
.sidebar nav a.on .navicon{opacity:1}
.sidebar nav a span,.sidebar nav .nav-accion span,.side-user-info,.export span{
  opacity:0;max-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;
  transition:opacity .2s ease, max-width .38s var(--ease-spring)}
.sidebar:hover nav a span,.sidebar:hover nav .nav-accion span,
.sidebar:hover .side-user-info,.sidebar:hover .export span,
.sidebar:focus-within nav a span,.sidebar:focus-within nav .nav-accion span,
.sidebar:focus-within .side-user-info,.sidebar:focus-within .export span{
  opacity:1;max-width:160px}
/* Botón de acción en el sidebar (abre un modal en vez de navegar): un
   <button> de verdad porque no es navegación, con el aspecto de un enlace. */
.sidebar nav .nav-accion{background:none;border-top:0;border-right:0;border-bottom:0;
  font:inherit;font-family:inherit;text-align:left;width:100%;cursor:pointer}
.nav-sep{height:1px;background:var(--borde);margin:10px 12px}
.nav-label{font-size:10px;font-weight:700;letter-spacing:0.12em;text-transform:uppercase;
  color:var(--tenue);padding:4px 12px 6px}
.side-footer{border-top:0.5px solid var(--borde);padding:12px 10px}
.side-user{display:flex;align-items:center;gap:0;padding:6px 8px 10px;transition:gap .38s var(--ease-spring)}
.sidebar:hover .side-user,.sidebar:focus-within .side-user{gap:10px}
.side-avatar{width:32px;height:32px;border-radius:50%;background:var(--vino-tinte);
  color:var(--vino);font-family:'Manrope',"Segoe UI",sans-serif;font-weight:800;
  font-size:14px;display:flex;align-items:center;justify-content:center;flex-shrink:0}
.side-user-info{min-width:0}
.side-user-name{font-size:13px;font-weight:700;color:var(--texto);white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}
.side-user-role{font-size:10.5px;color:var(--tenue);letter-spacing:0.03em}
.export{color:var(--tenue);text-decoration:none;padding:8px 9px;font-size:13px;border-radius:8px;
  display:flex;align-items:center;gap:0;
  transition:padding .38s var(--ease-spring), gap .38s var(--ease-spring), background .12s ease, color .12s ease}
.sidebar:hover .export,.sidebar:focus-within .export{padding:8px 12px;gap:11px}
.export .navicon{width:16px;height:16px;opacity:.75}
.export:hover{background:var(--fondo);color:var(--texto)}
.export:hover .navicon{opacity:1}

/* ── Contenido ────────────────────────────────────────────────── */
/* max-width 1200 -> 1600 y padding lateral 36 -> 28 (2026-08-12, pedido del
   usuario: "un margen tan grande que no deja aprovechar bien la pantalla").
   Medido en su ventana de 1830 px: quedan ~1773 útiles descontando el riel del
   sidebar, y el tope de 1200 usaba 1128 — o sea ~645 px desperdiciados. Con
   1600 quedan ~1544 de contenido: 416 px más.
   NO se suelta del todo a propósito: en las pantallas de formulario (Ajustes,
   editar factura, perfil de empleado) un campo de texto de 1800 px se lee peor,
   no mejor. La portada (`.home-wrap`, 1100) queda centrada aparte: son tarjetas
   sobre una imagen, no una tabla, y estirarlas la empeora.
   El full-bleed real sigue existiendo para las tablas que lo necesitan
   (`.content.facturas-full`, ver abajo). */
/* ── Informes en React (2026-08-31) ──────────────────────────── */
/* Reemplaza a las 181 líneas de CSS que vivían dentro de `informes.html`, que
   era un documento suelto con su propio diseño. Al entrar al Shell común, la
   mayoría dejó de hacer falta: las tarjetas ahora son `.tile` del tema y el
   encabezado es el `.row-head` de siempre. Queda lo que es propio del informe. */

/* El canvas de un gráfico necesita alto explícito: Chart.js con
   `maintainAspectRatio:false` crece hasta el del contenedor, y sin alto el
   contenedor mide 0 y el gráfico no se ve. El alto lo pasa cada `<Grafico>`. */
.grafico{position:relative;width:100%}
.grafico canvas{width:100%!important;height:100%!important}

/* La fila de filtros. `auto-fit` y no `auto-fill`: son 5 y queremos que ocupen
   el ancho, no que dejen huecos. */
.filtrera{display:grid;gap:10px;margin-bottom:10px;
  grid-template-columns:repeat(auto-fit,minmax(190px,1fr))}
.click-hint{color:var(--tenue);font-size:12.5px;margin:0 0 18px}
.chart-note{font-size:12px;color:var(--tenue);margin:10px 0 0;
  display:flex;gap:10px;flex-wrap:wrap}
.chart-note b{color:var(--texto);font-weight:700;font-variant-numeric:tabular-nums}
h2.tarjetas-tt .note{font-weight:400;font-size:12.5px;color:var(--tenue)}
/* El pie de la tabla mensual: una fila más, para que las columnas queden
   alineadas con el resto (como `<tfoot>` aparte se desalineaba al scrollear). */
.fila-total td{border-top:2px solid var(--borde);font-weight:700;
  color:var(--texto);background:var(--fondo)}
/* El nombre del mes abre el drill. Botón y no fila clicable: el teclado lo
   alcanza y el lector de pantalla lo anuncia. */
.enlace-mes{background:none;border:0;padding:0;font:inherit;color:var(--vino);
  cursor:pointer;text-align:left}
.enlace-mes:hover{text-decoration:underline}
.enlace-mes:focus-visible{outline:2px solid var(--vino);outline-offset:2px}
.tema-bento .enlace-mes{color:var(--a-lila)}

/* La leyenda de grupos, debajo del resumen. */
.legend-grupos{display:flex;flex-wrap:wrap;gap:8px 18px;margin:0 0 22px;
  font-size:12.5px;color:var(--tenue)}
.legend-grupos .grupo-dot{display:inline-block;width:9px;height:9px;
  border-radius:50%;margin-right:6px;vertical-align:0}

.drill-sub{margin:0 0 12px;font-size:13px;color:var(--tenue)}
.drill-sub b{color:var(--texto);font-variant-numeric:tabular-nums}
/* Mientras se recarga por un filtro, el informe se atenúa en vez de vaciarse:
   ver la pantalla en blanco entre filtro y filtro se siente roto. */
.informes.cargando{opacity:.55;pointer-events:none;transition:opacity .15s ease}

/* Los tres tonos de la esfera del globo. Están acá y no en el JS porque un
   `<canvas>` no entiende `var(--x)` —hay que resolverlo— y un hex escrito en el
   JSX sería un segundo lugar donde vive un color del sistema. Los lee
   `modules/informes/tokens.js`. */
:root{--globo-esfera-1:#FFFFFF; --globo-esfera-2:#F2F3F5; --globo-esfera-3:#E4E6EA}

/* El globo. `aspect-ratio` y no alto fijo: tiene que ser cuadrado o la esfera
   sale ovalada al proyectar. */
/* ⚠️ El tope baja de 430 a 310 px (2026-09-16). Era el bloque más alto de
   Informes y, al ser el más alto de su fila, estiraba al tile de al lado: «% de
   la carga exportada por país» tiene ocho renglones y quedaba con medio tile
   vacío. A 310 la esfera se sigue leyendo y la fila mide lo que necesita. */
.globe-box{position:relative;width:100%;aspect-ratio:1;min-height:260px;
  max-height:310px;margin:0 auto}
.globe-box canvas{width:100%;height:100%;display:block}
.globe-toast{position:absolute;left:50%;bottom:10px;transform:translateX(-50%) translateY(6px);
  background:var(--panel);border:1px solid var(--borde);border-radius:999px;
  padding:6px 14px;font-size:12.5px;white-space:nowrap;opacity:0;
  transition:opacity .25s ease, transform .25s ease;pointer-events:none;
  box-shadow:0 4px 14px rgba(20,22,28,.10)}
.globe-toast.show{opacity:1;transform:translateX(-50%) translateY(0)}
.globe-ctl{display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  margin-top:10px;font-size:12.5px;color:var(--tenue)}
.globe-ctl .cuenta b{color:var(--texto);font-variant-numeric:tabular-nums}
.rutas-origin-tag{margin-left:auto}

/* La puerta de entrada: nacional, exportación o ambas. Cubre el contenido
   porque la elección cambia TODO el informe: dejarlo ver detrás invitaría a
   leer números que están por cambiar. */
.puerta{position:fixed;inset:0;z-index:60;display:grid;place-items:center;
  padding:24px;background:rgba(244,245,249,.86);backdrop-filter:blur(7px)}
.puerta-caja{background:var(--panel);border-radius:20px;padding:30px 32px;
  max-width:660px;width:100%;text-align:center;
  box-shadow:0 18px 50px rgba(20,22,28,.16)}
.puerta-caja h2{margin:0 0 5px;font-size:21px;font-weight:800;letter-spacing:-.02em}
.puerta-caja > p{margin:0 0 20px;color:var(--tenue);font-size:13px}
.puerta-ops{display:grid;gap:12px;grid-template-columns:repeat(3,1fr)}
.puerta .op{background:var(--fondo);border:1.5px solid var(--borde);
  border-radius:14px;padding:16px 14px;cursor:pointer;font:inherit;color:inherit;
  display:flex;flex-direction:column;gap:2px;transition:.15s ease}
.puerta .op:hover{border-color:var(--vino);background:var(--vino-tinte)}
.puerta .op:focus-visible{outline:2px solid var(--vino);outline-offset:2px}
.puerta .op b{font-size:14.5px}
.puerta .op span{font-size:11.5px;color:var(--tenue)}
/* El MONTO en el botón no es decoración: es la razón de elegir. Sin él la
   pregunta es a ciegas y uno aprende a apretar siempre lo mismo. */
.puerta .op em{font-style:normal;font-size:19px;font-weight:800;color:var(--vino);
  font-variant-numeric:tabular-nums;margin-top:6px}
.tema-bento .puerta .op em{color:var(--a-lila)}
@media (max-width:760px){ .puerta-ops{grid-template-columns:1fr} }

/* Las tarjetas de grupo, con su foto. Son BOTONES: al hacer clic filtran el
   informe por ese grupo, igual que el anillo. */
/* Los tres grupos botánicos son una fila más del mosaico, así que llevan su
   mismo `gap` y su mismo margen: con 11 y 4 se leían como un bloque ajeno
   metido entre los tiles, que es de lo que sale la sensación de desorden. */
.gcards{display:grid;gap:14px;grid-template-columns:repeat(3,1fr);
  margin-bottom:14px}
.gcard{border:0;border-radius:var(--r-tile,16px);padding:0;display:flex;
  align-items:stretch;overflow:hidden;cursor:pointer;font:inherit;text-align:left;
  transition:transform .25s var(--ease-spring), box-shadow .25s var(--ease-spring)}
.gcard:hover{transform:translateY(-2px);box-shadow:0 8px 20px rgba(20,22,28,.10)}
.gcard:focus-visible{outline:2px solid currentColor;outline-offset:2px}
.gfoto{width:84px;flex-shrink:0;background-size:cover;background-position:center}
.gtxt{display:flex;flex-direction:column;gap:1px;padding:14px 16px;min-width:0}
.gl{font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  opacity:.85}
.gv{font-size:21px;font-weight:800;letter-spacing:-.02em;line-height:1.15;
  font-variant-numeric:tabular-nums}
.gs{font-size:11px;opacity:.75}
@media (max-width:760px){ .gcards{grid-template-columns:1fr} }

/* Estacionalidad: mes contra grupo, el color dice cuánto. */
/* La grilla crece hasta llenar su tile: ocupaba 75 px de los 210 que la fila le
   da (el tile se estira al más alto de los tres, que es el anillo), y esos 135
   px de blanco abajo eran de lo que más se leía como «desordenado». Ahora las
   celdas se reparten el alto disponible en vez de dejarlo muerto. */
.heat{display:flex;flex-direction:column;gap:4px;flex:1;justify-content:center}
/* ⚠️ `minmax(0,1fr)` y no `1fr`: una columna `1fr` no baja del ancho de su
   contenido, así que en un celular de 360 px los doce meses no podían encogerse
   y «Dic» sacaba la página 11 px de lado — el único desborde que quedaba en las
   34 pantallas (medido el 2026-09-16 a 1280/900/768/600/360). */
.heat-fila{display:grid;grid-template-columns:104px repeat(var(--cols),minmax(0,1fr));
  gap:4px;align-items:center}
.heat-fila:not(:first-child){flex:1;min-height:22px}
.heat-fila:not(:first-child) .heat-celda{height:100%;min-height:22px}
.heat-nom{font-size:10.5px;font-weight:700;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.heat-mes{font-size:9.5px;color:var(--tenue);text-align:center;
  overflow:hidden;white-space:nowrap}
/* En un celular la columna de los nombres cede ancho para que los meses entren
   enteros; es el nombre el que puede abreviarse, no el eje. */
@media (max-width:560px){
  .heat-fila{grid-template-columns:68px repeat(var(--cols),minmax(0,1fr));gap:3px}
  .heat-mes{font-size:9px}
}
.heat-celda{height:22px;border-radius:4px;opacity:var(--o)}

/* Variedades partidas por grupo. */
.vgs{display:flex;flex-direction:column;gap:14px}
.vg-t{font-size:10.5px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;
  display:block;margin-bottom:6px}
.vg-fila{display:grid;grid-template-columns:1fr 90px auto;gap:10px;align-items:center;
  margin-bottom:5px}
.vg-n{font-size:11.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.vg-p{height:10px}
.vg-b{display:block;height:10px;border-radius:3px}
.vg-v{font-size:11.5px;font-weight:700;font-variant-numeric:tabular-nums;
  white-space:nowrap}

/* Un número solo, grande, como respuesta de una tarjeta. */
.dato-grande{display:block;font-size:30px;font-weight:800;letter-spacing:-.025em;
  line-height:1.1;font-variant-numeric:tabular-nums;margin-bottom:2px}
.lista-simple{list-style:none;margin:10px 0 0;padding:0;display:flex;
  flex-direction:column;gap:5px}
.lista-simple li{display:flex;justify-content:space-between;gap:10px;font-size:11.5px;
  color:var(--tenue);border-bottom:1px dotted var(--borde);padding-bottom:4px}
.lista-simple li b{color:var(--texto);font-variant-numeric:tabular-nums}

/* El aviso del hueco sin vendedor. Solo aparece cuando el hueco es grande —
   en el informe nacional es el 96 % y sin decirlo el ranking parece que sobra. */
.aviso-hueco{background:var(--vino-tinte);color:var(--vino);border-radius:10px;
  padding:9px 12px;font-size:12px;margin-top:10px}
.tema-bento .aviso-hueco{background:var(--t-lila);color:var(--a-lila)}

/* Impresión: el informe en PDF sale sin sidebar ni controles. Es el «PDF» de la
   cabecera, que es el diálogo del navegador — no hay un generador aparte que
   pueda quedar desincronizado de lo que se está viendo en pantalla. */
@media print{
  .sidebar,.barra-mod,.filtrera,.click-hint,.acts,.globe-ctl,
  .puerta{display:none!important}
  body{padding-left:0}
  .content{max-width:100%;padding:0}
  .tile{break-inside:avoid;page-break-inside:avoid}
}

/* ── Administración en React (2026-08-31) ────────────────────── */
/* Las clases que reemplazan al markup en línea de las 5 plantillas Jinja que se
   borraron. Estaban como `style="..."` dentro del HTML; acá quedan una vez.
   `.proc-*` NO se tocó: la pantalla de procesos automáticos las sigue usando. */

/* Cabecera de un registro (un error de Monitoreo, una eliminación auditada):
   el badge, la fecha y —si va— el botón de acción, en una línea que envuelve. */
.reg-cab{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.reg-meta{color:var(--tenue);font-size:12px}
.reg-mensaje{margin-top:8px;font-weight:600}
.reg-recom{margin-top:8px;background:var(--vino-tinte);padding:8px 12px;
  border-radius:8px;font-size:13px}
/* La traza y el snapshot: texto técnico, siempre sobre fondo oscuro para que se
   lea como lo que es —una salida de consola— y no como parte de la interfaz. */
.traza{white-space:pre-wrap;font-size:11px;background:#1E1E24;color:#E4E4E7;
  padding:12px;border-radius:8px;overflow:auto;max-height:260px;margin-top:8px;
  font-family:ui-monospace,SFMono-Regular,Consolas,monospace}
/* Un registro ya atendido sigue visible pero deja de pedir atención. */
.apagado{opacity:.55}
/* Monitoreo en estilo compacto (2026-10-05): cada proceso es un punto de color
   con su nombre, y el mensaje del error es el dato de su renglón. */
.proc-punto{display:inline-flex;align-items:center;gap:7px;font-size:13px;font-weight:600;
  white-space:nowrap}
.proc-punto::before{content:"";width:8px;height:8px;border-radius:50%;flex:none;
  background:var(--ok)}
.proc-punto.malo::before{background:var(--peligro)}
.proc-punto-estado{font-weight:500;font-size:12px;color:var(--tenue)}
.proc-punto.malo .proc-punto-estado{color:var(--peligro-texto,var(--peligro));font-weight:700}
.reg-mensaje-fila{font-weight:600}

/* Permisos por módulo (formulario de usuario). `fieldset` sin borde propio: el
   borde lo lleva cada grupo, que es la unidad que se lee. */
/* ⚠️ En columnas y no apilados (2026-09-16). Los nueve grupos de permisos en una
   sola columna eran ~700 px: había que scrollear para ver si un usuario tenía
   Nómina, y la mitad derecha del formulario estaba vacía. `auto-fill` y no
   `auto-fit` a propósito: con `auto-fit`, un grupo solo en la última fila se
   estira a todo el ancho y se ve como otra cosa (la misma lección que dejaron
   los KPI el 2026-09-16). */
.perms{border:0;padding:0;margin:0;display:grid;gap:12px;
  grid-template-columns:repeat(auto-fill,minmax(260px,1fr))}
.perms.apagado{pointer-events:none}
.perm-group{border:1px solid var(--borde);border-radius:var(--r-control,10px);
  padding:10px 14px}
.perm-nombre{font-weight:600;margin-bottom:2px}
.perm-items{display:flex;flex-wrap:wrap;gap:10px 22px;margin-top:8px}
.perm-check{display:flex;gap:7px;align-items:center;font-weight:400;cursor:pointer}
.tema-bento .perm-group{border-color:var(--linea-control)}

/* ── Tarjetas de navegación (`ui/Tarjeta.jsx`) ────────────────── */
/* La portada de Ajustes: una tarjeta por catálogo y por pantalla del módulo.
   Volvieron el 2026-08-31 a pedido del usuario («cada catálogo con su propia
   tarjeta como era antes, se ve más bonito»). NO son las `.setcard` viejas: esas
   eran de ancho fijo (200px) y del sistema de color anterior, donde `--vino` era
   el rojo. Estas usan los tokens del tema y una rejilla que se acomoda sola.

   `auto-fill` y no `auto-fit`: con 11 catálogos y una ventana ancha, `auto-fit`
   estiraría las últimas dos tarjetas de la fila hasta el ridículo. */
/* El rótulo de cada grupo de tarjetas. Chico a propósito: separa, no compite
   con el h1 de la pantalla. */
/* Separador de sección. `margin-top` para que se despegue del bloque anterior:
   sin él, «Composición de la venta» quedaba pegado a la tabla de arriba y se
   leía como su pie en vez de como el título de lo que sigue. */
.tarjetas-tt{font-size:15px;font-weight:700;margin:22px 0 12px;letter-spacing:-.01em}
.tarjetas-tt:first-child{margin-top:0}
.tarjetas-tt + .tarjetas{margin-top:0}
.tarjetas{display:grid;gap:14px;margin-bottom:26px;
  grid-template-columns:repeat(auto-fill,minmax(212px,1fr))}
.tarjeta{display:flex;flex-direction:column;gap:2px;text-decoration:none;
  background:var(--panel);border:1px solid var(--borde);border-radius:var(--r-control,12px);
  padding:16px 18px;color:var(--texto);
  transition:transform .3s var(--ease-spring), border-color .2s ease,
             box-shadow .3s var(--ease-spring)}
.tarjeta:hover{transform:translateY(-3px);border-color:var(--vino);
  box-shadow:0 8px 20px rgba(34,38,46,.10)}
.tarjeta:focus-visible{outline:2px solid var(--vino);outline-offset:2px}
/* ⚠️ `margin-bottom:auto`: el icono se queda arriba y el nombre se apoya en el
   piso de la tarjeta, así los nombres de una fila quedan todos a la misma
   altura aunque uno ocupe dos renglones («Precios por cliente y variedad»
   junto a «Países»). Antes el texto flotaba a distinta altura en cada una
   (2026-09-16). */
.tarjeta-ico{font-size:24px;line-height:1.2;margin-bottom:auto;padding-bottom:6px}
/* `block` y no `flex`: con flex, un título de dos renglones («Precios por
   cliente y variedad», «Categorías de ajuste (Cartera)») empujaba el conteo al
   borde derecho de la tarjeta, lejos del texto. Así lo sigue como una palabra
   más. */
.tarjeta-tt{font-weight:700;font-size:14.5px}
/* El conteo no compite con el nombre: es el dato secundario de la tarjeta. */
.tarjeta-n{display:inline-block;margin-left:7px;vertical-align:1px;
  font-size:12px;font-weight:600;color:var(--tenue);
  background:var(--fondo);border-radius:999px;padding:1px 8px;
  font-variant-numeric:tabular-nums}
.tarjeta-ds{color:var(--tenue);font-size:12.5px;margin-top:2px}
/* Tarjetas compactas (`<Tarjetas compactas>`, Ajustes, 2026-10-05): el icono
   a la izquierda ocupando las dos líneas, y a su lado el nombre con el conteo
   y, si la tarjeta lo trae, su explicación en una sola línea. */
.tarjetas-compactas{gap:10px;margin-bottom:18px;
  grid-template-columns:repeat(auto-fill,minmax(230px,1fr))}
.tarjetas-compactas .tarjeta{display:grid;grid-template-columns:24px minmax(0,1fr);
  column-gap:11px;align-items:center;align-content:center;min-height:50px;padding:10px 16px}
.tarjetas-compactas .tarjeta-ico{grid-row:1 / span 2;font-size:19px;margin:0;padding:0;
  text-align:center}
.tarjetas-compactas .tarjeta-tt{font-size:14px;line-height:1.25}
.tarjetas-compactas .tarjeta-ds{grid-column:2;margin-top:1px;font-size:12px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tarjetas-compactas + .tarjetas-tt,.tarjetas-tt:has(+ .tarjetas-compactas){margin-top:6px;margin-bottom:9px}
.tarjetas-tt:first-child:has(+ .tarjetas-compactas){margin-top:0}
.tema-bento .tarjeta{border:none;border-radius:var(--r-tile)}
.tema-bento .tarjeta:hover{background:var(--t-lila)}
.tema-bento .tarjeta:hover .tarjeta-tt{color:var(--a-lila)}
.tema-bento .tarjeta-n{background:var(--fondo)}
.tema-bento .tarjeta:hover .tarjeta-n{background:var(--panel)}

/* ── Barra de módulo (nivel 2 de la navegación) ───────────────── */
/* El sidebar lista los MÓDULOS y esta barra las PANTALLAS del módulo en el que
   estás. Antes las pantallas colgaban del sidebar y el riel llegaba a 17 filas;
   el usuario lo rechazó por saturado el 2026-08-28. La dibuja
   `frontend/src/ui/BarraModulo.jsx`, desde el Shell: una sola barra para los
   once módulos.
   `sticky` y no `fixed`: el sidebar es `fixed` y ya reserva su riel con el
   `padding-left` del body; una barra fija además tendría que repetir esa cuenta
   y se rompería en la vista angosta, donde el sidebar pasa a `static`. */
.barra-mod{position:sticky;top:0;z-index:30;background:var(--panel);
  border-bottom:1px solid var(--borde)}
/* Mismo `max-width` que `.content`: la barra y la pantalla arrancan alineadas. */
.barra-mod-in{display:flex;align-items:center;gap:20px;height:52px;padding:0 28px;
  max-width:1600px;margin:0 auto;
  /* Nómina tiene 8 pantallas y Compras 6: en una ventana angosta la barra se
     desliza en vez de apretar los nombres o partirse en dos filas. */
  overflow-x:auto;scrollbar-width:none}
.barra-mod-in::-webkit-scrollbar{display:none}
.barra-modulo{display:flex;align-items:center;gap:7px;font-size:13px;font-weight:700;
  color:var(--texto);white-space:nowrap;flex-shrink:0;padding-right:20px;
  border-right:1px solid var(--borde);height:26px}
.barra-modulo .navicon{width:16px;height:16px;opacity:.75}
.barra-pags{display:flex;align-items:center;gap:4px}
.barra-pag{text-decoration:none;white-space:nowrap;font-size:13px;color:var(--tenue);
  padding:6px 13px;border-radius:999px;
  transition:background .12s ease, color .12s ease}
.barra-pag:hover{background:var(--fondo);color:var(--texto)}
.barra-pag:focus-visible{outline:2px solid var(--vino);outline-offset:2px}
/* La píldora activa. `--t-lila`/`--a-lila` es el par ya medido del tema (4.96:1)
   y el mismo que usa `.chip-op.activo`: la barra no inventa un color propio. */
.barra-pag.on{background:var(--vino-tinte);color:var(--vino);font-weight:700}
.tema-bento .barra-pag.on{background:var(--t-lila);color:var(--a-lila)}

.content{padding:30px 28px;max-width:1600px;margin:0 auto}
/* 🔑 **Las dos barras horizontales, juntas** (2026-09-30, pedido del usuario).
   La de módulo mide 52 px y `.content` abría con 30 px de aire: entre las dos
   quedaba una franja blanca que no decía nada y que en una pantalla de lista se
   paga en filas. Baja a 8.
   ⚠️ **Solo donde hay barra de trabajo**, con `:has()`: las otras 37 pantallas
   abren con un título y ahí los 30 px separan la barra del encabezado, que es
   para lo que estaban. */
.content:has(> .barra-trabajo){padding-top:8px}
h1{font-family:'Manrope',"Segoe UI",sans-serif;font-size:26px;font-weight:800;
  letter-spacing:-0.015em;margin-bottom:4px;line-height:1.15}
/* ⚠️ `margin-top:0` a propósito: cuando `.sub` es un `<p>`, el navegador le
   pone 1em arriba, y cuatro pantallas lo compensaban con un `marginTop`
   NEGATIVO escrito a mano (-4, -6, -8). Quitarlo de raíz es lo que hace que un
   subtítulo quede pegado a su título en todas por igual. */
.sub{color:var(--tenue);margin-top:0;margin-bottom:var(--esp-3,14px);font-size:14px}
.row-head{display:flex;justify-content:space-between;align-items:flex-start;
  gap:14px;flex-wrap:wrap;margin-bottom:18px}

/* ── `.barra-trabajo` — una sola franja arriba, y la tabla debajo ────
   Reemplaza a `.row-head` en las pantallas cuyo trabajo es mostrar una lista
   larga. Las dos conviven: la que no se convierte, no cambia.

   🔴 **38 px, y el número sale de una medición.** Con Playwright a
   1440 × 900 el 2026-09-29, el primer dato empezaba a los **343 px** en Cartera
   y a los **197** en Facturas — y de esos, la fila explica 44 en las dos. O sea
   que el alto de fila NO era el problema. Los 343 son la cabecera (65), las
   tarjetas de KPI (78), el aviso de vencidas (48), la cabecera del panel (57) y
   el aire del panel (95). Esta barra se come los tres primeros.

   ⚠️ **Fija arriba**: la tabla scrollea por debajo y el título no se va. Por
   eso el `z-index` y el fondo opaco — sin fondo, las filas se leen a través.
   📌 La tabla de abajo conserva su `altoScroll="alta"`, que la estira hasta el
   fondo de la ventana: así la página NO scrollea y el `thead` se sigue pegando
   dentro de su propia caja, como siempre. La ficha de diseño pedía sacarlo
   «para que la página scrollee entera», y eso traía de vuelta el scroll doble
   y despegaba el encabezado sobre las filas. */
:root{
  --barra-trabajo-alto:38px;   /* el contenido de la franja */
}
.barra-trabajo{
  position:sticky;top:0;z-index:30;
  display:flex;align-items:center;gap:14px;
  min-height:var(--barra-trabajo-alto);
  margin:0 0 10px;padding:0 0 6px;
  background:var(--fondo,#fff);
  border-bottom:1px solid var(--borde,#e6e6ef);
}
/* Un color de catálogo mostrado como color (Ajustes → Fincas, 2026-10-05). */
.punto-color{display:inline-flex;align-items:center;gap:7px}
.punto-color-muestra{width:10px;height:10px;border-radius:50%;flex:none;
  background:var(--tenue)}
/* La segunda línea de la franja (Novedades y Reportes de Nómina, 2026-10-05):
   los filtros, cuando no entran en la misma línea que los chips de arriba. A
   1366 px no entraban: cuatro o cinco chips, dos o cuatro desplegables y dos
   fechas. Una línea de 34 px sigue siendo la mitad de la tarjeta de filtros
   con rótulos encima que había. */
.barra-linea2{display:flex;align-items:center;gap:8px 12px;flex-wrap:wrap;margin:0 0 10px;
  padding-bottom:8px;border-bottom:1px solid var(--borde,#e6e6ef)}
/* La raya que cierra la franja va DEBAJO de la segunda línea: si quedara entre
   las dos, los filtros se leerían como parte de la tabla y no de la barra. */
.barra-trabajo:has(+ .barra-linea2){border-bottom:none;margin-bottom:0;padding-bottom:2px}
.tema-bento .barra-linea2 .desp-btn{height:34px}
/* Un chip que NAVEGA (las pestañas de Novedades y de Reportes): es un `<a>`, no
   un `<button>`, con la misma cara que `ChipOpcion`. */
a.chip.chip-op{text-decoration:none}
/* La miga («Ajustes ›») va pegada al título, en gris: dice de dónde venís sin
   competir con dónde estás (`<BarraTrabajo miga>`, 2026-10-05). */
.barra-miga{display:inline-flex;align-items:center;gap:6px;margin-right:-6px;
  font-size:13.5px;font-weight:600;color:var(--tenue);white-space:nowrap;flex:0 0 auto}
.barra-miga a{color:inherit;text-decoration:none}
.barra-miga a:hover{color:var(--vino);text-decoration:underline}
.barra-miga a:focus-visible{outline:2px solid var(--vino);outline-offset:2px;border-radius:3px}
/* El título NO crece: lo que crece es el hueco entre las cifras y la acción. */
.barra-trabajo>h1{
  margin:0;font-size:19px;line-height:1.2;white-space:nowrap;flex:0 0 auto}
.barra-trabajo-cifras{
  display:flex;align-items:center;gap:16px;flex:1 1 auto;min-width:0;
  overflow:hidden}
.barra-trabajo-acts{
  display:flex;align-items:center;gap:7px;flex:0 0 auto;margin-left:auto}

/* Cada cifra: la etiqueta chica y el valor al lado, en una línea. En Cartera
   eran cuatro tarjetas de 78 px de alto para cuatro números que caben acá. */
.barra-cifra{display:inline-flex;align-items:baseline;gap:6px;white-space:nowrap}
.barra-cifra-et{color:var(--tenue);font-size:11px;
  text-transform:uppercase;letter-spacing:.04em}
.barra-cifra-val{font-weight:700;font-size:14px;font-variant-numeric:tabular-nums}
/* Los tonos son los tokens que la pantalla YA usaba, no colores nuevos.
   ⚠️ `--peligro-texto` y no `--peligro`: el rojo de relleno da 3,48:1, que
   alcanza para un componente gráfico (WCAG 1.4.11) y NO para texto. Acá se
   escribió mal la primera vez y lo cazó `test_el_rojo_de_relleno_no_se_usa_como
   _color_de_texto`, que existe justamente para eso. */
.barra-cifra.peligro .barra-cifra-val{color:var(--peligro-texto,var(--peligro))}
.barra-cifra.ok .barra-cifra-val{color:var(--ok-texto,var(--ok))}

/* Los filtros de la lista de Compras, dentro de la franja (2026-10-05). Son
   los mismos campos de `.fl-field`, un poco más angostos y bajos para que
   entren en la línea con el conteo y los dos botones a 1366 px. */
.barra-filtros{display:flex;align-items:center;gap:6px;margin:0;flex:0 1 auto;min-width:0}
.barra-filtros .fl-wide input{width:190px}
.barra-filtros .fl-field:not(.fl-wide) input:not([type=date]){width:150px}
.barra-filtros .fl-field input[type=date]{width:128px}
.tema-bento .barra-filtros .fl-field input{height:34px}
/* La lista de Compras en una sola tabla (2026-10-05): sin las tarjetas, el
   primer renglón de cada pedido abre con una raya más marcada y el total del
   pedido va en negrita sobre el fondo. Es lo que dice dónde empieza y termina. */
.tema-bento table tr.pedido-ini>td{border-top:1px solid var(--borde)}
.tema-bento table tr.pedido-total>td{font-weight:700;background:var(--fondo)}
/* Una columna que no se parte en dos renglones (códigos, fechas, cifras): se
   pone con `clase: "col-sin-salto"` en la columna de `Tabla`. */
.col-sin-salto{white-space:nowrap}

/* Una nota corta dentro de la franja, con un enlace que cambia la vista
   (Movimiento ICA, 2026-10-05: «desde 01/07/2026 · ver todo»). Era una fila
   entera de texto entre la cabecera y la tabla. */
.barra-nota{font-size:12.5px;color:var(--tenue);white-space:nowrap}
/* Un botón que se lee como un enlace: hace algo en la pantalla, no navega. */
.enlace-boton{border:none;background:none;padding:0;font:inherit;cursor:pointer;
  color:var(--vino);font-weight:700}
.enlace-boton:hover{text-decoration:underline}
.enlace-boton:focus-visible{outline:2px solid var(--vino);outline-offset:2px;border-radius:3px}

/* ── La cifra que además FILTRA (`<Cifra onClick>`) ───────────────────────
   Nació el 2026-09-30 en Siigo → Facturas, donde los cuatro números YA eran
   los filtros de la lista desde el 09-23: al bajarlos a la barra tenían que
   seguir apretándose, o se volvía a tener el número arriba y el control abajo.
   Es un `<button>`, así que hay que apagarle lo que el navegador le pone —sin
   `font:inherit` la etiqueta y el valor caen al tamaño de un botón—.
   ⚠️ El activo NO se marca solo con color: lleva el tinte **y** una barra
   abajo. Con cuatro pegados en una línea de 38 px, el tinte solo no dice cuál
   está puesto; y el acento es de cada empresa, así que no puede ser la única
   señal. Misma receta que `.kseg-filtro.activo` y `.chip-op.activo`. */
.barra-cifra-filtro{border:none;background:none;font:inherit;cursor:pointer;
  padding:3px 8px;border-radius:6px;transition:background .15s ease}
.barra-cifra-filtro:hover{background:var(--fondo)}
.barra-cifra-filtro.activo{background:var(--vino-tinte);
  box-shadow:inset 0 -3px 0 var(--vino)}
.barra-cifra-filtro.activo .barra-cifra-et,
.barra-cifra-filtro.activo .barra-cifra-val{color:var(--vino)}
/* El tono del semáforo manda sobre el del activo: una cifra en rojo que está
   puesta como filtro sigue en rojo, que es lo que hay que ver. */
.barra-cifra-filtro.activo.peligro .barra-cifra-val{
  color:var(--peligro-texto,var(--peligro))}
/* Las cifras-filtro van pegadas, no separadas por 16 px: juntas se leen como
   UN control de cuatro posiciones, que es lo que son. */
.barra-trabajo-cifras:has(.barra-cifra-filtro){gap:2px}

/* ── Un CAMPO dentro de la franja (`<CampoBarra>`) ────────────────────────
   Nació el 2026-09-30 para Archivo plano OPD, donde el panel «Datos del
   archivo» se llevaba **180 px para dos campos cortos** —la fecha de las
   facturas y la hora del archivo— y empujaba el primer dato a los 442 px, el
   peor de las cinco pantallas de Logística.
   ⚠️ **El rótulo va al lado y no encima**: apilado necesita 34 px y la franja
   tiene 38. Al lado, el par entero entra en una línea.
   ⚠️ Y el `input` **no hereda** el alto de los de formulario: los de la hoja
   miden 38 px con su relleno, que es toda la franja. Acá van a 24. */
.barra-campo{display:inline-flex;align-items:center;gap:7px;white-space:nowrap}
.barra-campo>label,.barra-campo>.barra-campo-rotulo{color:var(--tenue);
  font-size:11px;text-transform:uppercase;letter-spacing:.04em;margin:0}
.barra-campo>input{height:24px;padding:0 8px;font-size:12.5px;
  border:1px solid var(--borde);border-radius:6px;
  background:var(--panel);color:var(--texto);font-family:inherit;
  font-variant-numeric:tabular-nums}
.barra-campo>input:focus-visible{outline:2px solid var(--vino);outline-offset:1px}
/* El de fecha trae su propio icono y necesita algo más de aire a la derecha. */
.barra-campo>input[type=date]{padding-right:4px}

/* A 1366 y abajo las cifras pueden no entrar. Se recortan con scroll propio en
   vez de empujar la acción fuera de la pantalla: el bug de las filas del
   2026-09-17 aparecía a 1366 y no a 1440, y por eso esto se mide a los dos. */
@media (max-width:1400px){
  .barra-trabajo-cifras{overflow-x:auto;scrollbar-width:none}
  .barra-trabajo-cifras::-webkit-scrollbar{display:none}
}
/* El emoji de un botón de la barra (`.btn-ico`) se va a 1200 px o menos.
   Nació el 2026-10-01 en `/siigo`, la única de las seis pantallas con barra que
   no entraba a 1152: con cinco botones, las cifras de la izquierda quedaban
   cortadas detrás de su scroll escondido («TODAS 4» sin el 4). Decisión del
   usuario entre esto, acortar «Generar Ventas diarias» o dejarlo. El ícono va en
   su propio `span` con `aria-hidden`, así que el lector de pantalla no lo leía
   antes y no pierde nada ahora. Lo mide `scripts/verificar_alto_fila_siigo.py`. */
@media (max-width:1200px){
  .barra-trabajo-acts .btn-ico{display:none}
}
@media (max-width:760px){
  .barra-trabajo{flex-wrap:wrap;min-height:0;padding-top:8px}
  .barra-trabajo>h1{flex:1 1 100%}
}

/* ── Separación entre bloques hermanos ─────────────────────────────
   Tres arriba y tres abajo, con los valores de `tokens.css`. Existen para
   que ningún JSX tenga que escribir un número: el que escribe una pantalla
   elige «poco, normal o mucho», no 13 px. Lo cuida
   `test_consistencia_visual.py`. */
.sep-sm{margin-top:var(--esp-1,6px)}
.sep{margin-top:var(--esp-2,10px)}
.sep-lg{margin-top:var(--esp-3,14px)}
.sep-abajo-sm{margin-bottom:var(--esp-1,6px)}
.sep-abajo{margin-bottom:var(--esp-2,10px)}
.sep-abajo-lg{margin-bottom:var(--esp-3,14px)}

/* Un formulario que vive DENTRO de otra cosa —una celda de tabla, una fila de
   acciones— y no debe abrir espacio a su alrededor. Estaba escrito a mano como
   `style={{margin:0}}` en once pantallas. `.form-fila` es el mismo caso cuando
   además alinea a sus hijos en una línea. */
.form-plano{margin:0}
/* Lo mismo para lo que no es un formulario: un párrafo que ya vive dentro de
   una caja con su propio relleno y no necesita el margen del navegador. */
.sin-margen{margin:0}
.form-fila{display:flex;gap:8px;align-items:center;margin:0}
/* Un control que ocupa el ancho de su contenedor (los botones y los campos de
   los modales). No se llama `ancho-total` para no confundirse con
   `.tile.ancho-total`, que es otra cosa: esa ocupa la fila de la rejilla. */
.ancho-completo{width:100%}
/* Empuja un elemento al final de su fila flex sin tener que escribir
   `margin-left:auto` a mano. */
.a-la-derecha{margin-left:auto}
/* La contracara: empuja el elemento al PRINCIPIO de su fila flex, separándolo
   del resto. La usa el botón «Eliminar» del pie de un modal, que va lejos de
   «Guardar» a propósito. */
.a-la-izquierda{margin-right:auto}
/* ⚠️ Texto en rojo. Existe porque siete pantallas lo pedían inline con el rojo
   de RELLENO, que da 3,48:1 sobre blanco cuando AA pide 4,5 para texto. Escrito
   en el JSX, ningún guardián de contraste del CSS las veía. Acá usa
   `--peligro-texto`, el mismo rojo de la marca, que pasa AA (2026-09-15). */
.texto-peligro{color:var(--peligro-texto,var(--peligro))}
.texto-alerta{color:var(--alerta)}
.texto-tenue{color:var(--tenue)}
/* Un formulario que va en la misma línea que otra cosa (dos botones de acción
   en la misma celda, cada uno con su propio POST). */
.en-linea{display:inline}
/* Una fila de botones apretada dentro de una celda: menos separación que
   `.acts`, que es la de las cabeceras. */
.acts-juntos{display:flex;gap:6px;justify-content:flex-end}
.texto-ok{color:var(--ok)}
.valor-fuerte{font-weight:700}
/* La caja de los estados de pantalla: «no hay nada» y «no se pudo cargar».
   Un solo relleno para los dos, que es lo que los hace ver hermanos. */
.estado-caja{padding:18px 20px;color:var(--tenue)}
/* ⚠️ `.panel` no trae relleno a propósito: casi siempre contiene una tabla, que
   pone el suyo. Cuando adentro va TEXTO hay que dárselo, o queda pegado a los
   bordes — en «Importar empleados» el párrafo de columnas opcionales llegaba a
   comerse su primera letra (2026-09-16). */
.panel-cuerpo{padding:16px 20px}

/* ── El campo de archivo ───────────────────────────────────────────────────
   Los nueve `input[type=file]` del sistema mostraban el botón que dibuja el
   sistema operativo, que al lado del resto se ve de otro programa — el mismo
   motivo por el que el `<select>` nativo se reemplazó por `Desplegable`
   (2026-09-16). Acá alcanza con `::file-selector-button`: el botón toma el
   aspecto del sistema y el control sigue siendo el nativo, con su diálogo y su
   accesibilidad intactos. ⚠️ El TEXTO del botón lo pone el navegador según su
   idioma y no hay CSS que lo cambie; en un Chrome en español dice «Elegir
   archivo». */
input[type=file]{font-family:inherit;font-size:13px;color:var(--tenue);
  padding:7px 10px}
input[type=file]::file-selector-button{font-family:inherit;font-size:13px;
  font-weight:700;color:var(--vino);background:var(--vino-tinte);border:none;
  border-radius:8px;padding:7px 12px;margin-right:10px;cursor:pointer;
  transition:filter .12s ease}
input[type=file]::file-selector-button:hover{filter:brightness(.97)}
.estado-error{font-weight:600;margin:0 0 var(--esp-1,6px)}
/* Lo contrario del error: «no hay nada que revisar» es una buena noticia. */
.estado-bien{text-align:center;color:var(--ok);font-weight:600;padding:30px}
.estado-centrado{text-align:center}
/* Las salidas de una pantalla en error: a la izquierda, que es por donde se
   sigue leyendo. `.acts` no sirve acá porque alinea a la derecha. */
.estado-acciones{display:flex;gap:7px;flex-wrap:wrap}
/* El pie del resumen de Transporte: un párrafo de datos, más chico que el
   cuerpo y con más aire entre renglones. */
.transp-resumen{font-size:13px;line-height:1.6}
/* Un formulario de una línea con sus campos alineados abajo: el de las notas de
   Ajustes, que vive dentro de la tarjeta y no arriba de ella. */
.form-fila-campos{display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap;
  margin-top:var(--esp-2,10px)}
/* El pie de una celda: el detalle chico debajo del dato principal. */
.pie-celda{font-size:11px;color:var(--tenue);margin-top:2px}
/* El historial plegable de una cuenta, que va indentado adentro de su tarjeta. */
.collapse.adentro{margin:0 20px var(--esp-1,6px)}
/* Los campos de un modal: el rótulo arriba, el control ocupando el ancho. Vivía
   escrito control por control en el modal de Transporte. */
.campos-modal label{display:block;margin-bottom:4px}
.campos-modal input,.campos-modal textarea{width:100%;margin-bottom:var(--esp-1,6px)}
/* El desplegable propio, si algún día entra a un modal, se estira igual. No es
   un `<select>` nativo: en las pantallas migradas no queda ninguno. */
.campos-modal .desp{width:100%;min-width:0;margin-bottom:var(--esp-1,6px)}
.campos-modal textarea{margin-bottom:var(--esp-2,10px)}

/* ── Botones ──────────────────────────────────────────────────── */
.btn{display:inline-block;background:var(--vino);color:#fff;border:none;text-decoration:none;
  padding:10px 18px;border-radius:9px;font-size:14px;font-weight:600;cursor:pointer;
  transition:transform .2s var(--ease-spring), background .15s ease}
.btn:hover{background:var(--vino-h)}
.btn:active{transform:scale(.95)}
.btn.ghost{background:transparent;color:var(--vino);border:1px solid var(--vino)}
.btn.ghost:hover{background:var(--vino-tinte)}
.btn.sm{padding:6px 12px;font-size:13px}
.btn.gray{background:#eef0f3;color:var(--texto)}
.btn.danger{background:transparent;color:var(--peligro-texto,var(--peligro));
  border:1px solid var(--peligro)}
.btn.danger:hover{background:#fdecea}
.btn:disabled,.btn[disabled]{opacity:0.4;cursor:not-allowed;pointer-events:none}
.btn.on    {background:var(--vino);color:#fff;box-shadow:none}
.btn.active{background:var(--vino);color:#fff;box-shadow:none}

/* ── Cards / métricas ─────────────────────────────────────────── */
.cards{display:flex;gap:16px;flex-wrap:wrap;margin-bottom:26px}
.card{background:var(--panel);border:1px solid var(--borde);border-radius:10px;
  padding:18px 20px;min-width:170px}
.card .v{font-size:30px;font-weight:800;color:var(--vino)}
.card .l{color:var(--tenue);font-size:13px}
.setcard{text-decoration:none;color:var(--texto);width:200px;display:block}
.setcard .ic{font-size:26px}
.setcard .tt{font-weight:700;margin-top:6px;color:var(--vino)}
.setcard .ds{color:var(--tenue);font-size:12px;margin-top:2px}

/* ── Tablas ───────────────────────────────────────────────────── */
.panel{background:var(--panel);border:1px solid var(--borde);border-radius:10px;overflow:hidden}
/* Scroll horizontal con indicador de fade — 100% CSS, sin JS. Doble
   degradado: uno "tapa" (mismo color que el fondo, se mueve CON el
   contenido vía background-attachment:local — desaparece cuando ya no hay
   más para scrollear de ese lado) y otro "sombra" (fijo respecto al
   contenedor vía background-attachment:scroll — siempre visible en el
   borde, pero tapado por el degradado anterior cuando no hace falta).
   Sin min-width propio a propósito: el ancho natural del padding+contenido
   de las columnas ya fuerza el scroll cuando no entra, y bajo 900px ICA/
   Facturas heredan min-width:560px de .panel table — evita competir con
   ese min-width condicional que ya usa .panel para otras tablas del sistema. */
.tabla-scroll{
  overflow-x:auto;
  background:
    linear-gradient(to right, var(--panel) 30%, rgba(255,255,255,0)),
    linear-gradient(to right, rgba(255,255,255,0), var(--panel) 70%) 100% 0,
    linear-gradient(to right, rgba(34,38,46,.12), rgba(255,255,255,0) 10%),
    linear-gradient(to left, rgba(34,38,46,.12), rgba(255,255,255,0) 10%) 100% 0;
  background-repeat:no-repeat;
  background-size:40px 100%, 40px 100%, 14px 100%, 14px 100%;
  background-attachment:local, local, scroll, scroll;
}
table{width:100%;border-collapse:collapse}
th{background:var(--fondo);color:var(--tenue);text-align:left;font-size:12px;
  padding:11px 14px;font-weight:700;text-transform:uppercase;letter-spacing:.3px}
td{padding:11px 14px;border-top:1px solid var(--borde);font-size:14px}
tr:hover td{background:var(--fondo)}
/* ⚠️ `flex-wrap` acá y no solo en el media query de móvil: la cabecera de
   Informes por finca lleva cinco filtros y sacaba la página 18px de lado en una
   pantalla de 1024 (medido el 2026-09-16), que es justo el ancho que NO entra en
   ninguna de las dos ramas. Envolver es lo que hace que la cabecera se acomode
   sola a cualquier ancho en vez de tener un umbral escrito a mano. */
.acts{display:flex;gap:7px;flex-wrap:wrap;justify-content:flex-end;align-items:flex-end}
/* 🔴 Dentro de una CELDA no envuelve nunca (2026-09-21). El `flex-wrap:wrap` de
   arriba es para las cabeceras, donde tener dos filas de botones está bien; en
   una celda de tabla, envolver estira la fila entera. Medido en navegador el
   09-19: a 1152 px la columna de acciones de `/facturas` apilaba «Abrir» sobre
   el menú de tres puntos y TODAS las filas pasaban de 44 a 77 px, con nombres
   cortos y largos por igual. Esos dos botones juntos miden poco más de 100 px:
   entran siempre, lo que sobraba era el permiso para partirlos. */
td .acts{flex-wrap:nowrap}

/* Tinte por tipo de cliente en Facturas (2026-07-14, refinado 2026-07-15;
   cambiado de verde/azul a ambar/purpura el mismo dia -- el usuario pidio
   "otros 2 colores diferentes" para no confundirse con verde=ok/naranja=aves
   de otros badges del sistema). Nacional = ambar (#B8860B), Exportación =
   púrpura (#5B4B8A) — pareja cálido/frío elegida por el usuario entre 3
   opciones. Borde izquierdo de 5px + fondo con cuerpo suficiente para
   distinguirse de un vistazo. */
.td-num{font-variant-numeric:tabular-nums;white-space:nowrap}
.td-money{color:var(--texto);font-variant-numeric:tabular-nums;font-weight:700;white-space:nowrap}


/* Menú de 3 puntos por factura (Facturas → lista). #menu-flotante es un
   único panel "portal" pegado directo al <body> (no dentro de la tabla),
   con position:fixed y coordenadas puestas por JS — inmune a cualquier
   overflow/transform de contenedores padres (tabla, tarjetas, sidebar),
   sin importar qué tan chico sea ese contenedor.
   🔴 **Esta regla estuvo MUERTA del 2026-08-21 al 2026-10-05.** Encima de ella
   quedó la segunda línea de `.dots-btn{…}` sin la primera (`f6fafaeb`), el
   navegador la leyó como parte del selector y descartó `.dots-panel` entera.
   No se notó porque `ui/Menu.jsx` pone casi todo en línea; lo único que se
   perdía era el `z-index`, y el 2026-10-05 la grilla de Despachos pintó su
   encabezado encima del menú «Etiquetas». La capa del modo portal la pone
   ahora `Menu.jsx` en línea (1100, la de los paneles flotantes). Al revivirla
   se le sacó el `min-width:230px`: nunca se aplicó en esas seis semanas y
   habría agrandado los menús que piden 170 o 200 px. Lo cuida
   `test_css_sin_declaraciones_huerfanas.py`. */
.dots-panel{display:none;position:fixed;background:var(--panel);
  border:1px solid var(--borde);border-radius:10px;
  box-shadow:0 10px 28px rgba(34,38,46,.16);padding:6px;z-index:500}
.dots-panel.open{display:block}
.dots-panel .dots-titulo{font-size:11px;font-weight:700;color:var(--tenue);
  text-transform:uppercase;letter-spacing:.3px;padding:6px 10px 4px}
.dots-panel a,.dots-panel button{display:block;width:100%;text-align:left;
  background:transparent;border:none;padding:8px 10px;border-radius:7px;
  font-size:13.5px;color:var(--texto);text-decoration:none;cursor:pointer;
  font-family:inherit}
.dots-panel a:hover,.dots-panel button:hover{background:var(--fondo);color:var(--vino)}
.dots-panel form{margin:0}
/* `.item-menu` es la clase de `ui/Menu.jsx::ItemMenu`. Hereda el mismo estilo
   que los `a`/`button` del panel: un ítem de menú se ve igual en todos los
   menús del sistema, venga de Jinja o de React. */
.dots-panel .item-menu{display:block;width:100%;text-align:left}

/* ── El desplegable de «Archivo plano» ────────────────────────────────────
   Se ve como el de «Lugar de entrega» (pedido del usuario, 2026-09-30), pero
   lleva dos cosas que ese no tiene: un aviso cuando alguna factura marcada no
   tiene la dirección completa, y la hora que entra en la cabecera del archivo.
   ⚠️ El `<form>` es UNO solo para todas las bodegas —las facturas marcadas y
   la hora son campos compartidos— así que no puede usar `ItemMenuPost`, que
   arma un form por fila. */
/* 🔑 **El `<form>` hereda el `flex` del panel, y por eso existe esta regla.**
   `ui/Menu.jsx` arma su panel con `display:flex; flex-direction:column;
   gap:10px` EN LÍNEA, así que en «Lugar de entrega» cada ítem es hijo directo
   y recibe esa separación. Acá los botones cuelgan de un `<form>`, que es UN
   solo hijo: sin esto las filas salen pegadas —medido, 33 px contra 42— y los
   dos menús no se ven hermanos aunque compartan todo lo demás. */
.opd-menu{display:flex;flex-direction:column;gap:10px}
/* ⚠️ **Y la altura de línea va fija, por el ⚠ de las bodegas apagadas.** Con
   `line-height:normal` la caja la decide el glifo más alto de la fila: medido,
   las filas con el símbolo daban 34 px y las de texto pelado 32, dentro del
   MISMO menú. 16 px es lo que `normal` da para los 13.5 px de la regla de
   arriba, así que las filas sin símbolo no se mueven ni un píxel. */
.opd-menu .item-menu{line-height:16px}
.opd-menu-aviso{font-size:12px;line-height:1.4;padding:0 10px;
  color:var(--peligro-texto,var(--peligro))}
.opd-menu-hora{display:flex;align-items:center;gap:8px;padding:0 10px;
  font-size:11px;font-weight:700;color:var(--tenue);
  text-transform:uppercase;letter-spacing:.3px}
.opd-menu-hora input{flex:1;min-width:0;height:26px;padding:0 8px;
  font-size:12.5px;font-weight:400;text-transform:none;letter-spacing:0;
  border:1px solid var(--borde);border-radius:6px;
  background:var(--panel);color:var(--texto);font-family:inherit}
/* ⚠️ Una bodega sin ID remitente queda apagada: `generar_plano()` levanta
   ValueError y no sale ningún archivo. Tiene que VERSE apagada, o se descubre
   recién al apretarla. */
.dots-panel .item-menu:disabled{opacity:.45;cursor:not-allowed}
.dots-panel .item-menu:disabled:hover{background:transparent}
/* Combo estricto (`ui/ComboEstricto.jsx`). Reemplaza al `<input list>` +
   `<datalist>`: el datalist lo dibuja el sistema operativo, igual que el
   `<select>`, y además no deja mostrar POR QUÉ un valor no sirve. */
.combo{position:relative;display:flex;flex-direction:column;gap:5px}
/* ⚠️ La POSICIÓN no está acá: la calcula `ui/usePanelFlotante.js` y la aplica en
   línea, porque la lista se dibuja en un portal (`position:fixed`) y no adentro
   del campo. Acá vive solo la apariencia. Si volviera a ser `position:absolute`,
   `.tabla-scroll` la recortaría de nuevo — se veían 7 px de 129 en el
   formulario de factura, medido el 2026-08-31. */
.combo-lista{margin:0;list-style:none;overflow-y:auto;background:var(--panel);
  border:1px solid var(--borde);border-radius:10px;padding:5px;
  box-shadow:0 4px 16px rgba(0,0,0,.10)}
.combo-lista li{padding:7px 10px;border-radius:7px;cursor:pointer;font-size:13px}
.combo-lista li.res,.combo-lista li:hover{background:var(--vino-tinte);color:var(--vino)}
.hint.invalido{color:var(--peligro-texto,var(--peligro));font-weight:600}
.tema-bento .combo-lista{border-radius:var(--r-control)}
.tema-bento .combo-lista li{border-radius:9px}

/* Modal (`ui/Modal.jsx`). Sube acá desde el `<style>` propio de 40 líneas que
   tenía `facturas/form.html` — el guardián de estilo prohibe esos bloques
   justamente para que no haya cinco modales distintos en el sistema. */
.mdl-overlay{position:fixed;inset:0;background:rgba(20,22,26,.45);display:flex;
  align-items:center;justify-content:center;z-index:var(--capa-dialogo);padding:20px}
.mdl-box{background:var(--panel);border-radius:12px;width:100%;
  max-height:85vh;display:flex;flex-direction:column;overflow:hidden;
  box-shadow:0 20px 50px rgba(0,0,0,.25)}
.mdl-head{display:flex;align-items:center;justify-content:space-between;
  padding:16px 20px;border-bottom:1px solid var(--borde)}
.mdl-head h3{margin:0;font-size:15px}
.mdl-close{background:none;border:none;font-size:16px;cursor:pointer;color:var(--tenue)}
.mdl-body{padding:16px 20px;overflow-y:auto}
.mdl-foot{display:flex;justify-content:flex-end;gap:8px;padding:14px 20px;
  border-top:1px solid var(--borde)}
.mdl-table{width:100%;border-collapse:collapse;font-size:13px}
.mdl-table th{text-align:left;font-size:11px;text-transform:uppercase;color:var(--tenue);
  padding:6px 8px;border-bottom:1px solid var(--borde)}
.mdl-table td{padding:6px 8px;border-bottom:1px solid var(--borde)}
.mdl-table .nr{text-align:right}
.mdl-hist{margin-top:14px;padding-top:10px;border-top:1px solid var(--borde)}
.mdl-hist-title{font-size:11px;text-transform:uppercase;color:var(--tenue);margin-bottom:6px}
/* `flex-wrap` en los dos: sin él, a 400 px el botón «Eliminar» se imprimía
   ENCIMA del campo Motivo -se leía «Eliseguinda p»- y la fila se salía del modal
   sin barra para alcanzarla. Medido en el navegador el 2026-09-14; es anterior
   al rediseño. A 1440 no cambia nada: solo se parte cuando no entra. */
.mdl-hist-row{font-size:12px;color:var(--tenue);padding:4px 0;
  display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.mdl-hist-edit{display:flex;align-items:center;gap:6px;flex:1;min-width:0;
  flex-wrap:wrap}
/* El motivo es el campo que se escribe, así que es el que tiene que ganar el
   ancho. A 1440 el mismo input cortaba el texto a ~90 px. */
.mdl-hist-edit input{flex:1 1 150px;min-width:150px}
.mdl-hist-variedad{flex-shrink:0;white-space:nowrap}
.mdl-hist-meta{flex-shrink:0;white-space:nowrap;font-size:11px}
/* La vista previa dentro del modal de «Generar cuenta de cobro»
   (`compras/Cuentas.jsx`, 2026-09-24). Solo SEPARA: la tabla, el chip y el pie
   los pone el sistema; acá no hay ni un color ni una tipografía propios.
   ⚠️ Nace con su regla acá y no en un `<style>` de la pantalla, que es como la
   lista de cuentas de cobro terminó dibujándose sin CSS doce días (2026-09-01).
   Lo cazó `test_consistencia_visual.py` en la corrida completa, antes del
   deploy: la clase estaba en el JSX y en ningún lado más. */
.cc-previa{margin-top:16px}
.cc-previa .hint{display:block;margin:8px 0 0}

.tema-bento .mdl-box{border-radius:var(--r-tile)}
.tema-bento .mdl-body input{height:32px}
/* ⚠️ Un icono es DECORATIVO: nunca puede ser el blanco de un clic.
   Sin esto, en un botón cuyo contenido es solo un `<svg>` el clic real del mouse
   llega con `target = <svg>` en vez del botón. Medido el 2026-08-21 con el menú
   de 3 puntos de la lista de Facturas: el botón no reaccionaba al mouse y SÍ a
   `boton.click()` desde JS — que dispara el evento con el botón como target. Un
   test de Vitest sobre el componente pasaba (ahí el clic sintético va al botón),
   así que solo se veía en un navegador de verdad, con un mouse de verdad.
   Va sobre todos los `svg` de un `.btn` y del panel, no sobre el caso puntual:
   el problema es de cualquier botón de solo-icono. */
/* ⚠️ `vertical-align` NO es cosmético: un `<svg>` es inline y se apoya en la
   línea base, así que el navegador le reserva debajo el espacio de la cola de
   una «g». Medido el 2026-09-16 en la lista de Facturas: el botón de tres
   puntos medía **31 px** contra los 27 del «Abrir» de al lado, y como era el
   elemento más alto de la fila, arrastraba a las 2.304 filas de la tabla y a
   todas las tablas del sistema con un botón de solo icono. */
.btn svg,.dots-panel svg,.item-menu svg{pointer-events:none;vertical-align:middle}
/* Y en el botón de SOLO icono se saca la caja de línea entera: con
   `vertical-align` quedaban 2 px, y son los que hacían que la fila de Facturas
   midiera 46 px contra los 44 de la lista de Clientes. `:only-child` acota la
   regla a ese caso: un botón con icono Y texto necesita que el icono siga en
   la línea del texto.
   El tamaño se fija en 14 px por lo mismo: los iconos del sistema se declaran
   unos en 14 y otros en 16, y el de 16 dejaba su botón 1 px más alto que el
   de al lado —una línea de texto de 13 px ocupa 15—. Con esto, TODA la fila de
   acciones mide lo mismo, tenga iconos o palabras. */
.btn>svg:only-child,.item-menu>svg:only-child{display:block}
.btn.sm>svg:only-child{width:14px;height:14px}
.dots-panel hr{border:none;border-top:1px solid var(--borde);margin:4px 2px}
/* Fila de documento: nombre (abre en pestaña nueva) + ⬇ que descarga directo.
   El hover resalta toda la fila, pero cada mitad conserva su propia acción. */
.dots-panel .dots-fila{display:flex;align-items:center;gap:2px;border-radius:7px}
.dots-panel .dots-fila:hover{background:var(--fondo)}
.dots-panel .dots-fila:hover>a{color:var(--vino)}
.dots-panel .dots-fila>a{flex:1;min-width:0}
.dots-panel .dots-fila>a:hover{background:transparent}
.dots-panel a.dots-descargar{flex:0 0 auto;width:auto;padding:8px 9px;font-size:13px;
  color:var(--tenue);text-align:center}
.dots-panel a.dots-descargar:hover{background:var(--vino);color:#fff}

/* ── Refinamiento visual de Facturas (2026-07-15) — solo esta vista, vía
   clases .facturas-*; no toca .panel/.btn/.searchbar del resto del sistema.
   Mismo minimalismo y paleta (vino/blanco/gris), más aire y detalle. ──── */
/* Full-bleed (2026-07-23): la vista de Facturas usa todo el ancho del área
   de contenido (entre el riel del sidebar y el borde de la ventana) en vez
   del max-width:1200px centrado que usa el resto del sistema — se necesita
   ese ancho completo para que la tabla quepa sin scroll horizontal con
   todas las columnas en una sola línea. Solo afecta esta vista vía
   .content.facturas-full (la pone `claseContenido` del Shell; era el
   `block content_class` de base.html, borrado el 2026-09-06). */
.content.facturas-full{max-width:none;padding:24px 20px}


/* Una sola línea por celda (punto 6 del ajuste 2026-07-23): ninguna fila
   crece en altura por texto largo, así entran más filas en pantalla. */


.td-lugar-entrega{max-width:160px}
/* ⚠️ Acá había dos declaraciones sueltas, sin selector (`43f046d1`), que
   apagaban la regla siguiente: `@keyframes fila-in`, que ya no la usaba nadie.
   Se fueron las tres el 2026-10-05; ver `test_css_sin_declaraciones_huerfanas`. */

/* Puntos de estado — infraestructura lista para futuros indicadores
   (pendiente / completada / con novedad); hoy no se pintan porque el modelo
   de datos aún no tiene esos estados, salvo el de descarte que ya usa su
   propio badge. */
.dot{width:7px;height:7px;border-radius:50%;display:inline-block;margin-right:5px}
.dot-red{background:var(--peligro)}
.dot-yellow{background:var(--grupo-aves)}
.dot-green{background:var(--grupo-ruscus)}

@media (prefers-reduced-motion: reduce){
}

/* ── "dónde estoy" en formularios largos: el título de la tarjeta que
   contiene el campo con foco se resalta en vino (helper genérico, no solo
   Facturas) ──────────────────────────────────────────────────────── */
.formcard h2{transition:color .15s ease}
.formcard:focus-within h2{color:var(--vino)}
@media (prefers-reduced-motion: reduce){
  .formcard h2{transition:none}
}

/* ── Formularios ──────────────────────────────────────────────── */
.formcard{background:var(--panel);border:1px solid var(--borde);border-radius:10px;
  padding:24px;margin-bottom:16px;
  transition:transform .3s var(--ease-spring), border-color .2s ease, box-shadow .3s var(--ease-spring)}
.formcard:hover{border-color:var(--vino)}
.formcard:not(.ica-grupo):hover{transform:translateY(-3px);box-shadow:0 8px 20px rgba(34,38,46,.10)}
.formcard h2,.formcard h3{font-size:16px;margin-bottom:16px}
.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}
/* ── `.form-compacto` (2026-10-05): un formulario largo con campos de 32 px y
   no de 38, y 8 px entre filas y no 14. Nació para Datos de la empresa (seis
   secciones) y la ficha del empleado, en el estilo compacto de Ajustes y
   Nómina. No cambia ningún campo: solo el aire. */
.form-compacto .grid{row-gap:8px;column-gap:12px}
.form-compacto .field{gap:3px}
.tema-bento .form-compacto input:not([type=checkbox]):not([type=hidden]):not([type=file]):not([type=radio]),
.tema-bento .form-compacto .desp-btn{height:32px}
.form-compacto .panel-cab{padding-top:2px;padding-bottom:8px}
.field{display:flex;flex-direction:column;gap:5px}
.field.full{grid-column:1/-1}
/* Un formulario de dos columnas de bloques (el de proveedor, 2026-10-05): cada
   columna lleva sus bloques con su título, y adentro los campos van de a dos.
   En pantallas angostas vuelve a una sola columna. */
.formcard-dos{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:0 32px;align-items:start}
.formcard-dos .grid{grid-template-columns:repeat(2,minmax(0,1fr))}
/* La variedad del formulario de compra se lleva el ancho que sobra
   (2026-10-05), como en la tabla de ítems de la factura. */
.pf-table th.col-variedad{width:100%}
@media (max-width:1100px){.formcard-dos{grid-template-columns:1fr}}
label{font-size:12px;font-weight:700;color:var(--tenue)}
input,select,textarea{padding:9px 11px;border:1px solid var(--borde);border-radius:8px;
  font-size:14px;font-family:inherit;background:#fff;width:100%}
textarea{resize:vertical}
/* ── Foco de los controles ─────────────────────────────────────────────────
   UN solo comportamiento para todo el programa: borde vino + anillo suave.
   Decisión del usuario (2026-08-20), y `DESIGN.md` se corrigió para
   declararlo — antes decía "sin anillo adicional" y el código tenía TRES
   comportamientos distintos conviviendo:
     · el global de acá: solo borde vino
     · `.facturas-search`: anillo de 3px de --vino-tinte
     · `.fl-field`: anillo de 2.5px de vino al 10 %
   Los tres sobre el mismo control, según en qué pantalla estuvieras. Eso es
   exactamente lo que el rediseño vino a matar: "el mismo buscador" en los 11
   módulos.
   ⚠️ El anillo del campo inválido (`input.campo-invalido:focus`) se declara
   más abajo en este archivo y por eso gana: es rojo, y debe ganar. */
input:focus,select:focus,textarea:focus{outline:none;border-color:var(--vino);
  box-shadow:0 0 0 2.5px rgba(139,26,43,.1)}
input:focus-visible,select:focus-visible,textarea:focus-visible{outline:2px solid var(--vino);outline-offset:2px}
.auto{color:var(--tenue);font-size:13px;font-weight:600;margin-top:10px}

/* ── Totales ──────────────────────────────────────────────────── */
/* ⚠️ Acá vivían `.tots` / `.tot` / `.tot.big`: la tira de totales del
   formulario de factura. Se fueron el 2026-09-14 con el rediseño «hoja», que
   los pasó a `.fh-tot*` (al pie del documento y no en un tile aparte). Era el
   único consumidor que les quedaba, en el frontend y en las plantillas. */

/* ── Items factura ────────────────────────────────────────────── */
table.items input,table.items select{padding:6px 8px;font-size:13px}
table.items td{padding:6px}
/* Valor calculado de solo lectura: misma caja que un input, fondo tenue
   (señal de no editable). */
table.items .ro-cell{display:inline-block;min-width:56px;box-sizing:border-box;
  padding:6px 8px;border:1px solid var(--borde);border-radius:8px;
  background:var(--fondo);font-size:13px;text-align:right}
/* Tallos descartados junto al neto de la línea (factura con descarte). */
table.items .c_desc{color:var(--vino);font-size:11px;font-weight:600}
.linkrow{display:flex;gap:8px;flex-wrap:wrap;margin-top:8px}

/* ── Toast / flash ────────────────────────────────────────────── */
/* `pointer-events:none` en el contenedor, `auto` en cada toast: `.flash-wrap`
   es fixed y ocupa una columna arriba a la derecha, así que su área vacía se
   quedaba con los clicks de lo que hubiera debajo. Los toasts sí tienen que
   recibirlos — el de "Deshacer" lleva un botón. */
.flash-wrap{position:fixed;top:18px;right:18px;z-index:var(--capa-aviso);
  display:flex;flex-direction:column;
  gap:8px;pointer-events:none}
.toast{padding:12px 18px;border-radius:9px;color:#fff;font-weight:600;font-size:14px;
  box-shadow:0 4px 14px rgba(0,0,0,.15);pointer-events:auto}
.toast.ok{background:var(--ok)}.toast.err{background:var(--peligro)}
/* El ámbar es para lo que hay que ir a arreglar, no para lo que salió mal:
   la acción se hizo pero dejó algo pendiente (una factura en 0 porque faltaba
   el precio). A diferencia de los otros dos, este NO se borra solo -- ver la
   regla de animación más abajo y `PERMANENTES` en `ui/Toasts.jsx` --, así que
   necesita su botón de cerrar. */
.toast.alerta{background:var(--alerta);display:flex;align-items:flex-start;
  gap:14px;max-width:min(520px,calc(100vw - 36px));line-height:1.45}
.toast-x{background:none;border:0;color:#fff;font-size:15px;line-height:1;
  padding:2px 4px;cursor:pointer;opacity:.85;flex:0 0 auto}
.toast-x:hover{opacity:1}
.toast-x:focus-visible{outline:2px solid #fff;outline-offset:2px;border-radius:4px}

/* ── Franja de alarma del admin — SIN USO desde el 2026-08-18 ──────────────
   Pintaba las 3 alarmas de fondo (backup, cumpleaños, foto del despacho) arriba
   de cada pantalla. El usuario pidió quitarla y verlas en Administración →
   Monitoreo (`.proc-card` más abajo). Se deja el bloque, no el include: esta
   alarma ya cambió de decisión dos veces (08-11 no, 08-12 sí, 08-18 no), así que
   volver a pintarla es agregar el include de nuevo y nada más.
   El nombre viejo era solo `.alerta-backup`; el alias quedó del rename del
   2026-08-12. */
.alerta-backup,.alerta-admin{display:flex;gap:10px;align-items:flex-start;margin:0 0 16px;
  padding:12px 16px;border-radius:10px;background:#FDECEA;
  border:1px solid var(--peligro);color:#7A2318;font-size:13.5px;line-height:1.45}
.alerta-backup svg,.alerta-admin svg{flex:0 0 auto;margin-top:1px;color:var(--peligro)}
.alerta-backup strong,.alerta-admin strong{display:block;font-weight:700;font-size:14px}
.alerta-backup span,.alerta-admin span{color:#8A3A2E}
/* Despachos va a pantalla completa: ahí la franja cuelga del <body> y no
   hereda el padding de ningún contenedor. */
body > .alerta-backup,body > .alerta-admin{margin:12px 16px 0}
/* ── Procesos automáticos (Monitoreo) — reemplazan la franja de arriba ──── */
.proc-titulo{font-size:15px;font-weight:700;margin:0 0 10px}
.proc-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));
  gap:12px;margin:0 0 18px}
/* ⚠️ Decía `var(--linea)`, que no existe en ningún lado: la declaración entera
   era inválida y estas tarjetas salían SIN borde. Encontrado el 2026-09-16 al
   escribir el guardián de tokens huérfanos. El token del sistema es `--borde`. */
.proc-card{border:1px solid var(--borde);border-radius:10px;padding:12px 14px;
  background:var(--panel)}
.proc-card.malo{border-color:var(--peligro);background:#FDECEA}
/* ⚠️ Acá el badge de estado recibía `background:#fff`, porque su rojo clarísimo
   (#FCE9EC) sobre la tarjeta en rojo quedaba invisible. **Desde el 2026-09-30
   `.badge-status` no tiene fondo**, así que la regla ya no describe nada; se
   deja apagada y no borrada por el mismo motivo que todo este bloque: la
   franja roja de admin cambió de decisión tres veces y `.proc-card` quedó sin
   uso cuando Monitoreo pasó a React, pero reponerla es reponer un include. */
/* .proc-card.malo .badge-status.danger{background:#fff} */
.proc-head{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-bottom:6px}
.proc-head strong{font-size:13.5px}
.proc-cadencia{color:var(--tenue);font-size:12px}
.proc-detalle{margin-top:8px;font-size:12.5px;line-height:1.45;color:#7A2318}
.proc-detalle strong{display:block;font-weight:700;margin-bottom:2px}

.pill{font-size:11px;padding:2px 9px;border-radius:20px;background:var(--vino-tinte);
  color:var(--vino);font-weight:700}
a.plain{color:var(--vino);text-decoration:none;font-weight:600}
a.plain:hover{text-decoration:underline}

/* ── Badges compartidos: grupo botánico ICA y estado ────────────────────
   Reemplazan los pills de color inline que estaban duplicados en
   Compras/ICA/Aforos/Correos.

   🔑 **Son DOS cosas distintas desde el 2026-09-30, y antes compartian regla
   por como se escribio, no por decision:**

   · `.badge-grupo` es un **codigo ante una autoridad**. Sigue siendo una caja:
     una norma se lee como etiqueta, no como texto suelto. Decision del usuario.
   · `.badge-status` es un **estado del sistema**, y pierde la caja: queda el
     mismo texto y el mismo color, sin el fondo.

   📊 **Por que se puede sin tocar un solo color**: el fondo de la fila es mas
   claro que el tinte de dia y mas oscuro de noche, asi que el contraste SUBE.
   Medido el 2026-09-30 con los valores reales de los tokens, el peor de los
   seis: de dia **4,66:1 → 5,17:1**; de noche **4,50:1 → 8,53:1**.

   ⚠️ El motivo no es estetico: en Archivo plano OPD hay **25 cajitas de color
   en una pantalla de 25 filas**, y con ese ruido la que importa no resalta. */
.badge-grupo{font-size:12px;font-weight:700;padding:2px 10px;
  border-radius:20px;white-space:nowrap;display:inline-flex;align-items:center;gap:4px}
/* Sin `padding` ni `background`: es texto. El `nowrap` se queda porque un
   estado partido en dos lineas estira la fila, que es lo contrario de lo que
   este cambio busca. */
.badge-status{font-size:12px;font-weight:700;white-space:nowrap;
  display:inline-flex;align-items:center;gap:4px}
.badge-grupo.aves{background:#FFF4E5;color:var(--grupo-aves)}
.badge-grupo.ruscus{background:#E7F6EC;color:var(--grupo-ruscus)}
.badge-grupo.hydrangea{background:#EAF3FE;color:var(--grupo-hydrangea)}
.badge-grupo.otro{background:#EEF0F3;color:#5A6270}
.badge-status.warn{color:var(--grupo-aves)}
.badge-status.ok{color:var(--grupo-ruscus)}
.badge-status.muted{color:#5A6270}
/* El lugar de entrega, mas llamativo que el gris de antes: sirve para ver de
   un vistazo que facturas ya lo tienen asignado y cuales no (pedido del
   usuario, 2026-09-13).
   ⚠️ Amarillo PROPIO y no el ocre de `.warn`, que es el de Aves del Paraiso:
   en la tabla de facturas conviven las dos cosas, y pintar la entrega del mismo
   color que un grupo botanico confunde mas de lo que ayuda.
   ⚠️ **#6B4E00 y no un amarillo brillante**: sin la caja detras, un amarillo
   claro sobre blanco no se lee. Este da 7,74:1 sobre la fila. */
.badge-status.entrega{color:#6B4E00}
.badge-status.danger{color:var(--peligro-texto,var(--peligro))}
.badge-status.info{color:#2563EB}

/* ── La banda al borde de la fila (`.fila-ojo`) ───────────────────────
   La otra mitad del cambio del 2026-09-30: al sacarle la caja al estado, lo
   que pide atencion necesita una senal que se vea **de lejos**.

   🔑 Marca la FILA y no la celda, que es lo que de verdad se busca: la factura
   sin direccion completa, no el texto que lo dice. Se recorre el borde
   izquierdo sin leer la ultima columna.

   ⚠️ `inset` y no `border-left`: un borde corre las celdas 2 px y desalinea
   la tabla entera con la de al lado. Esto no ocupa espacio.
   ⚠️ Va en la PRIMERA celda, no en el `<tr>`: en una tabla con
   `border-collapse` el `box-shadow` de una fila no se pinta. */
/* ⚠️ **Un `inset` NO sirve acá, y se vio en la captura**: la primera celda
   lleva `border-radius:12px 0 0 12px` (el tema Bento redondea la fila), así que
   la sombra sigue la curva y con una fila densa de 26 px el radio se come casi
   todo el alto — la banda se leía como un paréntesis alrededor de la casilla,
   no como una marca. Un pseudo-elemento absoluto se dibuja recto y tampoco
   ocupa espacio, que es lo que descartaba el `border-left`. */
.fila-ojo>td:first-child,
.fila-lista>td:first-child{position:relative}
.fila-ojo>td:first-child::before,
.fila-lista>td:first-child::before{content:"";position:absolute;
  left:0;top:0;bottom:0;width:2px;border-radius:2px}
.fila-ojo>td:first-child::before{background:var(--peligro-texto,var(--peligro))}
.fila-lista>td:first-child::before{background:var(--ok)}

/* ── Pantalla de carga ────────────────────────────────────────── */
.loader{position:fixed;inset:0;z-index:200;display:none;align-items:center;
  justify-content:center;background:rgba(245,244,242,.85);
  backdrop-filter:blur(3px);-webkit-backdrop-filter:blur(3px)}
.loader.show{display:flex;animation:lfade .15s ease}
.loader-box{display:flex;flex-direction:column;align-items:center;gap:14px}
.spin{width:48px;height:48px;border:4px solid #E6E1E3;border-top-color:var(--vino);
  border-radius:50%;animation:spin .8s linear infinite}
.spin.sm{width:28px;height:28px;border-width:3px}
.loader-txt{color:var(--vino);font-weight:700;letter-spacing:1px;font-size:13px;
  animation:pulse 1.2s ease-in-out infinite}
@keyframes spin{to{transform:rotate(360deg)}}
@keyframes pulse{0%,100%{opacity:.5}50%{opacity:1}}
@keyframes lfade{from{opacity:0}to{opacity:1}}

/* ── Animaciones de interfaz ──────────────────────────────────── */
.content{animation:rise .28s ease}
@keyframes rise{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.card{transition:transform .3s var(--ease-spring), border-color .2s ease, box-shadow .3s var(--ease-spring)}
.card:hover{transform:translateY(-3px);border-color:var(--vino);box-shadow:0 8px 20px rgba(34,38,46,.10)}
.card.hero,.kpi.hero{box-shadow:0 6px 18px rgba(34,38,46,.09);border-color:#EDD9DC}
.panel tbody tr{transition:background .12s ease}
/* Tarjetas de grupo botánico en Movimiento ICA (una por grupo, en factura.html):
   mismo lenguaje de hover que .card, aplicado solo al borde (sin transform,
   porque son tarjetas grandes con tablas de asignación — un lift constante
   mientras el usuario llena inputs sería ruido, no ayuda). */
.formcard.ica-grupo{transition:border-color .15s ease}
.formcard.ica-grupo:hover{border-color:var(--vino)}
.toast{animation:slidein .25s ease, fade2 4s forwards}
/* Sin esto `fade2` lo desvanece igual y queda un cartel invisible ocupando
   lugar, que es peor que no mostrarlo. */
.toast.alerta{animation:slidein .25s ease}
@keyframes slidein{from{opacity:0;transform:translateX(20px)}to{opacity:1;transform:none}}
/* Termina en `visibility:hidden`, no solo en `opacity:0`. Un elemento
   transparente sigue capturando clicks: el toast de un flash se quedaba
   invisible arriba a la derecha y **dejaba inalcanzable** lo que hubiera
   debajo hasta recargar la página. Encontrado abriendo Despachos con un
   navegador el 2026-08-13: tapaba el botón «Etiquetas Riobamba». */
@keyframes fade2{0%,85%{opacity:1;visibility:visible}
  99%{opacity:0;visibility:visible}100%{opacity:0;visibility:hidden}}
/* El de "Deshacer" NO se desvanece: se quita solo del DOM al resolverse
   (`confirm-modal.js`). Con la animación general se volvía invisible a los 4 s
   mientras su cuenta regresiva es de 5 s — o sea que el último segundo el botón
   estaba ahí, activo, y no se veía, con la barra de progreso todavía corriendo. */
.confirm-toast{animation:slidein .25s ease}

@media (prefers-reduced-motion: reduce){
  .content{animation:none}
  .card,.btn,.panel tbody tr,.sidebar,.sidebar nav a,.export,.side-user,
  .sidebar nav a span,.side-user-info,.export span,.modcard,.formcard,.formcard.ica-grupo,
  .kpi,.chartcard{transition:none!important}
  .card:hover,.kpi:hover,.chartcard:hover,.formcard:not(.ica-grupo):hover{transform:none!important}
  .btn:active{transform:none}
  /* Sin `slidein`, pero `fade2` se queda: no es decoración, es lo que saca al
     toast del paso (ver su comentario arriba). */
  .toast{animation:fade2 4s forwards}
  .confirm-toast{animation:none}
  .spin{animation-duration:1.6s}
  .loader-txt{animation:none}
}

/* ── Barra de búsqueda ────────────────────────────────────────── */
.searchbar{display:flex;gap:8px;align-items:center;margin-bottom:18px}
.searchbar input[type=search]{flex:1;max-width:380px;padding:9px 12px;
  border:1px solid var(--borde);border-radius:9px;font-size:14px}

/* ── Informes ─────────────────────────────────────────────────── */
.kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:14px;margin-bottom:20px}
.kpi{background:var(--panel);border:1px solid var(--borde);border-radius:10px;padding:18px 20px;
  transition:transform .3s var(--ease-spring), border-color .2s ease, box-shadow .3s var(--ease-spring)}
.kpi:hover{transform:translateY(-3px);border-color:var(--vino);box-shadow:0 8px 20px rgba(34,38,46,.10)}
/* ⚠️ Misma tipografía que `.kseg`, del panel segmentado: son el mismo dato y
   tienen que leerse igual. El valor va en `--texto` y no en el acento —el
   acento queda para UNA cosa por pantalla— salvo cuando el semáforo pide rojo,
   que lo pone el componente (2026-09-16). */
.kpi{display:flex;flex-direction:column}
.kpi .l{color:var(--tenue);font-size:12px;font-weight:700;letter-spacing:.3px;
  text-transform:uppercase}
/* ⚠️ `margin-top:auto`: los números se apoyan en el PISO de su tarjeta, no
   debajo de su rótulo. Sin esto, el rótulo que ocupa dos renglones —«Días de
   vacaciones pendientes (todos)»— empujaba su número 18 px más abajo que los
   otros cuatro de la misma fila (2026-09-16). */
.kpi .v{font-size:26px;font-weight:800;color:var(--texto);margin-top:auto;
  padding-top:2px;line-height:1.15;font-variant-numeric:tabular-nums}
.charts{display:grid;grid-template-columns:repeat(auto-fit,minmax(420px,1fr));gap:18px}
.chartcard{background:var(--panel);border:1px solid var(--borde);border-radius:10px;padding:18px 20px;
  transition:transform .3s var(--ease-spring), border-color .2s ease, box-shadow .3s var(--ease-spring)}
.chartcard:hover{transform:translateY(-3px);border-color:var(--vino);box-shadow:0 8px 20px rgba(34,38,46,.10)}
.chartcard h3{margin:0 0 4px;color:var(--vino);font-size:15px}
.chartcard .chart-sub{color:var(--tenue);font-size:12px;margin:0 0 14px}
.chartcard.wide{grid-column:1 / -1}
.kpi.muted .v{color:var(--tenue);font-size:20px}
/* `<Kpis compactos>` (inicio de Nómina, 2026-10-05): los enlaces de «por
   atender» en una fila de 54 px, el número a la izquierda y el rótulo al lado.
   Eran tarjetas de 96 px con el número abajo. */
.kpis.kpis-compactos{grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:10px;margin-bottom:16px}
.kpis-compactos .kpi{flex-direction:row-reverse;justify-content:flex-end;align-items:center;
  gap:12px;padding:10px 16px;min-height:54px}
.kpis-compactos .kpi .v{font-size:22px;margin-top:0;line-height:1}
.kpis-compactos .kpi .l{text-transform:none;letter-spacing:0;font-size:13px;color:var(--texto);
  font-weight:600}

.filterbar{display:flex;flex-wrap:wrap;align-items:center;gap:10px;background:var(--panel);
  border:1px solid var(--borde);border-radius:10px;padding:12px 16px;margin-bottom:18px}
.filterbar label{font-size:11px;color:var(--tenue);text-transform:uppercase;
  letter-spacing:0.04em;display:block;margin-bottom:2px}
.filterbar .fgroup{display:flex;flex-direction:column}
.filterbar select{padding:7px 10px;border:1px solid var(--borde);border-radius:8px;
  font-size:13px;background:#fff;color:var(--texto);min-width:150px}
.filterbar .clear-filters{font-size:12.5px;color:var(--vino);text-decoration:none;
  margin-left:auto;align-self:flex-end;padding:7px 0}
.filterbar .clear-filters:hover{text-decoration:underline}

.grupo-dot{display:inline-block;width:9px;height:9px;border-radius:50%;margin-right:6px;
  vertical-align:middle}
.legend-grupos{display:flex;flex-wrap:wrap;gap:10px 16px;margin:0 0 14px;font-size:12.5px;color:var(--texto)}

/* ── Pestañas (Documentos) ────────────────────────────────────── */
.doctabs{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:16px}
.doctab{display:inline-flex;align-items:center;gap:8px;text-decoration:none;
  padding:9px 16px;border-radius:10px;background:var(--panel);color:var(--texto);
  border:1px solid #E4E7EC;font-weight:600;font-size:14px;transition:all .15s ease}
.doctab:hover{border-color:var(--vino);color:var(--vino)}
.doctab.on{background:var(--vino);color:#fff;border-color:var(--vino)}
.doctab .badge{font-size:11px;font-weight:800;background:rgba(0,0,0,.12);padding:1px 8px;border-radius:20px}
.doctab.on .badge{background:rgba(255,255,255,.25)}

/* ── Pistas de ayuda ──────────────────────────────────────────── */
.hint{display:block;color:var(--tenue);font-size:11px;margin-top:3px;font-weight:400}
/* ⚠️ `:user-invalid` y no `:invalid`: el navegador considera inválido un campo
   obligatorio VACÍO, así que un formulario recién abierto salía con todos sus
   campos en rojo antes de que nadie escribiera nada. El
   `:not(:placeholder-shown)` de antes lo tapaba solo donde había placeholder;
   en el modal de catálogos, que no tiene, se veían los dos campos marcados al
   abrir (visto el 2026-09-16). `:user-invalid` marca recién cuando el operador
   tocó el campo o intentó enviar, que es cuando el aviso significa algo. */
.field input:user-invalid,
.field select:user-invalid{border-color:#E0A0A8}

/* ── Responsivo ───────────────────────────────────────────────── */
@media (max-width: 900px){
  body{padding-left:0}
  /* ⚠️ **En un celular el menú es UNA fila, no una grilla de iconos.**
     Con `flex-wrap:wrap` los catorce módulos se apilaban en tres filas y la
     navegación se comía 190 px de los 900 de alto de la pantalla antes de que
     empezara el contenido (medido el 2026-09-16 en Informes). Ahora la barra
     mide ~56 px y los módulos que no entran se alcanzan deslizando de lado,
     que es lo mismo que hacen las pestañas de los días en Despachos.
     La barra de scroll va oculta: si ocupara alto, volveríamos a lo de antes. */
  .sidebar{width:100%;height:auto;position:static;padding:8px 12px;
    border-right:none;border-bottom:0.5px solid var(--borde);overflow-y:visible;
    display:flex;flex-direction:row;align-items:center;gap:10px}
  .sidebar nav{flex-direction:row;flex-wrap:nowrap;gap:5px;padding:0;
    flex:1;min-width:0;overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}
  .sidebar nav::-webkit-scrollbar{display:none}
  .sidebar nav a,.sidebar .nav-accion{flex:none}
  .nav-sep{display:none}
  /* Los rótulos de sección piden el ancho entero, que es justo lo que no hay
     en una fila que se desliza. */
  .nav-label{display:none}
  .sidebar nav a{padding:8px 11px;font-size:13px;border-left:none;border-radius:8px;
    border-bottom:2px solid transparent}
  .sidebar nav a.on{border-left:none;border-bottom-color:var(--vino);background:var(--vino-tinte)}
  .brand{padding:0;border-bottom:none;flex:none}
  .side-footer{border-top:none;padding:0;display:flex;align-items:center;
    gap:8px;flex:none}
  .side-user{padding:0}
  .export{padding:6px}
  /* El nombre del usuario y «Cerrar sesión» no caben en la fila de un celular;
     el avatar y el candado alcanzan para saber quién sos y para salir. */
  .side-user-info{display:none}
  .row-head .acts{justify-content:flex-start}
  .tarjetas{grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px}
  .barra-mod-in{padding:0 16px;gap:14px}
  .content{padding:18px 16px;max-width:100%;margin:0}
  .row-head{flex-direction:column;gap:12px;align-items:stretch}
  .grid{grid-template-columns:1fr 1fr}
  .charts{grid-template-columns:1fr}
  .panel{overflow-x:auto}
  .panel table{min-width:560px}
  h1{font-size:24px}
  /* Objetivo táctil: en escritorio .btn.sm prioriza densidad de tabla; en
     tablet/móvil (contexto táctil real) sube a ~44px de alto sin cambiar su
     tamaño visual en desktop. */
  .btn.sm{min-height:44px;display:inline-flex;align-items:center;justify-content:center;
    padding-top:0;padding-bottom:0}
  table.items input,table.items select{min-height:44px}
}
@media (max-width: 560px){
  .grid{grid-template-columns:1fr}
  .kpis{grid-template-columns:1fr 1fr}
  .tots{gap:14px}
  .searchbar{flex-wrap:wrap}
  .searchbar input[type=search]{max-width:100%;width:100%}
  .btn{padding:9px 14px}
}

/* ── Pantalla de inicio (2026-07-14: foto de hydrangeas a pantalla
   completa como fondo fijo, con la barra superior y el saludo flotando
   encima en tarjetas blancas — sustituye el fondo claro con gradiente
   sutil de la versión anterior). ─────────────────────────────────── */
/* ⚠️ **El fondo lo pone React en línea, no esta regla** (2026-09-03): su URL
   la resuelve `url_for` en el servidor y el CSS no puede saber la ruta del
   estático. Acá quedan solo el encuadre y el respaldo.

   Hasta esa fecha la foto se bajaba de `images.unsplash.com` en cada carga —la
   décima imagen externa de esta pantalla, y la más pesada—. Ahora vive en
   `static/img/inicio/fondo.jpg`, 155 kB, por la misma razón por la que AG Grid
   salió del CDN el 2026-09-02: la primera pantalla del sistema no puede
   depender de que un tercero responda. */
/* El fondo va en un pseudo-elemento FIJO y no en el propio `.home-body`: con
   `background-attachment:fixed` la imagen solo cubre lo que mide la ventana, y
   en cuanto la página era más alta el resto quedaba del gris de `--fondo`. Se
   veía como un corte horizontal a media pantalla (medido el 2026-09-16, cuando
   la portada medía 1,24 pantallas). Así cubre siempre, haya scroll o no. */
/* Columna con el contenido centrado en lo que sobra: sin esto, las tarjetas
   quedaban pegadas arriba y todo el aire se juntaba abajo en una franja muerta
   (medido el 2026-09-16: 360 px de fondo vacío bajo la última fila). Centrado,
   el mismo aire se reparte arriba y abajo. `margin:auto` y no
   `justify-content`: si algún día los módulos no entran, el bloque crece y se
   puede llegar a todos con scroll en vez de quedar recortado. */
.home-body{display:flex;flex-direction:column;position:relative;min-height:100vh;
  background-color:var(--fondo)}
/* ⚠️ El `width:100%` de los dos hace falta por el `display:flex` de arriba: un
   hijo de flex con `margin:0 auto` toma el ancho de su CONTENIDO en vez de
   estirarse, así que la barra superior se encogía a 744 px y dejaba de estar
   alineada con las tarjetas. Con `display:block` el `max-width` alcanzaba.
   Medido en pantalla el 2026-09-16, al centrar la portada. */
.home-body > .home-top{width:100%}
.home-body > .home-wrap{margin-top:auto;margin-bottom:auto;width:100%}
.home-body::before{content:"";position:fixed;inset:0;z-index:-1;
  background-image:inherit;background-position:center;background-size:cover;
  background-repeat:no-repeat}
/* `flex-wrap` y `gap`: a 400 px el nombre de la empresa, el usuario y los dos
   botones no entran en una línea y la barra empujaba la página 62 px fuera de
   la pantalla (medido el 2026-09-16). Ahora baja de renglón en vez de
   desbordarse. */
.home-top{display:flex;justify-content:space-between;align-items:center;
  flex-wrap:wrap;gap:10px 16px;
  padding:16px 36px;max-width:1100px;margin:0 auto;
  background:rgba(255,255,255,.9);backdrop-filter:blur(6px);
  border-radius:0 0 12px 12px}
.home-brand{display:flex;align-items:center;gap:12px}
.home-brand .brand-f{font-size:15px;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;color:var(--vino)}
.home-logo{height:46px;width:auto;display:block}
/* El nombre de la empresa cuando todavía no subió su logotipo (2026-09-21).
   Ocupa el mismo alto que la imagen para que la fila de arriba no salte, y va
   en blanco porque atrás hay una foto: es la misma superficie donde vive el
   logo. */
.home-logo--texto{display:flex;align-items:center;height:46px;color:#fff;
  font-weight:700;font-size:18px;letter-spacing:.2px;white-space:nowrap}
.home-user{display:flex;align-items:center;gap:14px;color:var(--tenue);font-size:13px}
.home-user span{display:inline-flex;align-items:center;gap:6px}
.home-user .navicon{width:15px;height:15px;flex-shrink:0}
.home-wrap{max-width:1100px;margin:0 auto;padding:8px 36px 28px}
/* 📌 El saludo «Hola, <nombre>» con la fecha vivía acá y se fue el
   2026-09-17: ocupaba 52 px de la primera pantalla para decirle a alguien
   su propio nombre y qué día es. Su CSS (`.home-hello`, `.hello-card`) se
   fue con él, igual que `.modgo` — el «Entrar →» que la tarjeta de foto
   ya no muestra, porque la tarjeta ENTERA es el enlace. */

/* ── Grid de módulos: estructura original (una sola cuadrícula, todas las
   tarjetas al mismo nivel) — el usuario pidió explícitamente conservarla.
   Ahora reutiliza el patrón `.card` plano del resto del sistema en vez de
   foto + duotono. ────────────────────────────────────────────────── */
/* `auto-fit` en vez de tres columnas fijas: con ocho módulos, tres columnas
   dejaban la última fila con dos tarjetas y un hueco, que era lo primero que se
   veía al entrar. Así la rejilla arma las columnas que entren —cuatro en
   escritorio, ocho módulos en 4+4— y se acomoda sola en pantallas chicas sin
   necesitar una media query aparte. */
.home-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));
  gap:16px}
.modcard{background:var(--panel);color:var(--texto);border:1px solid var(--borde);
  border-radius:12px;overflow:hidden;text-decoration:none;
  display:flex;flex-direction:column;gap:0;box-shadow:0 4px 16px rgba(0,0,0,.12);
  transition:border-color .15s ease, transform .15s ease, box-shadow .15s ease}
.modcard:hover{border-color:var(--vino);transform:translateY(-2px);box-shadow:0 8px 20px rgba(0,0,0,.08)}
.modcard:active{transform:translateY(0) scale(.99)}
.modcard:focus-visible{outline:2px solid var(--vino);outline-offset:2px}

/* ══════════════════════════════════════════════════════════════════════════
   LA PORTADA DE FOTO — elegida por el usuario el 2026-09-17, por la tarde
   ══════════════════════════════════════════════════════════════════════════
   La foto LLENA la tarjeta y el texto va encima, sobre un degradado. Reemplaza
   a la banda lateral de 96 px, que se leía como una franja de color y no como
   una imagen. Con ella se fueron tres cosas que el usuario pidió sacar: el
   nombre de la empresa de la barra, el saludo con la fecha, y los 197 px de
   hueco que había debajo de la barra. La rejilla sube 247 px.

   🔴 **El riesgo de esta variante está MEDIDO, y ya mordió una vez.** En la
   primera versión de esta idea (2026-09-16) dos títulos quedaron en 2,71:1 de
   contraste contra los 4,5 que pide AA, porque una regla `color:#fff` empataba
   en especificidad con `.modcard .modtitle` y perdía. Hoy el título se pinta
   con `.home-grid--foto .modcard .modtitle` —tres clases, gana— y las ocho
   fotos se midieron una por una contra este velo: la peor es Logística con
   4,97:1 y la mejor Ajustes con 10,15:1.
   ⚠️ **Cambiar una foto obliga a volver a medir.** Lo recuerda
   `tests/test_portada_contraste.py`. */

/* La barra y el fondo ---------------------------------------------------- */
.home-body--foto{background:var(--fondo)}
.home-body--foto .home-top{background:var(--panel);backdrop-filter:none;
  border:1px solid var(--borde);border-top:0;
  box-shadow:0 1px 3px rgba(0,0,0,.05)}
/* La rejilla arranca donde termina la barra: sin saludo y sin hueco. */
.home-body--foto .home-wrap{padding-top:14px}
.home-body--foto .home-wrap,
.home-body--foto .home-top{max-width:1240px}
/* `.home-body` centra su contenido vertical con `margin:auto`, que con la
   rejilla arriba deja todo flotando en el medio otra vez. Acá se ancla arriba,
   que es justo lo que se pidió. */
.home-body--foto > .home-wrap{margin-top:0;margin-bottom:auto}

/* La tarjeta ------------------------------------------------------------- */
/* `aspect-ratio` y no `min-height`: la tarjeta tiene que mantener la
   proporción de la foto en cualquier ancho de columna, o la imagen se recorta
   distinto en cada pantalla y el texto se le monta encima en las angostas. */
.home-grid--foto{grid-template-columns:repeat(auto-fit,minmax(252px,1fr))}
.home-grid--foto .modcard{
  position:relative;aspect-ratio:280/200;flex-direction:column;
  justify-content:flex-end;isolation:isolate;border:0;
  box-shadow:0 2px 10px rgba(0,0,0,.10)}
.home-grid--foto .modfoto{
  position:absolute;inset:0;background-size:cover;background-position:center;
  transition:transform .5s cubic-bezier(.2,.7,.3,1)}
.home-grid--foto .modcard:hover .modfoto{transform:scale(1.045)}
/* El velo es un ELEMENTO propio y no un `box-shadow` interior ni un
   `::before`: tiene que quedar entre la foto y el texto, y crecer con la
   tarjeta.

   🔴 **El 0,62 de abajo es un número calculado, no elegido a ojo.** Es el alfa
   con el que el título blanco llega a 4,5:1 (AA) sobre **el peor fondo
   posible**, que es una foto blanca pura: da 6,19:1, o sea margen. El mínimo
   exacto está en 0,55 (4,74:1) y por debajo de 0,52 ninguna foto clara se
   salva.
   ⚠️ **Estaba en 0,90 y bajó el 2026-09-17**, el mismo día que nació: con ese
   valor la foto quedaba casi negra justo en el tercio donde se la mira, y el
   test que lo vigilaba **no podía fallar nunca** —probé con un blanco puro y
   pasó igual—, o sea que era cobertura falsa. Aclararlo por debajo de 0,55
   hace fallar `test_portada_contraste.py`, que ahora mide esta propiedad y no
   las ocho fotos: **si el velo aguanta el blanco puro, aguanta cualquier
   foto**, y nadie tiene que acordarse de medir al cambiar una. */
.home-grid--foto .modvelo{
  position:absolute;inset:0;
  background:linear-gradient(to top,
    rgba(8,10,14,.62) 0%, rgba(8,10,14,.40) 38%,
    rgba(8,10,14,.12) 72%, rgba(8,10,14,0) 100%)}
.home-grid--foto .modbody{
  position:relative;padding:14px 16px 15px;gap:3px;flex:0 0 auto}
/* ⚠️ **Tres clases a propósito.** Con `.home-grid--foto .modtitle` (dos) el
   empate con `.modcard .modtitle` lo ganaba la regla de más abajo en el
   archivo, y el título salía casi negro sobre la foto: 2,71:1. Eso no se ve a
   ojo —la sombra sí se aplicaba, así que parecía un problema de la foto— y hay
   que mirarlo con `getComputedStyle`. */
.home-grid--foto .modcard .modtitle{color:#fff;font-size:16px;
  text-shadow:0 1px 3px rgba(0,0,0,.45)}
.home-grid--foto .modcard .moddesc{color:rgba(255,255,255,.88);margin-top:0;
  font-size:12.5px;flex:0 0 auto;text-shadow:0 1px 3px rgba(0,0,0,.4)}
.home-grid--foto .modcard:hover{transform:translateY(-3px);
  box-shadow:0 10px 26px rgba(0,0,0,.18)}
.home-grid--foto .modcard:focus-visible{outline:3px solid var(--vino);
  outline-offset:3px}
/* 📌 **Las dos marcas comparten la barra de arriba desde el 2026-09-17**, del
   mismo alto, por decisión del usuario. Antes Santikode firmaba al pie de la
   rejilla y pequeño, para que no compitiera con el cliente; hoy comparten lugar
   y tamaño, y el `.marca-santikode` del pie se fue entero con su CSS.
   ⚠️ El separador es una LÍNEA vertical y no un «·»: un punto entre dos logos se
   lee como si una marca perteneciera a la otra, que es justo lo contrario. */
.marca-sep{width:1px;height:26px;background:var(--borde);margin:0 2px;
  flex:0 0 auto}
/* ⚠️ Se fija el ALTO y nunca el ancho: el archivo es de 211x138 px —tres veces
   los 46 que se ven— para que no se vea borroso en una pantalla de alta
   densidad, y cambiar el logo por uno de otra proporción no obliga a tocar
   esto. `.home-logo` ya vale 46 px, así que los dos quedan parejos solos: esta
   regla existe para que se note que la igualdad de alto es intencional y no
   una coincidencia que el próximo cambio rompa sin avisar. */
.home-logo--santikode{height:46px;width:auto}

/* ── El botón de la luna (2026-09-17) ───────────────────────────────────────
   Cambia el modo claro/oscuro. Vive en la barra de la portada, entre el nombre
   de la persona y «Administración».

   📌 **Es un `<form>` con POST, no un `fetch`**: el servidor decide el tema y
   vuelve a pintar la pantalla, así que funciona con JavaScript apagado y no hay
   nada que sincronizar en el cliente. El `form` no debe alterar el `flex` de la
   barra, de ahí el `display:contents`. */
.tema-form{display:contents}
.btn-tema{
  width:34px;height:34px;border-radius:50%;flex:0 0 auto;
  display:grid;place-items:center;padding:0;line-height:1;
  font-size:15px;cursor:pointer;
  background:transparent;border:1px solid var(--borde);color:var(--texto);
  transition:background .15s ease, border-color .15s ease}
.btn-tema:hover{background:var(--fondo);border-color:var(--vino)}
.btn-tema:focus-visible{outline:2px solid var(--vino);outline-offset:2px}
/* El nombre de quien entró se recorta antes de empujar los botones fuera de la
   barra: en una pantalla angosta, «Santiago Pérez Palacio · Admin» mide más
   que el espacio que queda. */
.home-quien{max-width:32ch;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}

.modbody{padding:18px 20px 20px;display:flex;flex-direction:column;flex:1;gap:6px}

/* Entrada escalonada al cargar /inicio — la única pantalla del sistema
   donde se justifica un momento de "llegada" (ver DESIGN.md, registro
   product: nada de coreografías por página, pero un stagger acotado a
   una sola lista sí es legítimo). */
@keyframes home-card-rise{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:none}}
/* ⚠️ El retraso sale de `--retraso`, que la portada pone por tarjeta con su
   índice (2026-09-17). Antes eran cinco reglas `:nth-child` escritas a mano y
   **las tarjetas 6, 7 y 8 aparecían todas juntas**, sin desfase: la lista tenía
   ocho módulos y las reglas se habían quedado en cinco. Hoy son nueve (Gastos
   entró el 09-18) y el índice las cubrió sola, que es de lo que se trataba. Un
   `nth-child` por tarjeta es una lista que hay que acordarse de alargar; el
   índice no. */
.home-grid .modcard{animation:home-card-rise .55s cubic-bezier(.16,1,.3,1) both;
  animation-delay:var(--retraso,0ms)}

/* Tarjetas de módulo con icono en vez de foto (ej. panel de Administración,
   donde una foto de stock no encaja con herramientas técnicas). */
.modicon{display:flex;align-items:center;justify-content:center;width:44px;
  height:44px;border-radius:10px;background:var(--vino-tinte);margin-bottom:14px}
.modicon .navicon{width:22px;height:22px;color:var(--vino)}

.modcard .modtitle{font-size:17px;font-weight:800;color:var(--texto);letter-spacing:-0.01em}
.modcard .moddesc{color:var(--tenue);font-size:13px;flex:1;line-height:1.5;margin-top:6px}

@media (prefers-reduced-motion: reduce){
  .home-grid .modcard{animation:none}
}


@media (max-width: 820px){
  .home-grid{grid-template-columns:1fr}
  .home-top,.home-wrap{padding-left:18px;padding-right:18px}
}

/* Campo de catálogo con valor fuera de la lista (combo estricto) */
input.campo-invalido{border-color:#B3261E;background:#FEF7F6}
input.campo-invalido:focus{box-shadow:0 0 0 2.5px rgba(179,38,30,.12)}

/* ══════════════════════════════════════════════════════════════════════════
   LAS TRES CAPAS DE ARRIBA — el orden importa y por eso está escrito acá
   ══════════════════════════════════════════════════════════════════════════
   Un diálogo tapa la pantalla; un aviso tapa al diálogo; una confirmación tapa
   a todos. Antes no era así y costaba caro:

   · `.mdl-overlay` (el `<Modal>` de React) está en 1000 y `.confirm-modal`
     estaba en **61**. O sea que un `data-confirm` DENTRO de un modal dibujaba
     su cartel DEBAJO: el botón existía y era inalcanzable, el POST nunca salía,
     sin error en consola ni nada en el log. Pasó en producción el 2026-09-06 y
     dejó «Generar facturas» de Despachos imposible de usar; se midió contra el
     log de nginx (12 POST entre el 31/08 y el 04/09, **cero** el 07/09).
   · `.flash-wrap` —los avisos y el toast de «Deshacer»— estaba en **50**, o sea
     que con un modal abierto NINGÚN aviso del sistema se veía. Peor: el clic
     donde estaba «Deshacer» aterrizaba en `.mdl-overlay`, cuyo `onMouseDown`
     cierra el modal. Se confirmaba un borrado y no se podía deshacer.

   Medido en Chromium el 2026-09-16, montando el caso sobre una pantalla real:
   con los valores viejos el botón de confirmar no era alcanzable ni con
   `elementFromPoint`; con estos, el clic llega y el POST sale.
   ⚠️ Estos tres números se mueven JUNTOS o no se mueven. Lo cuida
   `test_los_avisos_van_encima_de_cualquier_dialogo`. */
:root{
  --capa-dialogo:1000;   /* `.mdl-overlay`, `.etq-scrim` — tapan la pantalla */
  --capa-flotante:1100;  /* desplegables y menús DENTRO de un diálogo */
  --capa-aviso:2000;     /* `.flash-wrap` — tapa al diálogo */
  --capa-confirmar:2100; /* `.confirm-*` — tapa a todos, es la última palabra */
}
/* ⚠️ `--capa-flotante` está acá para que se lea junto a las otras, pero el valor
   vive ESCRITO EN `ui/usePanelFlotante.js` (y en el modo portal de
   `ui/Menu.jsx`, desde el 2026-10-05): ese estilo va en línea y le gana a
   cualquier clase, y un `var()` que no resuelva dejaría las opciones de un
   desplegable otra vez detrás del modal —el bug que el usuario reportó el
   2026-09-13 al crear un cliente—. Si se cambia uno, se cambia el otro; lo
   comprueba `test_un_desplegable_dentro_de_un_modal_se_ve`. */

/* ── Modal de confirmación + toast de deshacer (reemplaza confirm() nativo,
   2026-07-12) ─────────────────────────────────────────────────────────── */
.confirm-scrim{position:fixed;inset:0;background:rgba(34,38,46,.35);
  z-index:var(--capa-confirmar);
  opacity:0;pointer-events:none;transition:opacity .3s var(--ease-spring)}
.confirm-scrim.open{opacity:1;pointer-events:auto}
.confirm-modal{position:fixed;top:50%;left:50%;transform:translate(-50%,-48%);
  z-index:calc(var(--capa-confirmar) + 1);
  background:var(--panel);border:1px solid var(--borde);
  border-radius:10px;box-shadow:0 20px 48px rgba(0,0,0,.18);
  padding:22px 24px;max-width:420px;width:calc(100% - 40px);
  opacity:0;pointer-events:none;transition:opacity .3s var(--ease-spring), transform .3s var(--ease-spring)}
.confirm-modal.open{opacity:1;pointer-events:auto;transform:translate(-50%,-50%)}
.confirm-modal-msg{font-size:14.5px;color:var(--texto);line-height:1.5;margin:0 0 18px}
.confirm-modal-acts{display:flex;justify-content:flex-end;gap:10px}

.confirm-toast{display:flex;align-items:center;gap:14px;position:relative;
  overflow:hidden;padding-bottom:16px}
.confirm-toast-texto{flex:1}
.confirm-toast-deshacer{all:unset;cursor:pointer;font-weight:800;
  text-decoration:underline;flex-shrink:0;color:#fff}
.confirm-toast-barra-wrap{position:absolute;left:0;right:0;bottom:0;height:3px;
  background:rgba(255,255,255,.3)}
.confirm-toast-barra{width:100%;height:100%;background:#fff;transition:width linear}

@media (prefers-reduced-motion: reduce){
  .confirm-scrim,.confirm-modal{transition:none}
  .confirm-toast-barra{transition:none}
}

/* ── Modal de etiqueta suelta (botón "Nueva etiqueta" del sidebar, 2026-08-18)
   Mismo lenguaje visual que `.confirm-modal` pero con clases propias: ese lo
   crea y lo referencia `confirm-modal.js`, y compartir las clases acoplaría dos
   modales que no tienen nada que ver. Los campos usan los estilos globales de
   `label`/`input`/`textarea`/`.btn`, así que acá solo va la caja. ──────────── */
.etq-scrim{position:fixed;inset:0;background:rgba(34,38,46,.35);z-index:60;
  opacity:0;pointer-events:none;transition:opacity .3s var(--ease-spring)}
.etq-scrim.open{opacity:1;pointer-events:auto}
.etq-modal{position:fixed;top:50%;left:50%;transform:translate(-50%,-48%);
  z-index:61;background:var(--panel);border:1px solid var(--borde);
  border-radius:10px;box-shadow:0 20px 48px rgba(0,0,0,.18);
  padding:22px 24px;max-width:420px;width:calc(100% - 40px);
  opacity:0;pointer-events:none;transition:opacity .3s var(--ease-spring), transform .3s var(--ease-spring)}
.etq-modal.open{opacity:1;pointer-events:auto;transform:translate(-50%,-50%)}
.etq-modal h2{font-size:17px;margin:0 0 2px}
.etq-modal .sub{margin-bottom:18px;font-size:13px}
.etq-campo{display:flex;flex-direction:column;gap:5px;margin-bottom:16px}
.etq-modal textarea{min-height:96px;line-height:1.45}
.etq-modal input[type=number]{width:110px}
.etq-acts{display:flex;justify-content:flex-end;gap:10px;margin-top:4px}

@media (prefers-reduced-motion: reduce){
  .etq-scrim,.etq-modal{transition:none}
}

/* ── Barra de filtros (.fl-*) ─────────────────────────────────────────────
   Vivía en un `<style>` en línea dentro de `compras/templates/compras/lista.html`
   y la usaban DOS módulos: Compras y Estados de Cuenta. O sea que el buscador
   de Estados de Cuenta se renderizaba **sin estilos** — input sin alto, sin
   borde, sin radio y sin el foco vino, y la lupa `⌕` (que necesita el
   `position:absolute` de `.fl-ico`) saliendo como texto suelto delante del
   campo. Medido el 2026-08-20, al migrar Estados de Cuenta a React.
   No hay ningún selector `.fl-*` más en este archivo, así que subirlo acá no
   cambia nada en Compras: son las mismas reglas, sin conflicto de
   especificidad. ─────────────────────────────────────────────────────────── */
.fl-bar{margin-bottom:20px}
.fl-fields{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.fl-field{position:relative;display:flex;align-items:center}
.fl-field input,.fl-field select{height:36px;padding:0 12px;border:1px solid var(--borde);
  border-radius:8px;font-size:13px;font-family:inherit;background:var(--panel);
  color:var(--texto);min-width:0}
.fl-field input:focus-visible,.fl-field select:focus-visible{outline:2px solid var(--vino);outline-offset:2px}
.fl-wide input{padding-left:30px;padding-right:32px;width:220px}
.fl-ico{position:absolute;left:9px;color:var(--tenue);pointer-events:none;
  display:flex;align-items:center}
.fl-field input[type=date]{width:140px}
.fl-field:not(.fl-wide) input:not([type=date]){width:160px}

/* ── Estados de carga (esqueletos) ────────────────────────────────────────
   Para las pantallas de React, que dibujan el sidebar y el encabezado al
   instante y solo esperan los datos. El `#loader` de `ui.js` no sirve acá:
   tapa la pantalla entera, y lo que falta es una tabla.
   Respeta `prefers-reduced-motion` — un pulso infinito es justamente lo que
   esa preferencia pide apagar. `.sr-only` ya existe más arriba (línea 26), no
   se redefine. ──────────────────────────────────────────────────────────── */
.skel-wrap{padding:14px}
.skel-fila{height:38px;border-radius:8px;margin-bottom:8px;
  background:linear-gradient(90deg,var(--fondo) 25%,#EDEFF2 37%,var(--fondo) 63%);
  background-size:400% 100%;animation:skel 1.4s ease infinite}
.skel-fila:last-child{margin-bottom:0}
@keyframes skel{0%{background-position:100% 50%}100%{background-position:0 50%}}
@media (prefers-reduced-motion: reduce){
  .skel-fila{animation:none}
}

/* ── Acción discreta de fila (.btn.plano) ─────────────────────────────────
   Para la acción repetida en cada fila de una lista larga. El listado de
   Estados de Cuenta tenía **86 botones `.btn.ghost.sm` idénticos** con borde
   vino, uno por cliente: el borde se repetía 86 veces y competía con el dato
   que el operador está buscando, que es el nombre. Sin borde ni fondo, la fila
   vuelve a leerse como una lista. Decisión del usuario, 2026-08-20.
   Conserva el objetivo táctil de 44px que `.btn.sm` gana en ≤900px. ─────── */
.btn.plano{background:transparent;color:var(--vino);border:none;font-weight:700;
  padding:6px 8px}
.btn.plano:hover{background:var(--vino-tinte)}
.btn.plano:focus-visible{outline:2px solid var(--vino);outline-offset:2px}
/* ⚠️ El foco de CUALQUIER botón, no solo el plano. Sin esta regla el anillo lo
   dibujaba el navegador —azul de Chrome sobre un botón del color de la
   empresa—, que además cambia de una máquina a otra. Quien navega con teclado
   tiene que ver dónde está, y verlo igual en todas las pantallas (2026-09-16). */
.btn:focus-visible,a.btn:focus-visible{outline:2px solid var(--vino);
  outline-offset:2px}

/* ── Aviso informativo (.aviso) ───────────────────────────────────────────
   Un dato que hay que leer ANTES de leer los números de la pantalla. En
   Estados de Cuenta la nota «Cartera solo tiene datos completos desde enero de
   2026» era la letra más chica de la pantalla (12px, `--tenue`, debajo de los
   KPIs) y es justamente lo que explica por qué las facturas viejas salen sin
   pagos. Jerarquía invertida: el que explica iba después y más chico que lo
   explicado. Decisión del usuario, 2026-08-20. ──────────────────────────── */
/* ⚠️ La primera versión de esto (2026-08-20, más temprano) usaba
   `border-left:3px solid` y un fondo tintado. Violaba un Don't explícito de
   `DESIGN.md`: «no usar border-left/border-right de más de 1px como franja de
   acento decorativa en cards o listas», cuya única excepción es el indicador
   del ítem activo del sidebar. Ahora: superficie blanca, borde de 1px como
   toda card del sistema, y el color vive solo en el ícono y en la palabra que
   importa. El aviso sigue leyéndose de un vistazo y deja de inventar un
   lenguaje propio. */
.aviso{display:flex;gap:9px;align-items:center;background:var(--panel);
  border:1px solid var(--borde);border-radius:8px;padding:9px 14px;
  margin-bottom:8px;font-size:13px;line-height:1.45;color:var(--texto)}
.aviso .aviso-ico{flex-shrink:0;display:flex;color:var(--tenue)}
.aviso.peligro .aviso-ico{color:var(--peligro)}
.aviso.ok .aviso-ico{color:var(--ok)}
.aviso strong{font-weight:800}
.aviso.peligro strong{color:var(--peligro-texto,var(--peligro))}
.aviso-sub{color:var(--tenue)}

/* ── «Hay una versión nueva del sistema» ──────────────────────────────────
   Aparece después de un despliegue, en la pantalla de quien la tenía abierta:
   su JavaScript quedó viejo y al navegar pediría un archivo que ya no existe
   (`ui/AvisoVersion.jsx` tiene el porqué completo).

   ⚠️ **Empuja el contenido, no flota.** Un aviso flotante taparía la barra de
   emisión de Siigo, que vive pegada al pie; y las tablas del sistema miden su
   alto contra el fondo de la ventana en cada render, así que ceder unos píxeles
   arriba se acomoda solo.
   📌 Sigue la regla de `.aviso`: superficie de panel con borde de 1 px, y el
   color solo en el ícono — nada de franjas de acento (`DESIGN.md`). 2026-09-23. */
.aviso-version{display:flex;gap:11px;align-items:center;background:var(--panel);
  border:1px solid var(--borde);border-radius:10px;padding:10px 14px;
  margin-bottom:14px;font-size:13px;line-height:1.45;color:var(--texto)}
.aviso-version-ico{flex-shrink:0;display:flex;color:var(--vino)}
.aviso-version-texto{flex:1;min-width:0}
.aviso-version b{font-weight:800}
/* El reloj de la cuenta regresiva del deploy: con cifras de ancho fijo, «0:11»
   y «0:10» miden lo mismo y el texto de al lado no tiembla cada segundo. */
.aviso-version-reloj{font-variant-numeric:tabular-nums}
/* El botón no se encoge aunque el texto envuelva: es la salida del aviso. */
.aviso-version .btn{flex-shrink:0}
@media (max-width:900px){
  .aviso-version{flex-wrap:wrap}
  .aviso-version-texto{flex-basis:100%}
}

/* ── Filtros como chips (opción C, 2026-08-20) ─────────────────────────────
   Lo que la pantalla está filtrando queda A LA VISTA y se quita desde el
   propio chip, en vez de esconderse dentro de un `<select>` que hay que abrir
   para saber qué dice. El botón «+ Filtro» abre el menú de los que faltan.
   Reemplaza la barra de filtros `.fl-*` en las pantallas migradas; `.fl-*`
   sigue vivo mientras queden módulos en Jinja. ─────────────────────────── */
.chips{display:flex;align-items:center;gap:7px;flex-wrap:wrap;margin-bottom:18px}
.chip{display:inline-flex;align-items:center;gap:7px;background:var(--vino-tinte);
  color:var(--vino);border:none;border-radius:20px;padding:5px 8px 5px 12px;
  font-size:12px;font-weight:700;font-family:inherit;line-height:1.35}
.chip-quitar{all:unset;display:flex;cursor:pointer;color:inherit;opacity:.7;
  border-radius:50%;padding:1px}
.chip-quitar:hover{opacity:1;background:rgba(139,26,43,.12)}
.chip-quitar:focus-visible{outline:2px solid var(--vino);outline-offset:1px;opacity:1}
.chip select{all:unset;cursor:pointer;font-weight:700;font-family:inherit;
  font-size:12px;color:inherit}
.chip select:focus-visible{outline:2px solid var(--vino);outline-offset:2px;border-radius:4px}
/* ⚠️ El `all:unset` del select le saca TAMBIÉN la flecha nativa, y sin ella el
   chip se lee como texto fijo: nadie descubre que el idioma se puede cambiar.
   Visto en pantalla el 2026-08-20. La flecha se repone con un ::after en la
   etiqueta que lo envuelve. */
.chip-select{display:inline-flex;align-items:center;gap:5px;cursor:pointer}
.chip-select::after{content:"";width:0;height:0;flex-shrink:0;
  border-left:3.5px solid transparent;border-right:3.5px solid transparent;
  border-top:4px solid currentColor;opacity:.65}
.chip-select:hover::after{opacity:1}
.chip-agregar{display:inline-flex;align-items:center;gap:6px;background:transparent;
  color:var(--tenue);border:1px dashed var(--borde);border-radius:20px;
  padding:5px 12px;font-size:12px;font-weight:700;font-family:inherit;
  cursor:pointer;line-height:1.35}
.chip-agregar:hover{border-color:var(--vino);color:var(--vino);border-style:solid}
.chip-agregar:focus-visible{outline:2px solid var(--vino);outline-offset:2px}

/* ── KPIs segmentados (opción C) ───────────────────────────────────────────
   UN panel dividido por bordes de 1px, en vez de N cards separadas: menos
   cajas apiladas para la misma información. Convive con `.kpis`/`.kpi`, que
   siguen sirviendo donde las tarjetas son navegables o clicables.
   ⚠️ El segmento destacado usa `--vino-tinte` como fondo. `DESIGN.md` decía
   «nunca como color de superficie grande»; se ajustó esa regla el 2026-08-20
   por decisión del usuario, acotándola: vale en un segmento de panel, no en
   una superficie de pantalla. ─────────────────────────────────────────── */
/* ⚠️ **Flex y no grid, y el porqué es un ida y vuelta que conviene no repetir**
   (2026-09-16). Con `grid` + `auto-fit`, cinco números y ancho para cuatro dejan
   al quinto estirado a lo ancho de la fila entera: el mismo dato, cuatro veces
   más grande (Informes a 1024 px). Cambiarlo a `auto-fill` arregla eso pero rompe
   lo otro: reserva columnas vacías, así que con cuatro números en 1326 px cada
   uno se encoge a 221 px y «$ 6.379.088.324» se parte en dos renglones (Cartera).
   Con `flex:1 1 190px` no pasa ninguna de las dos: los que entran en una fila se
   reparten TODO el ancho —cinco de 190 px entran en los 966 px de una pantalla de
   1024—, y no hay columnas fantasma que encojan a nadie. */
.kpis-seg{background:var(--panel);border:1px solid var(--borde);border-radius:10px;
  display:flex;flex-wrap:wrap;overflow:hidden;margin-bottom:14px}
.kpis-seg .kseg{flex:1 1 190px;min-width:0}
.kpis-seg .kseg{display:flex;flex-direction:column;padding:16px 20px;
  border-left:1px solid var(--borde)}
.kpis-seg .kseg:first-child{border-left:none}
.kpis-seg .kseg-l{color:var(--tenue);font-size:12px;font-weight:700;
  letter-spacing:0.3px;text-transform:uppercase}
/* Mismo motivo que en `.kpi .v`: los números de una fila se leen juntos, y
   para eso tienen que estar a la misma altura aunque un rótulo sea más largo. */
.kpis-seg .kseg-v{font-size:26px;font-weight:800;color:var(--texto);
  font-variant-numeric:tabular-nums;margin-top:auto;padding-top:2px;
  line-height:1.15}
/* El pie de los números: el contexto que antes iba apretado dentro de cada
   tile (comparación con el año pasado, tallos por caja). Va bajo el panel y no
   adentro, para que los cinco números queden a la misma altura. */
.kpis-pie{color:var(--tenue);font-size:12.5px;margin:-6px 0 16px}
.kpis-seg .kseg.destacado{background:var(--vino-tinte)}
.kpis-seg .kseg.destacado .kseg-l,.kpis-seg .kseg.destacado .kseg-v{color:var(--vino)}

/* ── El segmento que además FILTRA (`<Seg onClick>`) ──────────────────────
   Nació en Siigo → Facturas el 2026-09-23, donde el panel de KPI y los chips
   de filtro mostraban los mismos tres números, uno encima del otro.
   Es un `<button>`, así que hay que apagarle lo que el navegador le pone: sin
   `font-family:inherit` los 26 px del número caen al tamaño de un botón. */
.kpis-seg .kseg-filtro{border:none;border-left:1px solid var(--borde);
  background:none;font-family:inherit;text-align:left;cursor:pointer;
  transition:background .15s ease}
.kpis-seg .kseg-filtro:first-child{border-left:none}
.kpis-seg .kseg-filtro:hover{background:var(--fondo)}
/* El activo lleva el color de la empresa, igual que `.chip-op.activo`, y una
   barra abajo: el tinte solo no alcanza para leer cuál está puesto de un
   vistazo cuando hay cuatro pegados. */
.kpis-seg .kseg-filtro.activo{background:var(--vino-tinte);
  box-shadow:inset 0 -3px 0 var(--vino)}
.kpis-seg .kseg-filtro.activo .kseg-l,
.kpis-seg .kseg-filtro.activo .kseg-v{color:var(--vino)}

/* Cabecera de panel: aloja el título y el buscador de la tabla (opción C).
   El buscador vive ACÁ y no en una barra aparte — una capa menos de chrome. */
.panel-cab{display:flex;justify-content:space-between;align-items:center;gap:14px;
  padding:12px 14px;border-bottom:1px solid var(--borde);flex-wrap:wrap}
/* ⚠️ En un celular la cabecera de un panel se apila: título arriba, acciones
   abajo alineadas a la IZQUIERDA. Con `space-between` + `flex-wrap`, los cinco
   controles de Facturas caían escalonados contra el borde derecho, uno por fila
   y con el hueco a la izquierda (2026-09-16).
   ⚠️ Va acá y no con los otros `@media` de más arriba: tiene la misma
   especificidad que la regla de la línea de arriba, así que allá perdía por
   orden y el título terminaba centrado. */
@media (max-width: 560px){
  .panel-cab{flex-direction:column;align-items:stretch;gap:10px}
  .panel-cab .acts,.panel-cab .acts-juntos{justify-content:flex-start}
}
.panel-cab h3{font-size:15px;font-weight:700;margin:0}
/* El conteo, en píldora y no pegado al título: «Detalle mensual 2026 12» se
   leía como si el 12 fuera parte del nombre de la tabla (2026-09-16). */
.panel-cab .cuenta{color:var(--tenue);font-weight:600;font-size:12px;
  background:var(--fondo);border-radius:999px;padding:2px 8px;margin-left:8px;
  vertical-align:middle;font-variant-numeric:tabular-nums}

@media (max-width: 560px){
  .kpis-seg{grid-template-columns:1fr}
  .kpis-seg .kseg{border-left:none;border-top:1px solid var(--borde)}
  .kpis-seg .kseg:first-child{border-top:none}
}


/* ══════════════════════════════════════════════════════════════════════════
   TEMA BENTO — acotado a las pantallas migradas a React
   ══════════════════════════════════════════════════════════════════════════
   Decisión del usuario del 2026-08-20, elegida sobre 10 direcciones visuales
   (lienzo: «Diez estilos para Santillana»). Mosaico de tiles con tintes
   pastel y acento lila.

   ⚠️ **EL VINO SALE DEL SOFTWARE.** Se le dijo dos veces que `#8B1A2B` está en
   el logo y en los PDFs oficiales DIAN/ICA impresos, y que abandonarlo hace que
   lo digital y lo impreso dejen de parecer la misma empresa. Mantuvo la
   decisión. **Los PDFs y el Excel siguen en vino**: los genera ReportLab y
   openpyxl del lado del servidor y no leen este archivo. O sea que la pantalla
   y el documento que recibe el cliente ya NO comparten paleta. Es la
   consecuencia asumida, no un olvido.

   ⚠️ **EL ALCANCE ES `.tema-bento`, NO EL ARCHIVO.** La clase la pone
   `react_base.html` sobre `#root`, así que solo alcanza a lo que se dibuja
   dentro de React. Lo que queda afuera conserva el vino y Manrope, pero ya NO
   son módulos: son `login.html` y `error.html` (remedido el 09-07 — esta línea
   decía «los 10 que siguen en Jinja», y no queda ninguno).
   El sidebar hereda estas variables porque vive DENTRO del árbol de React
   (`Shell` lo renderiza), así que hasta el 2026-09-02 **cambiaba de color según
   el módulo**: lila en uno migrado, vino en uno sin migrar. Ya no puede pasar
   —todas las entradas son React—, y se deja escrito porque no era un bug: se le
   mostró el parpadeo al usuario y lo aceptó. ⚠️ Esta nota ponía como ejemplo
   «Estados de Cuenta», que **dejó de ser un módulo el 2026-09-07** (se mudó a
   Cartera).

   ⚠️ **POR QUÉ SE REDEFINEN LOS TOKENS EXISTENTES en vez de crear nombres
   nuevos:** las ~48 reglas de este archivo ya referencian `--vino`,
   `--vino-tinte`, `--fondo`, etc. Redefinirlos dentro del scope hace que
   botones, sidebar, chips y avisos sigan al tema sin tocar una sola regla.
   El nombre `--vino` pasa a ser un ROL («el acento»), no un color. Si algún día
   el tema se vuelve global, hay que renombrarlos de verdad.
   ══════════════════════════════════════════════════════════════════════════ */
.tema-bento{
  /* Los tokens de siempre, re-apuntados. El nombre es el rol, no el color. */
  --vino:var(--marca); --vino-h:var(--marca-h);
  --vino-tinte:var(--marca-tinte);
  /* 🔴 **Los neutros los manda el SERVIDOR desde el 2026-09-17, no se escriben
     acá.** Estaban fijos (`--fondo:#F4F5F9` y compañía) y **pisaban los de
     `:root`**: el modo noche cambiaba los tokens y estas pantallas —o sea casi
     todo el sistema— seguían blancas. Se vio en el navegador, no leyendo el
     código, y es el mismo patrón que ya había mordido con el color de marca el
     09-16.
     ⚠️ **Los valores del modo claro NO cambiaron**: `config.NEUTROS_BENTO`
     guarda exactamente los que estaban acá, con su medición. Unificarlos con
     los de `:root` —se parecen pero no son iguales— habría repintado el modo
     que todos usan hoy.
     📌 La medición de cada uno sigue escrita acá abajo: es la razón por la que
     el modo claro tiene estos valores y no otros, y hay que leerla antes de
     tocarlos. */
  --fondo:var(--bn-fondo); --panel:var(--bn-panel); --texto:var(--bn-texto);
  /* ⚠️ #6B7280, el gris de la maqueta, daba 4.44:1 sobre este fondo — falla
     4.5:1 por poco, y los rótulos de tile son de 12px (texto de cuerpo).
     Medido el 2026-08-20. #616A78 da 5.47:1 sobre blanco y 5.02:1 sobre el
     fondo, sin cambiar la temperatura del gris. */
  --tenue:var(--bn-tenue);
  --borde:var(--bn-borde); --ok:var(--bn-ok); --peligro:var(--bn-peligro);
  /* ⚠️ `--peligro` (#F05252) es para RELLENOS Y BORDES, no para texto: da
     **3,48:1** sobre blanco y el mínimo de WCAG AA para texto de cuerpo es 4,5.
     Lo usaba `.hint.invalido` en 11 px, o sea el mensaje de error de todo
     `ComboEstricto`. Medido por `auditor-frontend` el 2026-09-14.
     `--peligro-texto` es el mismo rojo de la marca que ya vive en `tokens.css`,
     así que no entra un color nuevo al sistema. Medido el 2026-09-15: 5,44:1
     sobre blanco, 5,12:1 sobre el fondo de la app, 4,66:1 sobre el rosado de
     `.badge-status.danger` y 4,76:1 sobre el de `.proc-card.malo`. Pasa AA en
     los cuatro. Mismo criterio con el que se separó `--tenue` en agosto. */
  --peligro-texto:var(--bn-peligro-texto);
  /* ⚠️ `--borde` (#E6E8F0) da 1.22:1 contra el panel blanco: sirve para separar
     zonas, NO para dibujar el contorno de un control. WCAG 1.4.11 pide 3:1 para
     el límite de un componente. `--linea-control` da 3.67:1 sobre blanco,
     3.37:1 sobre el fondo y 3.12:1 sobre el tinte lila — medido el 2026-08-20.
     ✅ Desde el 2026-08-21 lo usan TAMBIÉN las cajas de texto, los `select` y
     los `textarea`: iban con `border:none` sobre `--fondo` y eran invisibles
     fuera de un tile. Con eso quedó cerrada la deuda de 1.4.11 que este mismo
     comentario declaraba abierta el día anterior. */
  --linea-control:var(--bn-linea-control);
  /* Los cuatro tintes del mosaico y su acento. */
  /* ⚠️ `--a-lila` NO es `--vino`, aunque nació igual (#6D4AFF). Se separaron
     el 2026-08-20: como TEXTO sobre `--t-lila` daba **4.39:1** y falla el 4.5:1
     de texto de cuerpo. Y no era un caso teórico — lo usaban el chip activo de
     Documentos, el encabezado de mes, y la opción resaltada del desplegable, o
     sea justo lo que el operador mira con el teclado. #6540F5 da 4.96:1 sobre el
     tinte. `--vino` sigue en #6D4AFF: ahí es RELLENO, no texto.
     Los otros tres acentos (3.04 / 3.11 / 4.43 sobre su tinte) solo se usan en
     `.tile-v`, que es texto grande — 3:1 — y por eso pasan. Si alguno se usa
     para texto chico, lo caza `test_ningun_par_del_tema_baja_del_minimo`. */
  --t-lila:var(--marca-tinte); --a-lila:var(--marca-acento);
  /* ⚠️ **Menta, durazno y cielo ya NO se escriben acá** (2026-09-17): los
     emite `/tokens.css` en `:root`, con un juego por tema (`config.TINTES`).
     Escritos acá pisaban los del servidor exactamente igual que los neutros
     —`.tema-bento` gana por especificidad— y el modo noche no llegaba a
     ninguna pantalla. Es el MISMO bug, encontrado dos veces el mismo día: si
     un color tiene que cambiar con el tema, se define en un solo lugar y ese
     lugar es el servidor. `--t-lila` sí se queda, porque no es un tinte fijo:
     es el de la marca de cada empresa, que ya llega ajustado al tema. */
  /* Radios del mosaico: tile 22, control 14. Mucho más abiertos que los
     10/9 del sistema anterior. */
  --r-tile:22px; --r-control:14px;
  font-family:'Figtree','Segoe UI',system-ui,Arial,sans-serif;
  background:var(--fondo);
  /* ⚠️ `min-height`: el fondo del tema (#F4F5F9) y el del `body` (#F5F6F7) son
     dos grises distintos, y en una pantalla corta —Administración, Ajustes— el
     del tema terminaba donde terminaba el contenido y el resto quedaba del
     otro. Se veía como una línea horizontal a media pantalla (2026-09-16). */
  min-height:100dvh;
}
.tema-bento h1,.tema-bento h2,.tema-bento h3,.tema-bento .brand-f{
  font-family:'Figtree','Segoe UI',system-ui,Arial,sans-serif}
.tema-bento h1{font-weight:800;letter-spacing:-.03em}

/* ⚠️ CIFRA TABULAR OBLIGATORIA EN TODA COLUMNA DE PLATA.
   Medido el 2026-08-20 con Playwright: en Figtree, `1111111111` mide **165 px**
   y `0000000000` mide **256 px** — 91 px de diferencia cada 10 dígitos, porque
   el `1` es muy angosto. Con `tabular-nums` las dos miden 249. Manrope
   perdonaba más este olvido; Figtree no. Sin esto, una columna de saldos se
   desalinea a la vista. */
.tema-bento .num,
.tema-bento td[style*="right"],
.tema-bento .tile-v,
.tema-bento .kseg-v{font-variant-numeric:tabular-nums}

/* ── El mosaico ───────────────────────────────────────────────────────────
   Rejilla de 4 columnas; cada tile declara cuántas ocupa con `--ancho`.
   El tamaño del tile ES jerarquía: el más grande responde la pregunta
   principal de la pantalla. ─────────────────────────────────────────────── */
.tema-bento .tiles{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));
  gap:14px;margin-bottom:14px}
/* `anchoTotal` de `ui/Tile.jsx`: el tile ocupa la fila entera pase lo que pase
   con `--ancho`. ⚠️ Estaba SIN DEFINIR desde que existe el mosaico —la prop se
   usa en Administración, Informes y ahora Cartera, y no hacía nada—: con la
   rejilla de 4 columnas `ancho={4}` daba el mismo resultado por casualidad, y
   por eso nadie lo notó. Se encontró el 2026-09-01 abriendo Reportes de
   Cartera en un navegador de verdad, no leyendo el CSS. */
.tema-bento .tile.ancho-total{grid-column:1/-1}
/* ⚠️ `min-width:0` NO es cosmético: sin él, un tile no puede achicarse por
   debajo del ancho de su contenido, así que una tabla ancha adentro **empuja la
   columna de la rejilla** y saca la PÁGINA ENTERA de la pantalla. El
   `.tabla-scroll` de adentro no alcanza: para que su barra aparezca, su
   contenedor tiene que poder ser más angosto que la tabla.
   Medido el 2026-09-16 a 400 px: Facturas se iba 502 px, OPD 299 y el detalle
   mensual de Informes 129, los tres por el mismo `div.tile.hueco`. La rejilla ya
   usaba `minmax(0,1fr)` justamente para esto; faltaba la otra mitad. */
.tema-bento .tile{grid-column:span var(--ancho,1);min-width:0;background:var(--panel);
  border:none;border-radius:var(--r-tile);padding:20px 22px;
  transition:transform .3s cubic-bezier(.34,1.4,.64,1), box-shadow .3s ease}
.tema-bento .tile:hover{transform:translateY(-4px);
  box-shadow:0 14px 30px rgba(23,26,33,.10)}
.tema-bento .tile-l{font-size:12px;font-weight:700;letter-spacing:.04em;
  text-transform:uppercase;color:var(--tenue)}
.tema-bento .tile-v{font-size:26px;font-weight:800;letter-spacing:-.02em;margin-top:4px;
  line-height:1.15}
.tema-bento .tile-sub{font-size:12.5px;color:var(--tenue);margin-top:2px}
.tema-bento .tile.grande .tile-v{font-size:38px}
/* Los cuatro tintes. El acento del tile se hereda al rótulo y al valor. */
/* ⚠️ EL RÓTULO NO VA EN EL COLOR DEL ACENTO, EL VALOR SÍ. Medido con la
   fórmula de WCAG el 2026-08-20: cada acento sobre su propio tinte da entre
   3,04:1 (menta) y 4,43:1 (cielo). Eso alcanza para el VALOR, que es de 26-38px
   y por lo tanto texto grande (umbral 3:1), pero NO para el rótulo, que es de
   12px y necesita 4,5:1. La primera versión de este bloque pintaba los dos con
   el acento, y los cuatro rótulos fallaban.
   El rótulo va en `--texto`: 14,8:1 a 15,6:1 sobre los cuatro tintes. Sigue
   leyéndose como rótulo por el tamaño, el peso y las mayúsculas, no por el
   color. */
.tema-bento .tile.lila{background:var(--t-lila)}
.tema-bento .tile.lila .tile-v{color:var(--a-lila)}
.tema-bento .tile.menta{background:var(--t-menta)}
.tema-bento .tile.menta .tile-v{color:var(--a-menta)}
.tema-bento .tile.durazno{background:var(--t-durazno)}
.tema-bento .tile.durazno .tile-v{color:var(--a-durazno)}
.tema-bento .tile.cielo{background:var(--t-cielo)}
.tema-bento .tile.cielo .tile-v{color:var(--a-cielo)}
.tema-bento .tile.lila .tile-l,.tema-bento .tile.menta .tile-l,
.tema-bento .tile.durazno .tile-l,.tema-bento .tile.cielo .tile-l{color:var(--texto)}
.tema-bento .tile.lila .tile-sub,.tema-bento .tile.menta .tile-sub,
.tema-bento .tile.durazno .tile-sub,.tema-bento .tile.cielo .tile-sub{color:var(--texto);opacity:.72}
/* Un tile que solo contiene otra cosa (una tabla, unos filtros) no lleva
   padding propio ni se levanta: no es un dato, es un contenedor. */
/* Un tile contenedor es una COLUMNA: así lo que tiene adentro puede estirarse
   para llenarlo en vez de dejar el resto en blanco (la grilla de estacionalidad
   ocupaba 75 px de 210). Sin esto, un `flex:1` en el contenido no hace nada,
   porque el padre es un bloque. */
.tema-bento .tile.hueco{padding:16px 12px;display:flex;flex-direction:column}
.tema-bento .tile.hueco:hover{transform:none;box-shadow:none}

/* ── La tabla, dentro de su tile, con scroll propio ───────────────────────
   Decisión del usuario: el mosaico completo entra en una pantalla y la tabla
   scrollea adentro. ⚠️ El costo aceptado son DOS scrolls anidados; para que
   no se pierda el encabezado, `thead` va `position:sticky`. Sin eso, el scroll
   interno deja al operador mirando columnas sin nombre. ──────────────────── */
.tema-bento .tabla-scroll{max-height:var(--alto-tabla,48vh);overflow:auto}
.tema-bento .tabla-scroll thead th{position:sticky;top:0;z-index:2;
  background:var(--fondo)}
/* 🔴 **Y el `tfoot` va pegado al FONDO, por la misma razón que el `thead` al
   techo.** `ui/Tabla.jsx` ya lo dibujaba como `<tfoot>` de verdad para que la
   fila de totales no se mezclara con los datos, pero sin esta regla se iba con
   el scroll igual: en el reporte mensual de Siigo el total quedaba al final de
   5.277 px de tabla, o sea que para ver cuánto se facturó en el mes había que
   bajar 103 filas. El operador termina sumando a ojo, que es exactamente lo
   que el `<tfoot>` existía para evitar.
   Alcanza a TODAS las tablas del sistema que llevan pie, no solo a esa: es el
   mismo componente. Agregado el 2026-09-23. */
/* 🔴 **La esquina de abajo a la izquierda necesita su propia regla.**
   `.tema-bento .tabla-scroll td.col-fija` pesa (0,3,1) y la de acá abajo (0,2,2),
   así que en una tabla con `columnaFija` —hoy solo `/informes/fincas`, 52
   columnas— la celda «Total» se quedaba con el `z-index:1` de la columna y una
   fila del cuerpo le pasaba por encima: medido con las dos barras desplazadas,
   en ese punto se veía un dato del cuerpo, no el total. Y además salía del color
   del panel mientras el resto del pie iba del color del fondo.
   Se vio montando la estructura y desplazando en las dos direcciones a la vez;
   leyendo el CSS no se ve. 2026-09-23. */
.tema-bento .tabla-scroll tfoot td.col-fija{z-index:3;background:var(--fondo)}
.tema-bento .tabla-scroll tfoot td{position:sticky;bottom:0;z-index:2;
  background:var(--fondo);border-top:2px solid var(--borde);font-weight:700;
  /* ⚠️ Un TOTAL no se lee partido. Sin esto, «COP 15.856.382.792» salía como
     «COP» y abajo «15.856.382.792», que es justo el número que alguien copia
     para cuadrar el mes. Medido en el navegador el 2026-09-23. */
  white-space:nowrap}
/* La primera columna quieta cuando la tabla se desplaza a lo ancho. Solo donde
   la pide `columnaFija` (`ui/Tabla.jsx`): el caso es `/informes/fincas`, donde
   un año agrupado por semana son 52 columnas y al llegar a la semana 40 ya no
   se ve de qué finca es cada renglón.
   Necesita fondo propio: sin él, el contenido que pasa por debajo se lee
   encima. El `z-index` de la esquina (thead + primera columna) tiene que ser
   mayor que el de los dos, o el título de esa columna queda tapado al
   desplazar en las dos direcciones a la vez. */
.tema-bento .tabla-scroll td.col-fija,
.tema-bento .tabla-scroll th.col-fija{position:sticky;left:0;z-index:1;
  background:var(--panel)}
.tema-bento .tabla-scroll thead th.col-fija{z-index:3;background:var(--fondo)}
/* El borde derecho marca dónde termina lo fijo; si no, las cifras parecen
   pegadas al nombre de la finca. */
.tema-bento .tabla-scroll td.col-fija{border-right:1px solid var(--borde)}
.tema-bento .tabla-scroll th.col-fija{border-right:1px solid var(--borde)}
/* ⚠️ 8 px y no 11 (2026-09-16, pedido del usuario: «se ven muy anchas las
   filas»). Con 11 la fila medía 54 px y en la lista de Facturas entraban 10;
   con 8 mide 44 y entran 13. Va acá y no en el `td` de arriba a propósito: el
   de arriba lo usan los documentos que se imprimen, donde el aire del papel es
   otra decisión. */
.tema-bento table td{border-top:none;border-bottom:1px solid var(--borde);
  padding:8px 14px}
.tema-bento table tbody tr:last-child td{border-bottom:none}
.tema-bento tbody tr{transition:background .2s ease}
.tema-bento tbody tr:hover{background:var(--fondo)}
.tema-bento tbody td:first-child{border-radius:12px 0 0 12px}
.tema-bento tbody td:last-child{border-radius:0 12px 12px 0}
.tema-bento th{border:none;letter-spacing:.05em}

/* ══ Modo denso: más renglones sin scroll ═══════════════════════════════
   Pedido del usuario el 2026-09-24 para Siigo → Facturas, donde se veían **10
   facturas** de 387 en una pantalla de 900 px: 315 px se iban antes de que la
   tabla empezara y cada fila medía 48.

   🔴 **Los selectores llevan especificidad de más A PROPÓSITO.** La primera
   versión de este bloque usaba `.tabla-densa tbody td` (0,1,2) y `.tabla-densa
   .btn` (0,2,0), y **no aplicaba ninguna de las dos**: `.tema-bento table td`
   empata en (0,1,2) y `.btn.sm` empata en (0,2,0), las dos se declaran más
   abajo, y en un empate gana la última. Se descubrió midiendo la fila en el
   navegador —seguía en 48 px— y no leyendo el CSS. Ganar por ORDEN es frágil:
   el día que alguien mueva un bloque, esto se apaga en silencio.

   📌 Y la lección de Cartera (2026-08-12, misma petición): **el culpable del
   alto de fila no suele ser el padding sino lo que va adentro**. Medido acá: la
   fila de 48 px es el botón (31) más el relleno de celda (8+8). Apretar solo la
   celda deja la fila igual, porque la altura la fija el botón. */
.tema-bento table.tabla-densa thead th{padding:6px 12px;font-size:11px}
.tema-bento table.tabla-densa tbody td{padding:4px 12px;font-size:13px}
/* `nowrap` porque «🎁 Muestra» partido en dos líneas devuelve lo ganado y más:
   es exactamente lo que le pasó a Cartera con el botón de N. Crédito. */
.tabla-densa .btn.sm{padding:3px 10px;font-size:12.5px;white-space:nowrap}
/* Un campo DENTRO de una fila densa (el ID de una bodega, el código de una
   caja, el nombre de un cargo): 28 px y no los 38 de un formulario, que eran
   los que dejaban la fila en 55 (estilo compacto de Ajustes y Nómina,
   2026-10-05). La especificidad de más es la misma lección de arriba. */
.tema-bento table.tabla-densa tbody td input:not([type=checkbox]):not([type=hidden]){
  height:28px;padding:0 10px;font-size:13px;border-radius:8px}
/* ⚠️ Acá también se apretaba el `padding`, y desde el 2026-09-30 el estado
   no tiene caja: solo queda el tamaño. */
.tabla-densa .badge-status{font-size:10.5px}
.tabla-densa tbody input[type=checkbox]{margin:0;vertical-align:middle}
/* 🔴 **El piso táctil de 44 px es por ANCHO (≤900 px), no por puntero.** Acá
   decía que lo daba un `@media (pointer:coarse)` y **esa regla no existe**: la
   única aparición de `pointer:coarse` en todo el CSS era esta frase. El piso
   vive en la línea ~1071, dentro de `@media (max-width:900px)`.
   Consecuencia real: en una tablet táctil de MÁS de 900 px —un iPad en
   horizontal, 1024 o 1180— el botón denso quedaba en 24 px sin ningún piso.
   Lo levantó una revisión adversarial antes de desplegar. */
@media (pointer:coarse){
  .tabla-densa .btn.sm{min-height:44px;display:inline-flex;align-items:center;
    justify-content:center;padding-top:0;padding-bottom:0}
}

/* El panel segmentado cuando además de mostrar FILTRA una lista larga: el número
   baja de 26 a 19 px y el relleno de 16 a 8. De 78 px a ~53. */
.kpis-seg.compacto .kseg{padding:8px 16px}
.kpis-seg.compacto .kseg-v{font-size:19px}
.kpis-seg.compacto .kseg-l{font-size:11px}

/* El encabezado de pantalla en una sola línea: el subtítulo al lado del título
   en vez de debajo, y menos margen abajo.
   ⚠️ Vuelve a apilarse bajo 900 px: en un portátil angosto el subtítulo de
   Facturas mide 380 px y empujaría los botones fuera de la fila. */
.row-head.compacta{margin-bottom:10px}
/* 🔴 El `align-items:center` va DENTRO de `min-width:901px` y no suelto.
   Suelto pesaba (0,2,0) contra el (0,1,0) del apilado de `max-width:900px`, y
   **las media queries no suman especificidad**: ganaba a cualquier ancho. En
   dirección columna el `align-items` es el eje horizontal, así que bajo 900 px
   el título y los botones quedaban centrados y encogidos al texto, mientras las
   otras 43 pantallas que usan `Cabecera` los alinean a la izquierda. Medido a
   860 px: el título arrancaba en x=173 y los botones en x=45.
   ⚠️ El primer arreglo fue un reset dentro del bloque angosto, y **tampoco
   funcionó**: empataba en (0,2,0) y perdía por orden, porque ese bloque va
   antes en el archivo. Declararlo solo donde aplica no depende del orden.
   No se vio al construirlo porque las tres mediciones fueron a 1366 px o más. */
@media (min-width:901px){
  .row-head.compacta{align-items:center}
}
.row-head.compacta .row-head-tit{display:flex;align-items:baseline;gap:12px;
  flex-wrap:wrap;min-width:0}
.row-head.compacta h1{font-size:22px;margin:0}
.row-head.compacta .sub{margin:0}
@media (max-width:900px){
  .row-head.compacta .row-head-tit{display:block}
}


/* ── Controles del tema ──────────────────────────────────────────────────── */
.tema-bento .btn{border-radius:var(--r-control);font-weight:600;
  transition:transform .2s cubic-bezier(.34,1.4,.64,1), background .2s ease}
.tema-bento .btn:hover{transform:translateY(-2px)}
.tema-bento .btn:active{transform:translateY(0)}
/* ⚠️ Este botón tenía `border-color:transparent` sobre `--panel`: BLANCO SOBRE
   BLANCO. Se veía como pastilla solo porque los primeros usos cayeron sobre el
   fondo gris de la página; el primero que cayó DENTRO de un tile («Crear
   borrador», Aforos, 2026-08-20) desapareció — quedó como texto suelto al lado
   del botón lila. Y el hover pintaba el texto con `--a-lila` sobre `--t-lila`:
   **4.39:1**, por debajo del 4.5:1 que pide texto de 14px. Las dos cosas
   pasaron porque nada medía; ahora las mide
   `test_el_boton_secundario_se_distingue_de_su_superficie`. */
.tema-bento .btn.ghost{background:var(--panel);border-color:var(--linea-control);
  color:var(--texto)}
.tema-bento .btn.ghost:hover{background:var(--t-lila);color:var(--texto);
  border-color:var(--linea-control)}
/* Tercera vez el mismo error: `background:var(--panel)` sin borde es blanco
   sobre blanco dentro de un tile. Le pasó a `.btn.ghost` (Aforos), a los
   `input` (Correos) y acá al botón neutro de la lista de Facturas. El tema
   asumía que todo control vive sobre el fondo gris de la página. */
.tema-bento .btn.gray{background:var(--panel);
  border:1px solid var(--linea-control);color:var(--texto)}
/* ⚠️ Estos controles tenían `border:none` con `background:var(--fondo)`, y
   `--fondo` **es el color de fondo de la página**. O sea: un input suelto sobre
   el fondo era invisible como control — se veía como texto flotando. Funcionaba
   de casualidad mientras todos los inputs vivían DENTRO de un tile blanco; el
   primero que quedó afuera (el calendario de la barra de filtros de Correos,
   2026-08-21) desapareció.

   Es exactamente el mismo error que tenía `.btn.ghost`, del otro lado: uno era
   blanco sobre blanco dentro del tile, este es gris sobre gris afuera. La causa
   común es la misma y vale escribirla: **el tema asumía que todo control vive
   sobre un tile.**

   El borde de `--linea-control` (3,37:1 sobre el fondo, 3,67:1 sobre el panel)
   arregla las dos cosas de una: se ve en cualquier superficie y recién ahora
   cumple el 3:1 que WCAG 1.4.11 pide para el límite de un control — la deuda
   que quedó anotada al migrar Aforos. */
.tema-bento input,.tema-bento select,.tema-bento textarea,
.tema-bento .fl-field input,.tema-bento .fl-field select{
  border:1px solid var(--linea-control);border-radius:var(--r-control);
  background:var(--fondo);height:38px}
.tema-bento .panel-cab input{background:var(--fondo)}
/* ⚠️ La regla de arriba le pone `height:38px`, `border-radius:14px` y
   `background:var(--fondo)` a TODO `input`. Sobre un checkbox eso da un
   rectángulo gris de 38px de alto con las esquinas redondeadas: se lee como una
   caja de texto vacía, no como algo que se marca. Correos (grupo 3, 2026-08-21)
   es la primera pantalla migrada con checkboxes, así que hasta ahora no había
   mordido — igual que el `select` dentro de un chip.

   Y acá el checkbox NATIVO se conserva, al revés que el `<select>`. La
   diferencia no es de criterio: al `<select>` lo dibuja el sistema operativo la
   LISTA DESPLEGABLE entera, y ningún CSS la alcanza. Un checkbox no tiene
   popup: se resuelve con `accent-color`, y el control del navegador ya trae
   teclado, foco y anuncio para lector de pantalla. Reescribirlo a mano sería
   perder todo eso sin ganar nada. */
/* Tinte de fila (lista de Facturas). Decisión del usuario el 2026-08-21 sobre
   tres opciones: fondo teñido suave, cielo = Exportación y lila = Nacional.

   ⚠️ **Reemplaza la franja de 5px** que tenían `.fila-nacional` /
   `.fila-exportacion` — la franja era la única excepción que quedaba en
   `FRANJAS_PERMITIDAS` del guardián de estilo, anotada como «decisión del
   usuario entre 3 opciones, resolver al migrar Logística». Resuelta.

   El tinte va en el `<td>` y no en el `<tr>`: con `border-collapse` el fondo del
   `tr` queda debajo del de la celda y el hover no lo tapa. */
.tema-bento tbody tr.cielo td{background:var(--t-cielo)}
.tema-bento tbody tr.cielo:hover td{background:#DBE8FD}
.tema-bento tbody tr.lila td{background:var(--t-lila)}
.tema-bento tbody tr.lila:hover td{background:#E4DCFD}
.tema-bento input[type=checkbox]{
  height:16px;width:16px;border:none;border-radius:0;background:none;
  accent-color:var(--vino);cursor:pointer;margin:0;flex-shrink:0}
.tema-bento input:focus,.tema-bento select:focus,.tema-bento textarea:focus{
  outline:2px solid var(--vino);outline-offset:0;box-shadow:none}
/* El panel deja de existir como caja con borde: acá todo es tile. */
.tema-bento .panel{border:none;border-radius:var(--r-tile);background:var(--panel)}
.tema-bento .panel-cab{border-bottom:1px solid var(--borde);padding:6px 12px 12px}
/* Un formulario DENTRO de un tile no puede usar la rejilla de 3 columnas del
   sistema (`.grid`): en un tile de media pantalla con dos campos, un tercio
   quedaba vacío y los dos campos angostos. Auto-fit con mínimo — dos campos
   llenan el tile de media, y en el de ancho completo siguen entrando tres. */
.tema-bento .tile .grid{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}
.tema-bento .panel-cab h3{font-size:15px;font-weight:700}
/* Avisos: sin borde, con el tinte que les corresponde. */
.tema-bento .aviso{border:none;border-radius:var(--r-control);background:var(--panel)}
.tema-bento .aviso.peligro{background:var(--t-durazno)}
.tema-bento .aviso.ok{background:var(--t-menta)}
.tema-bento .chip{border-radius:20px}
/* ⚠️ El select DENTRO de un chip vuelve a quedar desnudo. La regla de arriba
   (`.tema-bento input,select{height:38px;background:var(--fondo)}`) tiene la
   MISMA especificidad que `.chip select{all:unset}` y va después en el archivo,
   así que le ganaba: el chip de idioma se convertía en una caja gris de 38px
   dentro de una píldora de 20. Visto en pantalla el 2026-08-20. */
.tema-bento .chip select{all:unset;cursor:pointer;font-weight:700;
  font-family:inherit;font-size:12px;color:inherit;height:auto;background:transparent}
.tema-bento .kpi,.tema-bento .kpis-seg{border:none;border-radius:var(--r-tile)}

@media (prefers-reduced-motion: reduce){
  .tema-bento .tile,.tema-bento .btn,.tema-bento tbody tr{transition:none}
  .tema-bento .tile:hover,.tema-bento .btn:hover{transform:none}
}
@media (max-width: 900px){
  .tema-bento .tiles{grid-template-columns:repeat(2,minmax(0,1fr))}
  .tema-bento .tile{grid-column:span 1}
  .tema-bento .tile.ancho-total{grid-column:span 2}
}
@media (max-width: 560px){
  .tema-bento .tiles{grid-template-columns:1fr}
  .tema-bento .tile,.tema-bento .tile.ancho-total{grid-column:span 1}
}

/* ── Desplegable propio (.desp) ────────────────────────────────────────────
   Reemplaza al `<select>` nativo, cuya flecha y cuya lista las dibuja el
   sistema operativo y ningún CSS alcanza — al lado del resto de la interfaz se
   veía de otro programa (visto en pantalla el 2026-08-20).
   La lista SÍ lleva sombra: es un overlay que flota sobre el contenido, que es
   el caso que `DESIGN.md` permite. ────────────────────────────────────────── */
.desp{position:relative;display:inline-block}
/* ⚠️ El ancho que estas tres pantallas le daban a su `<select>` y que se
   perdió al cambiarlo por el desplegable propio (2026-09-03,
   `test_ninguna_regla_de_css_apunta_a_un_select_nativo`). **Solo el ancho**:
   el borde, el alto, el relleno y la tipografía ya los trae `.desp-btn`, y
   copiárselos también al contenedor pintaría un segundo borde alrededor. */
.filterbar .desp{min-width:150px}
.fl-field .desp{min-width:0}
.filtros-mes .desp{min-width:200px}

.desp-btn{display:flex;align-items:center;justify-content:space-between;gap:10px;
  width:100%;height:38px;padding:0 12px;background:var(--panel);
  border:1px solid var(--borde);border-radius:8px;font-family:inherit;font-size:13px;
  font-weight:500;color:var(--texto);cursor:pointer;text-align:left;
  transition:border-color .16s ease}
.desp-btn:hover{border-color:var(--vino)}
.desp-btn:focus-visible{outline:none;border-color:var(--vino);
  box-shadow:0 0 0 2.5px color-mix(in srgb,var(--vino) 10%,transparent)}
.desp-vacio{color:var(--tenue)}
.desp-flecha{flex-shrink:0;color:var(--tenue);transition:transform .18s ease}
.desp-btn[aria-expanded="true"] .desp-flecha{transform:rotate(180deg)}
/* ⚠️ Igual que `.combo-lista`: la posición la calcula `ui/usePanelFlotante.js`
   y va en línea, porque la lista se dibuja en un portal. Acá, solo apariencia. */
.desp-lista{margin:0;padding:5px;list-style:none;overflow-y:auto;
  background:var(--panel);border:1px solid var(--borde);border-radius:10px;
  box-shadow:0 4px 16px rgba(0,0,0,.10)}
.desp-op{display:flex;align-items:center;justify-content:space-between;gap:8px;
  padding:8px 10px;border-radius:7px;font-size:13px;color:var(--texto);cursor:pointer;
  line-height:1.35}
/* `.res` es la opción resaltada por TECLADO, y por eso se marca aparte del
   hover: quien navega con flechas tiene que ver dónde está sin mover el mouse. */
.desp-op.res{background:var(--vino-tinte);color:var(--vino)}
.desp-op.sel{font-weight:700}
@media (prefers-reduced-motion: reduce){
  .desp-flecha,.desp-btn{transition:none}
}

/* En el tema Bento el desplegable sigue al tema, como el resto de los controles. */
/* Mismo caso que los `input` y que `.btn.ghost`: `background:var(--fondo)` sin
   borde da 1,18:1 contra el tile blanco en el que vive — no se lee como un
   control. El `<button>` del desplegable no lo alcanzaba la regla de `input`
   porque no ES un input. `.chip .desp-btn` sí queda sin borde: ahí el contorno
   lo dibuja la pastilla del chip. */
.tema-bento .desp-btn{border:1px solid var(--linea-control);
  border-radius:var(--r-control);background:var(--fondo)}
.tema-bento .desp-btn:hover{background:#EAECF3}
.tema-bento .desp-btn:focus-visible{outline:2px solid var(--vino);outline-offset:0;
  box-shadow:none;background:var(--fondo)}
.tema-bento .desp-lista{border:none;border-radius:var(--r-control);
  box-shadow:0 10px 26px rgba(23,26,33,.14)}
.tema-bento .desp-op.res{background:var(--t-lila);color:var(--a-lila)}

/* El chip con desplegable adentro: el botón se desnuda y hereda el chip. */
.chip .desp-btn,.tema-bento .chip .desp-btn{height:auto;padding:0;border:none;
  background:transparent;font-size:12px;font-weight:700;color:inherit;gap:5px}
.chip .desp-btn:hover,.tema-bento .chip .desp-btn:hover{background:transparent}
.chip .desp-flecha{color:inherit;opacity:.65}
.chip .desp-lista{left:auto;right:0;min-width:150px}

/* ── Campos de fecha ──────────────────────────────────────────────────────
   El selector de fecha nativo SÍ se conserva: el calendario que abre el
   navegador es mejor que cualquier cosa que se escriba a mano acá, y ya conoce
   el formato local. Lo único que se puede estilizar es el ícono, así que se
   tiñe para que deje de ser el gris del sistema. ────────────────────────── */
input[type=date]::-webkit-calendar-picker-indicator{
  cursor:pointer;opacity:.55;transition:opacity .16s ease}
input[type=date]::-webkit-calendar-picker-indicator:hover{opacity:1}
.tema-bento input[type=date]{color:var(--texto)}


/* ══════════════════════════════════════════════════════════════════════════
   EL SIDEBAR ES BENTO EN LOS 11 MÓDULOS — global, fuera de `.tema-bento`
   ══════════════════════════════════════════════════════════════════════════
   Decisión del usuario del 2026-08-20: quiere que los 11 módulos queden
   exactamente iguales. El sidebar es el ÚNICO componente compartido por todos
   los módulos, y por eso el único que se puede unificar hoy sin dejar nada a
   medias: sus reglas usan solo tokens (`--vino`, `--vino-tinte`, `--borde`,
   `--tenue`), **cero color clavado a mano**.

   ⚠️ **Por qué el resto del programa NO se unifica hoy, aunque se haya pedido
   «todo igual»:** medido el 2026-08-20, las plantillas Jinja tienen **92
   colores hex escritos a mano en 17 archivos** (incluidos tres `#8B1A2B`
   directos). Repuntar los tokens globalmente dejaría cada uno de esos 10
   módulos **mitad lila y mitad vino** — peor que cualquiera de los dos
   extremos. La uniformidad se consigue MIGRANDO cada módulo, no con un CSS
   global. El código React, en cambio, tiene **cero** hex a mano: por eso un
   módulo migrado queda consistente por construcción.

   Efecto de este bloque: se va el parpadeo del sidebar al navegar. La
   navegación es idéntica desde hoy; el contenido difiere hasta que a cada
   módulo le toque su turno.
   ══════════════════════════════════════════════════════════════════════════ */
.sidebar{
  --vino:var(--marca); --vino-h:var(--marca-h);
  --vino-tinte:var(--marca-tinte);
  font-family:'Figtree','Segoe UI',system-ui,Arial,sans-serif;
}
.sidebar nav a,.sidebar nav .nav-accion,.side-user-name,.side-user-role,
.export span{font-family:inherit}

/* Encabezado de grupo dentro de una tabla (Documentos por mes; Compras y
   Nómina también agrupan). Va DENTRO de la misma tabla en vez de una tabla por
   grupo: con doce meses, doce tablas son doce encabezados de columna repetidos
   y doce scrolls. `scope="colgroup"` para que el lector de pantalla anuncie a
   qué grupo pertenece la fila. */
.fila-grupo th{background:var(--fondo);color:var(--texto);font-size:12.5px;
  font-weight:800;letter-spacing:0;text-transform:none;padding:9px 14px;
  text-align:left;position:static}
.fila-grupo-sub{color:var(--tenue);font-weight:500}
/* Un grupo con algo a la derecha (el saldo del proveedor en Pagos, 2026-10-05):
   el título a la izquierda y la cifra pegada al borde, en el mismo renglón. */
.fila-grupo-flex{display:flex;align-items:baseline;gap:6px}
.fila-grupo-fin{margin-left:auto;font-variant-numeric:tabular-nums}
.tema-bento .fila-grupo th{background:var(--t-lila);color:var(--a-lila);border-radius:8px}

/* Chip seleccionable (`.chip-op`) — reemplaza a las pestañas `.doctab`. Un
   grupo donde una opción está activa. Mismo aspecto que el chip de filtro, así
   las pestañas de un módulo no se ven distintas de los filtros de otro. */
.chip-op{background:transparent;color:var(--tenue);border:1px solid var(--borde);
  cursor:pointer;padding:5px 12px;transition:border-color .16s ease, color .16s ease}
.chip-op:hover{border-color:var(--vino);color:var(--vino)}
.chip-op:focus-visible{outline:2px solid var(--vino);outline-offset:2px}
.chip-op.activo{background:var(--vino-tinte);color:var(--vino);border-color:transparent}
.chip-conteo{background:rgba(0,0,0,.06);border-radius:20px;padding:0 6px;font-size:11px;
  font-weight:700;margin-left:2px}
.chip-op.activo .chip-conteo{background:rgba(255,255,255,.6)}
.tema-bento .chip-op{border:none;background:var(--panel)}
.tema-bento .chip-op:hover{background:var(--t-lila);color:var(--a-lila)}
.tema-bento .chip-op.activo{background:var(--t-lila);color:var(--a-lila)}

/* ── Tarjeta de pedido (Compras → `modules/compras/Lista.jsx`) ─────────────
   Sube acá desde el `<style>` de 60 líneas que tenía `compras/lista.html`. El
   guardián de estilo prohíbe esos bloques justamente para que no haya cinco
   variantes de la misma tarjeta repartidas por las plantillas.
   Un «pedido» agrupa las líneas que llegaron juntas (proveedor + fito o
   remisión); la tarjeta es esa unidad. ─────────────────────────────────── */
.pc{background:var(--panel);border:1px solid var(--borde);border-radius:10px;
  margin-bottom:14px;overflow:hidden;
  transition:transform .3s var(--ease-spring), border-color .2s ease, box-shadow .3s var(--ease-spring)}
.pc:hover{transform:translateY(-3px);border-color:var(--vino);box-shadow:0 8px 20px rgba(34,38,46,.10)}
.pc-hdr{display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:14px 20px;border-bottom:1px solid var(--borde)}
.pc-hdr-l{display:flex;align-items:center;gap:8px;flex-wrap:wrap;min-width:0}
.pc-prov{font-size:14px;font-weight:700;color:var(--texto);white-space:nowrap}
.pc-fecha{font-size:12px;color:var(--tenue);white-space:nowrap}
.pc-hdr-r{display:flex;align-items:center;gap:16px;flex-shrink:0}
.pc-stat{display:flex;flex-direction:column;align-items:flex-end}
.pc-stat-v{font-size:14px;font-weight:600;font-variant-numeric:tabular-nums;line-height:1.2}
.pc-stat-v.money{color:var(--vino)}
.pc-stat-l{font-size:10px;color:var(--tenue);text-transform:uppercase;letter-spacing:.06em}
.pc-pill{display:inline-flex;align-items:center;padding:2px 8px;border-radius:20px;
  font-size:11px;font-weight:600;white-space:nowrap}
.pc-pill.fito{background:#EAF3FE;color:var(--grupo-hydrangea)}
.pc-pill.rem{background:#F0F4F8;color:#596070}
.pc-tbl{width:100%;border-collapse:collapse;font-size:13px}
.pc-tbl thead th{font-size:10.5px;font-weight:700;color:var(--tenue);
  text-transform:uppercase;letter-spacing:.07em;padding:9px 16px;
  background:var(--fondo-alt,#F7F8FA);border-bottom:1px solid var(--borde);
  white-space:nowrap}
.pc-tbl tbody td{border-bottom:1px solid var(--borde)}
.pc-tbl tbody tr:last-child td{border-bottom:none}
.pc-tbl tbody tr:hover td{background:var(--fondo-alt,#F7F8FA)}
.pc-tbl tfoot td{padding:11px 14px;background:var(--fondo-alt,#F7F8FA);
  border-top:2px solid var(--borde)}
.td-var{font-weight:500}
.td-num{font-variant-numeric:tabular-nums}
/* Un dato que NO se lee partido: un número de factura, una fecha, un NIT.
   Se usa en la celda, no en la columna entera, porque el que sí puede partirse
   —el nombre del cliente— convive con ellos en la misma tabla. 2026-09-23. */
.nowrap{white-space:nowrap}
.td-money{color:var(--vino);font-variant-numeric:tabular-nums;font-weight:600}
.tfoot-l{color:var(--tenue);font-size:11px;text-transform:uppercase;letter-spacing:.06em}
.empty-icon{font-size:36px;margin-bottom:10px;opacity:.5}
@media (prefers-reduced-motion: reduce){
  .pc{transition:none}
  .pc:hover{transform:none}
}

/* ── Formulario de pedido (Compras → `modules/compras/PedidoForm.jsx`) ─────
   Sube acá desde el `<style>` de 50 líneas que tenía `compras/pedido_form.html`.
   `.mdl-*` NO se repite: el modal de descarte usa el `ui/Modal.jsx` compartido,
   que ya trae esos estilos desde la migración de Facturas. ──────────────── */
.pf-card{background:var(--panel);border:1px solid var(--borde);border-radius:10px;overflow:hidden;
  transition:transform .3s var(--ease-spring), border-color .2s ease, box-shadow .3s var(--ease-spring)}
.pf-card:hover{transform:translateY(-3px);border-color:var(--vino);box-shadow:0 8px 20px rgba(34,38,46,.10)}
/* ⚠️ Se ve igual que `.panel-cab h3`, que es como se titula un panel en todo el
   resto del sistema. Era la segunda forma: 13px en gris y MAYÚSCULAS, usada en
   cuatro tarjetas de formulario de Compras, así que «Generar cuenta de cobro» y
   «Cuentas generadas» —una encima de la otra en la misma pantalla— se leían
   como dos cosas de distinta jerarquía (2026-09-16). */
.pf-card-title{font-size:15px;font-weight:700;color:var(--texto);
  padding:14px 20px;border-bottom:1px solid var(--borde)}
.pf-card-title-fl{display:flex;align-items:center;justify-content:space-between}
.pf-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;padding:18px 20px}
@media(max-width:900px){.pf-grid{grid-template-columns:1fr 1fr}}
@media(max-width:560px){.pf-grid{grid-template-columns:1fr}}
.pf-table{width:100%;border-collapse:collapse;font-size:13px}
.pf-table thead th{font-size:11px;font-weight:700;color:var(--tenue);text-transform:uppercase;
  letter-spacing:.06em;padding:10px 12px;background:var(--fondo-alt,#F7F8FA);
  border-bottom:1px solid var(--borde);white-space:nowrap}
.pf-table tbody .pf-row td{padding:5px 6px;border-bottom:1px solid var(--borde)}
.pf-table tbody .pf-row:last-child td{border-bottom:none}
.pf-table tbody .pf-row:hover td{background:var(--fondo-alt,#F7F8FA)}
/* Acá vivía `.pf-table tfoot .pf-tot td`, borrado el 2026-10-01: `ui/Tabla.jsx`
   arma el `<tfoot>` sin esa clase, así que nunca se aplicó, y desde el 09-23 ese
   pie lo pinta la regla general del sistema. */
.pf-hint{font-size:11px;color:var(--tenue);padding:8px 20px;border-top:1px solid var(--borde)}
/* Inputs dentro de la tabla: sin borde hasta que se los toca, para que la
   grilla se lea como una tabla y no como un muro de cajas. */
.ic{width:100%;padding:7px 9px;border:1px solid transparent;border-radius:8px;
  font-size:13px;font-family:inherit;background:transparent;box-sizing:border-box}
.ic:hover,.ic:focus{border-color:var(--borde);background:var(--panel);outline:none}
.ic:focus-visible{outline:2px solid var(--vino);outline-offset:2px}
.ic[readonly]{color:var(--tenue);cursor:not-allowed}
.ir{text-align:right}
.vc{text-align:right;font-variant-numeric:tabular-nums;color:var(--vino);
  font-weight:600;padding:0 12px;white-space:nowrap}
.bd{background:none;border:none;color:var(--tenue);cursor:pointer;font-size:13px;
  padding:4px 6px;border-radius:4px;line-height:1;transition:all .15s}
.bd:hover:not(:disabled){background:var(--t-peligro,var(--fondo));
  color:var(--peligro-texto,var(--peligro))}
.bd:disabled{opacity:.35;cursor:not-allowed}
@media (prefers-reduced-motion: reduce){
  .pf-card{transition:none}
  .pf-card:hover{transform:none}
}

/* ── Pagos y cuentas de cobro (Compras, grupo 3) ───────────────────────────
   Suben acá desde los `<style>` de `pagos.html` y `cuentas.html`. ─────── */
.sc{background:var(--panel);border:1px solid var(--borde);border-radius:10px;
  margin-bottom:10px;overflow:hidden}
.sc-hdr{display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:14px 20px;flex-wrap:wrap}
.sc-hdr-l{display:flex;flex-direction:column;gap:2px}
.sc-prov{font-size:14px;font-weight:700;color:var(--texto)}
.sc-ncmp{font-size:11px;color:var(--tenue)}
.sc-stat{display:flex;flex-direction:column;align-items:flex-end}
.sc-lbl{font-size:10px;text-transform:uppercase;letter-spacing:.07em;color:var(--tenue)}
.sc-val{font-size:16px;font-weight:700;font-variant-numeric:tabular-nums}
.sc-deuda{color:var(--vino)}
.cta-row{display:flex;align-items:center;gap:12px;padding:10px 20px;
  border-top:1px solid var(--borde);font-size:13px}
.cta-num{font-weight:700;width:110px}
.cta-fecha{color:var(--tenue);width:80px}
.cta-saldo{flex:1;text-align:right;font-weight:700}
.pago-form{background:var(--fondo);border-top:1px solid var(--borde);padding:14px 20px}
/* El equivalente en pesos: va en un recuadro punteado porque NO es un dato
   guardado, es lo que hay que transferir. Lo calcula el servidor. */
.pf-equiv{font-size:12.5px;color:var(--tenue);margin:10px 0;padding:8px 10px;
  border:1px dashed var(--borde);border-radius:8px}
/* Los dos campos de solo lectura del formulario de cuenta. */
.cc-ro{display:grid;grid-template-columns:1fr 1fr;gap:14px 20px;margin-bottom:16px}
@media(max-width:560px){.cc-ro{grid-template-columns:1fr}}
.ic-ro{background:var(--fondo);color:var(--tenue)}

/* ── Trazabilidad ICA (Compras, grupo 4) ─────────────────────────────────── */
.ica-alertas{margin-bottom:14px;padding:14px 18px}
.ica-alertas-tit{font-weight:600;margin-bottom:8px}
.ica-alertas-pie{margin-top:8px;font-size:12px;color:var(--tenue)}
.ica-filtros{display:flex;gap:12px;flex-wrap:wrap;align-items:center;margin-bottom:14px}
/* ⚠️ El tinte de fila avisa que ESA flor viene de un predio con el registro ICA
   vencido o por vencer. Asignarla invalida la constancia, así que el aviso va
   en la fila y no en una nota al pie. */
.fila-vencido td{background:var(--t-peligro,rgba(220,38,38,.06))}
.fila-proximo td{background:var(--t-alerta,rgba(245,158,11,.08))}

/* ── Facturación Siigo (migración 2026-08-31) ──────────────────────────────
   El panel de proformas al lado del contenido. En el `app.js` viejo esto era
   el sidebar entero; al entrar Siigo al Shell del sistema —que usa el sidebar
   para los módulos— pasó a ser una columna propia (decisión del usuario).
   ⚠️ NO es navegación: es el área de trabajo. Se elige una proforma acá y su
   detalle aparece al lado. ──────────────────────────────────────────────── */
/* ⚠️ **Una columna por omisión, DOS solo cuando el panel está.** Desde que el
   panel de proformas no se dibuja con la selección vacía (2026-09-02), la
   regla de dos columnas fijas dejaba la primera —230 px— para el contenido, y
   la pantalla de Facturas entera salía comprimida en esa franja: KPIs
   apilados, tabla cortada, botones amontonados. Se vio en el navegador, no
   leyendo el CSS.
   `:has()` y no una clase puesta por React a propósito: el layout depende de si
   HAY panel, y eso ya lo dice el DOM — una clase sería un segundo lugar donde
   vive el mismo dato y podría quedar desincronizada. */
.sg-layout{display:grid;grid-template-columns:minmax(0,1fr);gap:18px;align-items:start}
.sg-layout:has(> .sg-panel){grid-template-columns:230px minmax(0,1fr)}
@media(max-width:900px){.sg-layout,.sg-layout:has(> .sg-panel){grid-template-columns:1fr}}
.sg-vista{min-width:0}
.sg-panel{background:var(--panel);border:1px solid var(--borde);border-radius:12px;
  padding:14px;position:sticky;top:18px}
.sg-panel-tit{font-size:10px;font-weight:700;letter-spacing:.08em;
  text-transform:uppercase;color:var(--tenue);margin-bottom:10px}
.sg-panel-acts{display:flex;flex-direction:column;gap:6px;margin-top:12px;
  padding-top:12px;border-top:1px solid var(--borde)}
.sg-lista{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:4px}
.sg-item{display:flex;align-items:flex-start;gap:8px;width:100%;padding:8px;
  border:none;background:none;border-radius:9px;cursor:pointer;text-align:left;
  font-family:inherit;transition:background .15s ease}
.sg-item:hover{background:var(--fondo)}
.sg-item-cuerpo{display:flex;flex-direction:column;gap:2px;min-width:0}
.sg-item-nombre{font-size:12.5px;font-weight:600;color:var(--texto);
  display:flex;align-items:center;gap:4px;flex-wrap:wrap}
.sg-item-meta{font-size:11px;color:var(--tenue);font-variant-numeric:tabular-nums}
/* El punto resume el estado de la proforma; lo decide el servidor. */
.pf-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0;margin-top:4px;
  background:var(--tenue)}
.pf-dot.ok{background:var(--ok)}
.pf-dot.warn{background:var(--alerta)}
.pf-dot.err{background:var(--peligro)}
/* ⚠️ `.info` FALTABA, y por eso «no avisaba» que el borrador estaba creado:
   el servidor marca la proforma con `dot:"info"` en cuanto Siigo devuelve
   `draft_saved`, pero sin esta regla el punto quedaba del gris de «pendiente»
   y no había forma de distinguirlas. Lo reportó el usuario el 2026-09-02.
   `test_toda_clase_usada_existe_en_el_css` no lo vio: `pf-dot` se compone con
   un template string y el tono llega en una variable. */
.pf-dot.info{background:var(--vino)}

/* La moneda con la que la factura va a quedar en Siigo, bajo el total de la
   proforma. En `distinta` cuando NO coincide con la de la proforma —una
   proforma en dólares facturada como nacional queda en pesos—, que es
   justamente el caso en el que hay que mirar dos veces. */
.pv-emision{display:block;font-size:11px;font-weight:700;color:var(--tenue);
  letter-spacing:.02em;margin-top:2px;text-align:right}
.pv-emision.distinta{color:var(--alerta)}

/* Una proforma que YA está en Siigo (borrador guardado o factura emitida). Se
   tiñe la fila entera, no solo el punto: con seis facturas en la lista el
   punto de 8 px no alcanza para ver de un vistazo cuáles ya se subieron. */
.sg-item.hecho{background:var(--vino-tinte)}
.sg-item.hecho:hover{background:var(--vino-tinte)}
.sg-item.hecho .sg-item-nombre{color:var(--vino)}
.sg-item-estado{font-size:10.5px;font-weight:700;color:var(--vino);
  letter-spacing:.02em;white-space:nowrap}
/* 🔴 El estado se muestra ahora TAMBIÉN cuando hay algo que mirar, y entonces
   lleva su color: hasta el 2026-09-23 solo salía en las ya resueltas, así que
   «1 alerta(s) · 2 aviso(s)» y «Cliente no encontrado» —las que piden
   atención— eran las únicas tarjetas sin una línea de estado.
   ⚠️ `--alerta` YA es el tono oscuro (#8A4F00) y sirve para texto; `--peligro`
   es de relleno y no llega al 4,5:1 que pide AA, así que va su variante, igual
   que en `ui/Kpis.jsx`. (No existe `--alerta-texto`: no hace falta.) */
.sg-item-estado.t-warn{color:var(--alerta)}
.sg-item-estado.t-err{color:var(--peligro-texto,var(--peligro))}

/* Cuál de las proformas estás mirando. Hasta el 2026-09-23 la abierta se veía
   exactamente igual que las otras cinco. La barra va a la izquierda y no como
   fondo, porque `.hecho` ya usa el fondo para otra cosa y las dos se apilan
   (una emitida que además estás mirando). */
.sg-item.abierta{box-shadow:inset 3px 0 0 var(--vino);background:var(--fondo)}
.sg-item.abierta .sg-item-nombre{color:var(--vino)}
/* El conteo del panel, como el de las cabeceras de tabla (`.panel-cab .cuenta`). */
.sg-panel-tit .cuenta{color:var(--tenue);font-weight:700;margin-left:6px;
  font-variant-numeric:tabular-nums}

/* Notas crédito y alias de clientes (Siigo, grupo 2). */
/* Las pendientes de aplicar, como renglones de una sola lista (2026-10-05).
   Eran tarjetas `.panel` sin relleno: el texto tocaba el borde y se cortaba. */
.nc-lista{margin-bottom:16px}
.nc-fila{display:flex;align-items:center;gap:16px;padding:11px 14px;
  border-top:1px solid var(--borde)}
.nc-fila:first-child{border-top:none}
.nc-info{flex:1 1 auto;min-width:0}
.nc-tit{display:flex;align-items:center;gap:8px;font-size:14px}
.nc-sub{font-size:12.5px;color:var(--tenue);margin-top:2px}
.nc-aviso{font-size:12.5px;color:var(--tenue);margin-top:4px}
.nc-aviso.warn{color:var(--alerta);font-weight:600}
.nc-aviso.peligro{color:var(--peligro-texto,var(--peligro));font-weight:600}
.nc-fila .acts{flex:0 0 auto;display:flex;gap:6px;flex-wrap:wrap;justify-content:flex-end}
/* ⚠️ Se ve igual que `.tarjetas-tt`, que es como se titula una sección en el
   resto del sistema (Informes, Ajustes, Administración). Era una tercera forma
   —13px en gris y MAYÚSCULAS— y en Notas crédito convivía a cuatro píxeles de
   la cabecera de un panel, que es otra jerarquía (2026-09-16). */
.sec-title{font-size:15px;font-weight:700;letter-spacing:-.01em;margin:22px 0 12px}
.sec-title:first-child{margin-top:0}
/* Los clientes sin alias, como renglones de una sola lista (2026-10-05). Eran
   cajas sueltas con borde y radio cada una, con el combo estirado a media
   pantalla. */
.cd-lista{margin-bottom:16px}
.cd-row{display:flex;align-items:center;gap:10px;padding:8px 12px;
  border-top:1px solid var(--borde)}
.cd-row:first-child{border-top:none}
.cd-siigo{flex:1;min-width:0;font-size:13px;font-weight:600;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cd-row .combo{flex:0 1 420px;min-width:0}
@media(max-width:700px){.cd-row{flex-direction:column;align-items:stretch}}

/* Detalle de proforma (Siigo, grupo 3). */
.pv-chips{display:flex;gap:6px;flex-wrap:wrap;margin-top:4px}
.mchip{display:inline-flex;align-items:center;padding:2px 8px;border-radius:20px;
  background:var(--fondo);color:var(--tenue);font-size:11px;font-weight:600;
  white-space:nowrap}
.pv-total{font-size:22px;font-weight:800;font-variant-numeric:tabular-nums;
  color:var(--texto);line-height:1}
.pv-total .cur{font-size:12px;font-weight:700;color:var(--tenue);margin-right:5px}
/* La casilla de autorizar: es la que separa una factura revisada de una emitida
   por error, así que se ve como un control, no como una línea de texto. */
.auth-box{display:flex;align-items:center;gap:9px;padding:11px 13px;margin:12px 0;
  border:1px solid var(--borde);border-radius:10px;background:var(--fondo);
  font-size:13px;cursor:pointer}

/* ── La barra que emite la factura ────────────────────────────────────────
   🔴 Los tres botones flotaban sueltos entre los hallazgos y «Ajustes de esta
   factura», así que **cambiaban de sitio con cada proforma**: a 300 px del
   borde en una limpia y a 640 px en una con tres hallazgos, porque los avisos
   los empujaban hacia abajo. «Emitir oficial» es irreversible ante la DIAN.
   Y con tres hallazgos el contenido medía 729 px en una ventana de 678: lo que
   quedaba fuera de la vista era justo el candado y los botones que habilita.
   Anclada al pie, está siempre en el mismo lugar y siempre visible.
   2026-09-23. */
.pv-barra{position:sticky;bottom:0;z-index:3;display:flex;align-items:center;
  justify-content:space-between;gap:16px;flex-wrap:wrap;
  margin:16px 0 0;padding:12px 14px;
  background:var(--panel);border:1px solid var(--borde);border-radius:12px;
  box-shadow:0 -2px 12px rgba(0,0,0,.06)}
/* «Quitar» queda solo a la izquierda; las dos que escriben en Siigo, juntas a
   la derecha. Antes «Quitar» estaba a 7 px de «Emitir oficial». */
.pv-barra-der{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  justify-content:flex-end}
/* El candado, dentro de la barra: sin margen propio y sin ocupar todo el ancho. */
.auth-box.en-barra{margin:0;padding:7px 11px;font-size:12.5px;
  background:var(--vino-tinte);border-color:var(--vino)}
@media (prefers-reduced-motion: reduce){
  .kpis-seg .kseg-filtro,.barra-cifra-filtro{transition:none}
}
.code{background:var(--fondo);border:1px solid var(--borde);border-radius:8px;
  padding:10px 12px;font-size:11.5px;overflow-x:auto;white-space:pre-wrap;
  word-break:break-all;margin:6px 0}
.campo-trm{margin-bottom:16px}
.campo-trm label{display:block;font-size:12px;font-weight:600;color:var(--tenue);
  margin-bottom:6px}
.inline{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:8px}

/* Buscadores remotos y segmentado de modo (Siigo, grupo 4). */
.sg-buscar{margin-top:8px}
.sg-buscar-lista{list-style:none;margin:8px 0 0;padding:0;max-height:280px;
  overflow-y:auto;border:1px solid var(--borde);border-radius:10px}
.sg-buscar-fila{display:flex;align-items:center;justify-content:space-between;
  gap:10px;padding:8px 10px;border-bottom:1px solid var(--borde)}
.sg-buscar-fila:last-child{border-bottom:none}
.sg-buscar-nombre{font-size:13px;font-weight:600}
.sg-buscar-vacio{font-size:12.5px;color:var(--tenue);padding:10px 2px}
.sg-corregir{padding:12px;border:1px solid var(--borde);border-radius:10px;
  background:var(--fondo);margin-top:8px}
/* El segmentado del modo de facturación. */
.seg{display:inline-flex;border:1px solid var(--borde);border-radius:9px;
  overflow:hidden}
.seg button{padding:7px 14px;border:none;background:var(--panel);cursor:pointer;
  font-family:inherit;font-size:12.5px;font-weight:600;color:var(--tenue);
  border-right:1px solid var(--borde)}
.seg button:last-child{border-right:none}
.seg button:hover:not(:disabled){background:var(--fondo)}
.seg button.on{background:var(--vino);color:var(--panel);cursor:default}

/* Trabajos largos con progreso, y los paneles de tarea (Siigo, grupo 5). */
.sg-panel-tarea{padding:14px 16px;margin-bottom:14px}
.tl-barra{margin:10px 0}
/* Un trabajo de fondo que se cortó. El relleno se apaga y el mensaje pasa a
   ámbar: la barra tiene que verse DISTINTA de una que avanza, o el usuario
   sigue esperando frente a un progreso congelado (2026-09-02). */
.tl-barra.cortado .tl-msg{color:var(--alerta);font-weight:700}
.tl-barra.cortado .tl-lleno{background:var(--alerta);opacity:.45}
.tl-msg{font-size:13px;font-weight:600;color:var(--vino);margin-bottom:6px}
.tl-riel{height:6px;background:var(--borde);border-radius:3px;overflow:hidden}
.tl-lleno{height:100%;background:var(--vino);transition:width .3s ease}
/* El final de un trabajo largo, con su botón de descarga al lado.
   ⚠️ El botón se agregó el 2026-09-03: antes la descarga se disparaba en un
   único instante —cuando el sondeo veía terminar el trabajo— y si la pestaña
   no estaba abierta ahí, el archivo quedaba generado sin forma de bajarlo. */
.tl-fin{margin:10px 0;font-size:13px;color:var(--ok);font-weight:600;
  display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.consec-row{display:flex;gap:14px;padding:10px 0;border-bottom:1px solid var(--borde)}
.consec-row:last-of-type{border-bottom:none}
.consec-lbl{width:150px;flex-shrink:0;font-size:13px;font-weight:700}
.consec-body{flex:1;min-width:0;font-size:13px}
.consec-range{font-variant-numeric:tabular-nums;color:var(--tenue)}
.consec-nums{font-variant-numeric:tabular-nums}
@media(max-width:640px){.consec-row{flex-direction:column;gap:4px}
  .consec-lbl{width:auto}}

/* Bitácora de errores (Siigo, grupo 5). */
.err-card{margin-bottom:10px;padding:14px 16px}
.err-head{display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin-bottom:10px}
.err-title{font-size:13.5px;font-weight:700;flex:1;min-width:0}
.err-body{font-size:12.5px;color:var(--tenue);margin-top:8px}
.pf-chip{display:inline-block;padding:1px 7px;margin:0 3px 3px 0;border-radius:20px;
  background:var(--fondo);font-size:11px;font-variant-numeric:tabular-nums}

/* ══════════════════════════════════════════════════════════════════════════
   ⚠️ **CLASES RESCATADAS de plantillas Jinja borradas (2026-09-01).**

   Al migrar Compras y Siigo a React se borraron las plantillas que llevaban su
   CSS adentro, en un `<style>`. Las clases se siguieron usando desde el JSX,
   pero su CSS se fue con el archivo: **6 pantallas quedaron renderizando con
   estilos a medias o sin ninguno.** La peor fue la lista de cuentas de cobro,
   con 10 clases usadas y 0 definidas — el usuario lo reportó como «se ve super
   feo», y tenía razón: no era gusto, era que no había CSS.

   Lo cuida `test_toda_clase_usada_existe_en_el_css`, que compara los dos lados.
   ══════════════════════════════════════════════════════════════════════════ */

/* Detalle plegable. Lo usan Pagos, la bitácora de Siigo y el detalle de
   proforma. Venía del `styles.css` propio de Siigo. */
.collapse{border:1px solid var(--borde);border-radius:10px;margin-top:14px;
  overflow:hidden;background:var(--panel)}
.collapse > summary{list-style:none;cursor:pointer;padding:11px 15px;
  font-size:13px;font-weight:700;color:var(--texto);display:flex;
  align-items:center;gap:8px;user-select:none}
.collapse > summary::-webkit-details-marker{display:none}
.collapse > summary::before{content:"▸";color:var(--tenue);font-size:11px;
  transition:transform .3s var(--ease-spring)}
.collapse[open] > summary::before{transform:rotate(90deg)}
.collapse > summary:hover{background:var(--fondo)}
.collapse .cl-body{padding:4px 15px 15px;border-top:1px solid var(--borde)}

/* Un dato en negrita dentro de un bloque de texto (notas crédito de Siigo). */
.strong{font-weight:700;color:var(--texto)}

/* Campo de solo lectura: el dato que NO se edita porque ya salió impreso en el
   PDF que se le mandó al proveedor (formulario de cuenta de cobro). */
.field-ro{min-width:0}
.field-ro-label{font-size:11px;font-weight:600;color:var(--tenue);
  text-transform:uppercase;letter-spacing:.06em;margin-bottom:3px}
.field-ro-val{font-size:14px;font-weight:600;color:var(--texto)}

/* Campo dentro de un modal (el descarte del formulario de pedido). */
.mdl-input{width:100%;padding:6px 8px;border:1px solid var(--borde);
  border-radius:6px;font-family:inherit;font-size:13px;box-sizing:border-box}

/* La tabla de pagos dentro de la tarjeta de un proveedor, y el bloque derecho
   de su cabecera. */
.sc-tbl{width:100%;border-collapse:collapse;font-size:13px}
.sc-hdr-r{display:flex;align-items:center;gap:16px;flex-shrink:0}

/* ── Utilidades de celda ───────────────────────────────────────────────
   ⚠️ Van al FINAL del archivo a propósito. `.td-money` está definida DOS
   veces acá (una en la sección de tablas y otra dentro del bloque del
   tema bento, con `color:var(--vino)`), y entre dos reglas de la misma
   especificidad gana la última. Una utilidad que TIÑE una cifra tiene
   que venir después de las dos o no se ve — medido el 2026-09-01: el
   saldo por pagar salía lila, igual que el total. */
/* Utilidades de celda, compartidas por cualquier tabla del sistema.

   ⚠️ Nacieron el 2026-09-01 arreglando `/compras/cuentas`, que se dibujaba
   SIN CSS: sus 10 clases `.cc-*` vivían dentro de un `<style>` de
   `compras/cuentas.html` y se fueron con la plantilla al migrar a React.
   Están acá y son genéricas —no `.cc-algo`— justamente para que no vuelva a
   pasar: el CSS de una pantalla no debe vivir solo en esa pantalla.

   `.td-sub` es el renglón chico bajo el dato principal de una celda (la fecha
   de emisión bajo el número de cuenta). `.td-alerta` tiñe una cifra que pide
   acción —un saldo por pagar—; no es un error, por eso ámbar y no rojo. */
.td-sub{color:var(--tenue);font-size:11.5px;margin-top:1px;line-height:1.3}
.td-alerta{color:var(--alerta)}
/* Una cifra que ES una deuda: el saldo por cobrar de Cartera. Distinta de
   `.td-alerta` (ámbar, «revisar esto») a propósito — acá el rojo dice «esta
   plata no entró». Solo se pinta lo que de verdad se debe: un saldo en 0 o a
   favor va en `.td-money` normal, porque pintar todo de rojo es no pintar. */
.td-peligro{color:var(--peligro-texto,var(--peligro))}
/* Una fila que sigue siendo válida pero ya no pide nada (una cuenta pagada).
   Se apaga en vez de desaparecer: sigue siendo el respaldo de un pago. El
   hover la devuelve entera, porque se sigue pudiendo abrir su PDF. */
tbody tr.fila-apagada{opacity:.62}
tbody tr.fila-apagada:hover{opacity:1}
/* El ítem destructivo de un menú de 3 puntos. */
.dots-panel .item-menu.danger{color:var(--peligro-texto,var(--peligro))}
/* Un `Aviso` que enumera casos (los clientes con saldo a favor negativo, en
   Cartera). Va acá y no como clase propia: cualquier aviso que liste algo se
   ve igual, y una clase `.aviso-lista` sería un nombre para lo que el
   elemento ya dice. */
.aviso ul{margin:6px 0 0;padding-left:20px}
.aviso li + li{margin-top:2px}
.aviso a{color:inherit;font-weight:700}

/* ⚠️ Rescatada del `<style>` de `cartera/home.html` al migrar esa pantalla a
   React (2026-09-01), y **ya estaba rota antes de eso**: la usa también
   `nomina/organigrama.html` para avisar de un jefe inmediato en círculo, y esa
   página nunca cargó el `<style>` de Cartera — el aviso salía como texto
   plano, sin fondo ni color. El mismo accidente que dejó `/compras/cuentas`
   sin CSS ese mismo día, en otra pantalla y por el mismo motivo: CSS de una
   pantalla escrito dentro de esa pantalla.

   El nombre queda como estaba porque renombrarlo obliga a tocar Nómina, que
   sigue en Jinja. Cuando Nómina migre, esto se reemplaza por `ui/Aviso.jsx` y
   estas tres reglas se borran. */
.cartera-alerta{border-radius:10px;padding:12px 16px;font-size:13.5px;
  font-weight:600;margin-bottom:14px}
.cartera-alerta.danger{background:var(--t-durazno,#FCE9EC);
  color:var(--peligro-texto,var(--peligro))}
.cartera-alerta.warn{background:var(--t-ambar,#FFF4E5);color:var(--alerta)}


/* ══════════════════════════════════════════════════════════════════════════
   CARTERA — la pantalla del cliente (panel de cobro).

   ⚠️ Rescatado del `<style>` de `cartera/cliente.html` al migrar esa pantalla
   a React (2026-09-01). Son ~30 clases: si se hubieran ido con la plantilla,
   la pantalla que registra los pagos de los clientes habría quedado sin CSS,
   que es exactamente lo que le pasó a `/compras/cuentas` ese mismo día y
   estuvo doce días en producción sin que nada avisara.

   Se copian TAL CUAL, sin retocar nada: el layout de esta pantalla lo eligió
   el usuario entre dos opciones el 2026-08-12 y afinarlo «de paso» sería
   cambiarlo sin que nadie lo haya pedido.
   ══════════════════════════════════════════════════════════════════════════ */
.vista-btn.activo{background:var(--vino);color:#fff;border-color:var(--vino)}
.vistas{display:flex;gap:8px;flex-wrap:wrap;margin:18px 0 14px;
  border-bottom:1px solid var(--borde);padding-bottom:12px}
.filtros-mes{display:flex;gap:10px;align-items:center;margin-bottom:10px;flex-wrap:wrap}
.filtros-mes label{font-size:11px;font-weight:800;letter-spacing:.04em;
  text-transform:uppercase;color:var(--tenue)}
.filtros-mes select{font-family:inherit;font-size:13px;padding:5px 9px;min-width:200px}
.filtros-mes .filtro-conteo{font-size:12.5px;color:var(--tenue)}

/* ⚠️⚠️ **De `#id` a `.clase` el 2026-09-03, y no es cosmético: las 24 reglas
   de acá abajo estaban MUERTAS.** La pantalla se migró a React el 2026-09-01 y
   `<Tabla>` no pone `id` —solo `className`—, así que todo lo que colgaba de
   `#tabla-facturas` y `#tabla-historial` dejó de aplicar sin que nada avisara:
   las filas compactas que el usuario pidió el 2026-08-12 (de 6 facturas
   visibles a 14), los `nowrap` que impiden que «FV-3-3079» y «$ 1.027,65» se
   partan en dos renglones, el resaltado de la fila marcada, la opacidad de la
   otra moneda y el ocultar la columna N. Crédito fuera de la vista «Todas».

   Se descubrió arreglando el ancho del panel de cobro: al angostar la tabla,
   el texto empezó a partirse **porque el `nowrap` no existía**. Es el mismo
   accidente que el CSS de arriba dice que le pasó a `/compras/cuentas` y
   estuvo doce días en producción — y el mismo del `select` de `.ajuste-row`.
   `ver-todas` sí sobrevivió, porque va por `className`. */
/* La columna de N. Crédito solo existe en la vista «Todas»: en «Pendientes» se
   cobra, y un botón por fila competía con el badge de estado. */
.tabla-facturas:not(.ver-todas) .col-nc{display:none}

.cartera-banner{margin-top:14px;padding:14px 18px;border-radius:10px;
  display:flex;align-items:center;gap:14px;flex-wrap:wrap}
.cartera-banner .ico{font-size:24px;line-height:1}
.cartera-banner .k{font-size:11px;text-transform:uppercase;letter-spacing:.04em;opacity:.85}
.cartera-banner .v{font-size:19px;font-weight:700}
.cartera-banner .nota{font-size:12.5px;margin-top:2px;opacity:.95}
.cartera-banner .cta{margin-left:auto;font-size:12px;opacity:.9}
.cartera-banner.favor{cursor:pointer;background:linear-gradient(135deg,var(--vino),#6e1421);color:#fff}
.cartera-banner.negativo{background:#FFF4E5;border:1px solid var(--alerta);color:var(--alerta)}

/* ⚠️ 430 px, no 330 (2026-09-03, elección del usuario entre tres maquetas).
   Con 330 el panel tenía 298 px útiles y una fila de ajuste necesita 397: la
   fila se salía, le comía el margen interno a TODO el panel y el `overflow`
   de abajo **cortaba el botón «×»** de quitar el ajuste. Y la categoría más
   larga del catálogo —«Comisión/valor de transferencia», 237 px— no entraba
   en el desplegable. La tabla de facturas cede 100 px: usaba 980 y no
   desbordaba, así que le sobraban. */
.cobro-grid{display:grid;grid-template-columns:minmax(0,1fr) 430px;gap:16px;align-items:start}
/* El corte sube de 1100 a 1250: con el panel en 430, a 1100 px la tabla se
   quedaba con 654 y ahí sí se apretaba. */
@media (max-width:1250px){.cobro-grid{grid-template-columns:minmax(0,1fr)}}

/* `.panel` de style.css es `overflow:hidden`, así que una tabla más ancha que el
   panel queda cortada SIN scroll: en un pago de 14 facturas se perdían 853 px
   —Monto, Método, Eliminar— sin forma de llegar a ellos. */
.tabla-scroll{overflow-x:auto;max-width:100%}

/* Filas compactas (pedido del usuario 2026-08-12): se veían solo 6 facturas sin
   scroll. La fila medía 82 px y el culpable NO era el padding sino el botón de
   N. Crédito: con el texto largo se partía en dos líneas y medía 59 px de alto
   él solo. Texto corto + `nowrap` + padding propio = 6 filas -> 14. */
.tabla-facturas thead th{padding:7px 10px}
.tabla-facturas tbody td{padding:5px 10px;font-size:13px}
/* Discreto por defecto y con forma de botón al apuntarlo: había uno con borde
   vino en CADA fila, compitiendo con el badge VENCIDA y ensuciando el escaneo de
   la lista. Sigue visible siempre (no solo al hover) porque si aparece recién al
   pasar el mouse, con teclado o en táctil no se descubre. */
.tabla-facturas .btn-nc{white-space:nowrap;padding:3px 9px;font-size:11.5px;
  background:transparent;border-color:transparent;color:var(--tenue);
  transition:color .12s,background .12s,border-color .12s}
.tabla-facturas tr:hover .btn-nc{color:var(--vino)}
.tabla-facturas .btn-nc:hover{background:var(--vino);border-color:var(--vino);color:#fff}
.tabla-facturas .btn-nc:focus-visible{border-color:var(--vino);color:var(--vino)}
.tabla-facturas .badge-status{font-size:10px}
/* Vínculo entre una factura anulada y la que la reemplaza (2026-08-19). Va bajo
   el número, en gris y chico: es contexto, no el dato principal de la fila. */
/* ⚠️ Tenía `font-family:var(--fuente)`, un token que no existe: la declaración
   no hacía nada y el texto ya hereda la tipografía de su tabla, que es la que
   corresponde. Se quitó el 2026-09-16 en vez de inventarle un valor. */
.rel-anulada{display:block;font-size:10.5px;
  color:var(--tenue);font-weight:400;margin-top:1px;letter-spacing:0}
.tabla-facturas .chk-factura{width:15px;height:15px}
.tabla-facturas .col-fecha{white-space:nowrap}
.tabla-facturas .sub-celda{display:inline}
.tabla-historial .col-fecha{white-space:nowrap}
.tabla-historial .col-facturas{max-width:340px}
.tabla-historial .mas-facturas{font-family:inherit;font-size:11px;font-weight:700;
  color:var(--vino);background:var(--vino-tinte);border:1px solid var(--vino);
  border-radius:20px;padding:1px 8px;margin-left:6px;cursor:pointer;white-space:nowrap}
.tabla-historial .mas-facturas:hover{background:var(--vino);color:#fff}
.tabla-facturas .mono,.tabla-historial .mono{font-family:ui-monospace,Consolas,monospace;
  font-weight:600;white-space:nowrap}
.tabla-facturas .num,.tabla-historial .num{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
.tabla-facturas .sub-celda,.tabla-historial .sub-celda{font-size:11px;color:var(--tenue);font-weight:400}
.tabla-facturas .vacio,.tabla-historial .vacio{color:var(--tenue)}
.tabla-facturas .chk-factura{width:16px;height:16px;margin:0;accent-color:var(--vino);cursor:pointer}
.tabla-facturas tr.fila-cobrable{cursor:pointer}
.tabla-facturas tr.marcada td{background:var(--vino-tinte)}
.tabla-facturas tr.otra-moneda{opacity:.4}
.text-ok{color:var(--ok)}
.text-peligro{color:var(--peligro-texto,var(--peligro))}

.cobro-panel{background:var(--panel);border:1px solid var(--vino);border-radius:10px;
  overflow:hidden;position:sticky;top:14px}
.cobro-panel .cab{background:var(--vino);color:#fff;padding:11px 15px;font-size:12px;
  font-weight:800;letter-spacing:.05em;text-transform:uppercase}
/* `min-width:0`: sin esto un hijo más ancho que la columna estira el grid
   entero y el padding del panel se pierde de ese lado. */
.cobro-panel .cuerpo{padding:15px;display:grid;gap:12px;min-width:0}
.cobro-panel .cuerpo>*{min-width:0}
.cobro-panel .campo label{display:block;font-size:11px;font-weight:800;letter-spacing:.04em;
  text-transform:uppercase;color:var(--tenue);margin-bottom:4px}
.cobro-panel .campo input,.cobro-panel .campo select,.cobro-panel .campo .desp{width:100%}
.cobro-panel .ayuda-campo{font-size:11.5px;color:var(--tenue);margin-top:4px}
.cobro-panel .grupo-efectivo{display:grid;gap:12px}
.cobro-panel .mas-campos summary{font-size:12px;font-weight:700;color:var(--tenue);
  cursor:pointer;padding:4px 0}
.cobro-panel .mas-campos[open]{display:grid;gap:12px}
/* ⚠️⚠️ **El `flex:1` apuntaba a un `<select>` que ya no existe.** Este bloque
   se copió tal cual del `<style>` de `cartera/cliente.html`, donde la
   categoría del ajuste era un `<select>` nativo. Al migrar la pantalla a React
   (2026-09-01) pasó a ser el desplegable propio del sistema —un `div.desp`—,
   así que la regla dejó de aplicarle a nada: el control se quedó con su ancho
   fijo, no se encogió más, y la fila desbordó el panel. Apunta al `.desp`, y
   el `min-width:0` es lo que le permite achicarse por debajo de su contenido
   (un ítem de flex no lo hace solo). */
/* ⚠️ El monto bajó de 110 a 98 px el 2026-09-24. A 1280 de ancho —el que trae
   Chrome de fábrica y el que usan los e2e— «Comisión/valor de transferencia»
   necesitaba 188 px y el desplegable le dejaba 187: la última letra salía
   cortada. Los 12 px salen del monto, que con `type=number` y montos de cartera
   no los usaba. Lo cuida `cartera.spec.js::la categoría del ajuste se lee
   entera`, que mide el texto con la misma tipografía en vez de confiar en
   `scrollWidth`. */
.cobro-panel .ajuste-row{display:flex;gap:8px;align-items:center}
.cobro-panel .ajuste-row .desp{flex:1;min-width:0}
.cobro-panel .ajuste-row input{width:98px;flex:none}

.reparto{display:grid;gap:3px;font-size:12px;max-height:230px;overflow-y:auto}
.reparto .ln{display:grid;grid-template-columns:1fr auto;gap:10px;align-items:baseline;
  padding:6px 9px;border-radius:6px;background:var(--fondo)}
.reparto .ln .f{font-family:ui-monospace,Consolas,monospace;font-weight:700;font-size:11.5px}
.reparto .ln .m{font-variant-numeric:tabular-nums;font-weight:700}
.reparto .ln .et{display:block;font-size:9.5px;font-weight:800;letter-spacing:.05em;
  text-transform:uppercase;opacity:.85}
.reparto .ln.completa{background:#E8F4EC;color:var(--ok)}
.reparto .ln.parcial{background:#FFF4E5;color:var(--alerta)}
.cobro-tot{border-top:1px solid var(--borde);padding-top:10px;margin-top:10px;
  display:grid;gap:4px;font-size:12.5px}
.cobro-tot .r{display:flex;justify-content:space-between;gap:12px}
.cobro-tot .r .v{font-variant-numeric:tabular-nums;font-weight:700}
.cobro-vacio{color:var(--tenue);font-size:12.5px;text-align:center;padding:18px 8px}
.cobro-aviso{border-radius:7px;padding:9px 11px;font-size:12px;font-weight:650;margin-top:10px}
.cobro-aviso.ok{background:#E8F4EC;color:var(--ok)}
.cobro-aviso.av{background:#FFF4E5;color:var(--alerta)}
.cobro-aviso.mal{background:#FCE9EC;color:var(--peligro-texto,var(--peligro))}

.tabla-historial .expandir{padding:2px 7px;line-height:1.2}
.detalle-pago{background:var(--fondo);border:1px solid var(--borde);border-radius:8px;
  padding:12px 14px;margin:4px 0 10px}
.detalle-cab{font-size:10.5px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;
  color:var(--tenue);margin-bottom:8px}
.detalle-tabla{width:100%;border-collapse:collapse;font-size:12.5px}
.detalle-tabla th{font-size:10px;text-transform:uppercase;letter-spacing:.05em;
  color:var(--tenue);text-align:left;padding:5px 8px;border-bottom:1px solid var(--borde)}
.detalle-tabla td{padding:5px 8px;border-bottom:1px solid var(--borde)}
.detalle-tabla tr:last-child td{border-bottom:0}
.detalle-pie{display:flex;gap:16px;flex-wrap:wrap;margin-top:9px;padding-top:9px;
  border-top:1px dashed var(--borde);font-size:11.5px;color:var(--tenue)}
.detalle-totales{margin-top:10px;padding-top:10px;border-top:1px solid var(--borde);
  display:grid;gap:3px;max-width:340px;font-size:12.5px}
.detalle-totales .fila-tot{display:flex;justify-content:space-between;gap:14px}
.detalle-totales .fila-tot .v{font-variant-numeric:tabular-nums;font-weight:600}
.detalle-totales .fila-tot.costo{color:var(--alerta)}
.detalle-totales .fila-tot.total{margin-top:4px;padding-top:5px;
  border-top:1px solid var(--borde);font-weight:800}

.form-edicion{margin-top:12px;padding-top:12px;border-top:2px solid var(--vino)}
.form-edicion .edicion-facturas{display:grid;gap:2px;margin-bottom:10px;
  max-height:170px;overflow-y:auto;border:1px solid var(--borde);
  border-radius:7px;padding:5px;background:var(--panel)}
.form-edicion .edicion-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  gap:10px;margin-bottom:10px}
.form-edicion .edicion-grid .campo.full{grid-column:1/-1}
.form-edicion .campo label{display:block;font-size:11px;font-weight:800;
  letter-spacing:.04em;text-transform:uppercase;color:var(--tenue);margin-bottom:4px}
.form-edicion .campo input,.form-edicion .campo select,.form-edicion .campo .desp{width:100%}
.form-edicion .edicion-costos{margin-bottom:10px;max-width:340px}
/* Mismo caso que `.cobro-panel .ajuste-row`: el `select` se fue con la
   migración a React y el `flex:1` quedó apuntando al vacío. */
.form-edicion .ajuste-row{display:flex;gap:8px;margin-bottom:5px;align-items:center}
.form-edicion .ajuste-row .desp{flex:1;min-width:0}
.form-edicion .ajuste-row input{width:98px;flex:none}
.form-edicion .edicion-acciones{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.form-edicion .edicion-nota{font-size:11.5px;color:var(--tenue)}
.form-edicion .edicion-aviso:not(:empty){margin-bottom:10px;padding:9px 11px;
  border-radius:7px;font-size:12px;font-weight:650;
  background:#FFF4E5;color:var(--alerta)}


/* ⚠️ Estas seis se usaban en `cartera/cliente.html` y **nunca tuvieron CSS**:
   `.ajustes-lista`, `.fila-chk`, `.fac`, `.meta`, `.mto` y `tr.fila-detalle`
   estaban escritas en el HTML sin una sola regla que las alcanzara. Lo
   encontró `test_toda_clase_usada_existe_en_el_css` al migrar la pantalla a
   React (2026-09-01); antes de ese guardián nada podía verlo. La lista de
   ajustes se apilaba sin separación y las tres columnas de cada fila de
   edición quedaban pegadas una a la otra.
   `.edicion-costos-tit` es nueva: reemplaza un `style=` de seis propiedades
   escrito dentro de la plantilla. */
.cobro-panel .ajustes-lista{display:flex;flex-direction:column;gap:8px}
.cobro-panel .ajustes-lista:empty{display:none}
.form-edicion .fila-chk{display:grid;grid-template-columns:auto 1fr auto auto;
  align-items:center;gap:8px;padding:5px 7px;border-radius:7px;font-size:12.5px}
.form-edicion .fila-chk:hover{background:var(--fondo)}
.form-edicion .fila-chk .fac{font-weight:700;font-variant-numeric:tabular-nums}
.form-edicion .fila-chk .meta{color:var(--tenue);font-size:11.5px}
.form-edicion .fila-chk .mto{font-variant-numeric:tabular-nums;
  color:var(--tenue);min-width:70px;text-align:right}
.edicion-costos-tit{font-size:11px;font-weight:800;letter-spacing:.04em;
  text-transform:uppercase;color:var(--tenue);margin-bottom:5px}
/* La fila que se despliega bajo un pago del historial. No lleva el hover de
   fila normal: es contenido, no una fila más que se pueda elegir. */
tr.fila-detalle > td{background:var(--fondo);padding:0 14px 14px}
tr.fila-detalle:hover > td{background:var(--fondo)}

/* ── Pantalla sin barra lateral (Despachos) ───────────────────────
   `body` cede 58 px al sidebar fijo; cuando el shell no lo dibuja, esos
   58 px quedarían como un margen izquierdo sin nada del otro lado.
   `:has()` lo resuelve sin tocar `react_base.html`, que es compartido por
   los módulos que SÍ llevan barra. (El mismo recurso que en `.sg-layout`.)

   ⚠️ El `max-width:1600px` de `.content` también se anula: la grilla de
   Despachos tiene 9 columnas y el ancho es justamente lo que se busca. */
body:has(.content.sin-barra){padding-left:0}
.content.sin-barra{max-width:none;padding:0}
/* ══════════════════════════════════════════════════════════════════════════
   Despachos — la planilla en vivo
   ══════════════════════════════════════════════════════════════════════════
   Movido desde el `<style>` de `despachos/templates/despachos.html` el
   2026-09-02, al migrar la pantalla a React. Es el mismo CSS, con tres
   cambios:

   - Se fue `body{display:block;padding-left:0}`: la cáscara React lo resuelve
     con `.content.sin-barra` (más arriba en este archivo), que además no pisa
     el layout de las demás pantallas.
   - Se fueron `.desp-menu*`: el desplegable de Etiquetas ahora es el
     componente `Menu` del sistema, el mismo que usan Compras y Cartera.
   - `font-family:'Manrope'` pasó a `inherit`, 8 veces. La pantalla vive dentro
     de `.tema-bento`, donde la tipografía es Figtree; dejar Manrope escrita a
     mano la habría hecho la única pantalla migrada con dos tipografías.

   ⚠️ **La pestaña activa cambia de vino a lila**, como en los otros 9 módulos
   migrados: `--vino` se redefine dentro de `.tema-bento`. No es una decisión
   nueva de esta pantalla, es la del tema.
   ══════════════════════════════════════════════════════════════════════════ */
.desp-topbar{display:flex;align-items:center;justify-content:space-between;gap:14px;
  background:#fff;border-bottom:1px solid var(--borde);padding:8px clamp(12px,3vw,24px);
  position:sticky;top:0;z-index:5}
/* ⚠️ `white-space:nowrap` NO es cosmético: sin él «Volver a Inicio» se partía en
   tres líneas en un celular y la barra pasaba de 55px a 93px de alto, o sea que
   `--desp-barra` mentía y la grilla se salía de la ventana por abajo. El texto
   entero se ve igual; lo que sobra se alcanza deslizando el grupo. */
.desp-topbar .back{display:flex;align-items:center;gap:7px;color:var(--tenue);text-decoration:none;
  font-size:13px;padding:6px 12px;border-radius:8px;border:1px solid var(--borde);transition:.12s;
  background:#fff;font-family:inherit;cursor:pointer;white-space:nowrap;flex:none}
.desp-topbar .back:hover{background:var(--fondo);color:var(--texto)}
.desp-estado{font-size:12px;color:var(--tenue)}
/* ⚠️ **El grupo de acciones se desliza; NO envuelve.** Sus siete controles miden
   712px y en un celular de 400 sacaban la PÁGINA entera de lado (448px de
   desborde, medido el 2026-09-16). Envolverlos en dos filas parecía lo obvio y
   es la trampa: la barra crecería de alto y `--desp-barra` —el único número del
   que cuelga el alto de la grilla— dejaría de ser cierto, que es justo el
   desborde vertical que se cerró el 2026-09-07. Deslizarse de lado mantiene la
   barra en una línea y es lo que ya hacen las pestañas de los días.
   La barra de scroll va oculta a propósito: si ocupara alto, volveríamos al
   mismo problema. Lo que insinúa que hay más es el botón cortado en el borde. */
.desp-acciones{display:flex;align-items:center;gap:10px;min-width:0;
  overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}
.desp-acciones::-webkit-scrollbar{display:none}
.desp-sesion{display:flex;align-items:center;gap:12px;flex:none}
/* La foto del día cuando solo informa, en la barra (2026-10-05). La alarma
   (falta la foto de un día anterior) sigue siendo la banda ancha de abajo. */
.desp-foto-pildora{display:inline-flex;align-items:center;gap:8px;margin:0;height:30px;
  padding:0 4px 0 12px;border:1px solid var(--borde);border-radius:999px;
  background:var(--fondo);font-size:12.5px;color:var(--tenue);white-space:nowrap}
.desp-foto-pildora-btn{border:1px solid var(--borde);background:var(--panel);color:var(--vino);
  font-family:inherit;font-weight:700;font-size:12px;border-radius:999px;padding:3px 10px;
  cursor:pointer}
.desp-foto-pildora-btn:hover{border-color:var(--vino)}
.desp-foto-pildora-btn:focus-visible{outline:2px solid var(--vino);outline-offset:2px}
@media (max-width:1400px){.desp-foto-pildora{padding-left:4px}.desp-foto-pildora-txt{display:none}}
/* ⚠️⚠️ **El alto de la grilla se REPARTE, no se resta a mano.**
   Hasta el 2026-09-07 había TRES `calc(100vh - N)` escritos a mano sobre
   `.desp-grid` —140px pelado, 192px con la banda de la foto y 110px en pantalla
   completa—, o sea que cualquier cambio en lo que rodea la grilla (una fila más
   de pestañas, un botón más alto, la banda) la sacaba de la ventana y había que
   recalcular los tres. Ya pasó: el desborde de ~113px del 2026-08-19 lo destapó
   un cambio ajeno.
   Ahora `.desp-wrap` ocupa el alto de la ventana menos la barra superior, y de
   ahí para abajo cada nivel es `flex:1; min-height:0` hasta la grilla. La banda
   de la foto y las pestañas se descuentan **solas**, porque son hermanas de la
   grilla dentro del mismo flex.
   📌 Queda UN número, `--desp-barra`, y es el alto de la barra que está justo
   encima —lo único que separa al wrap de la ventana—. Medido en el navegador el
   2026-09-07: 55px en 1366, 1500 y 1920 de ancho. Lo cuida
   `scripts/verificar_despachos_pantalla.py` con umbral 0.
   ⚠️ `min-height:0` en cada nivel NO es decorativo: sin él un hijo flex se
   niega a encogerse por debajo de su contenido y la grilla vuelve a desbordar. */
:root{--desp-barra:55px}
.desp-wrap{width:100%;padding:10px clamp(12px,2.5vw,20px) 16px;box-sizing:border-box;
  height:calc(100dvh - var(--desp-barra));display:flex;flex-direction:column}
.desp-tabla-area{display:flex;flex-direction:column;flex:1;min-height:0}
.desp-tabs{display:flex;align-items:center;gap:8px;flex-wrap:nowrap;overflow-x:auto;
  margin-bottom:8px;padding-bottom:2px}
.desp-tabs::-webkit-scrollbar{height:4px}
.desp-tab-btn,.desp-control{height:30px;box-sizing:border-box;padding:0 13px;
  border-radius:8px;border:1px solid var(--borde);background:#fff;font-weight:600;
  font-size:13px;cursor:pointer;color:var(--tenue);font-family:inherit;
  white-space:nowrap;flex:none;transition:.12s}
.desp-tab-btn.activo{background:var(--vino);color:#fff;border-color:var(--vino)}
.desp-control:hover{background:var(--fondo);color:var(--texto)}
.desp-separador{width:1px;height:20px;background:var(--borde);flex:none;margin:0 2px}
/* Punto en la pestaña de un día que ya terminó y no quedó registrado en
   `despachos_historial`. Va en la propia pestaña y no como control nuevo:
   esa barra ya lleva 9 controles y es un pendiente abierto del módulo. */
.desp-tab-btn .desp-punto{display:inline-block;width:6px;height:6px;border-radius:50%;
  background:var(--vino);margin-left:6px;vertical-align:middle}
.desp-tab-btn.activo .desp-punto{background:#fff}
/* Banda de estado de la foto del día. Bordes en vez de sombras y el vino
   como único acento, igual que el resto del sistema (DESIGN.md). */
.desp-foto{display:flex;align-items:center;gap:10px;margin-bottom:8px;padding:7px 12px;
  border:1px solid var(--borde);border-radius:8px;font-size:12.5px;color:var(--tenue)}
.desp-foto.falta{border-color:var(--vino);color:var(--vino);background:#fdf5f6}
.desp-foto form{margin:0 0 0 auto}
.desp-foto .desp-foto-btn{height:28px;padding:0 12px;border-radius:8px;cursor:pointer;
  border:1px solid var(--borde);background:#fff;color:var(--tenue);font-weight:600;
  font-size:12px;font-family:inherit;white-space:nowrap;transition:.12s}
.desp-foto .desp-foto-btn:hover{background:var(--fondo);color:var(--texto)}
.desp-foto.falta .desp-foto-btn{border-color:var(--vino);background:var(--vino);color:#fff}
.desp-panel{display:none}
.desp-panel.activo{display:flex;flex-direction:column;flex:1;min-height:0}
.desp-grid{flex:1;min-height:0;width:100%;margin-bottom:0;font-size:12px}
/* ⚠️ Acá había un segundo `calc(100vh - 192px)` para descontarle a la grilla
   los 52px de la banda de la foto. Se fue el 2026-09-07 con el reparto por
   flex: la banda es hermana de la grilla, así que su alto ya se descuenta solo
   —esté o no esté—. El comentario que estaba acá además contradecía su propio
   cierre: hablaba de un desborde «pendiente aparte» que se había cerrado el
   2026-08-19. */
.desp-grid .ag-header-cell-text{font-size:12px}
.celda-bloqueada{background-color:#ffe3b3 !important}
/* ⚠️ Acá había 4 reglas colgando de `.ag-theme-alpine` — el hover de fila y el
   aspecto de la fila fija del resumen— y se borraron el 2026-09-07 porque
   **ningún archivo pone esa clase**: el div lleva solo `.desp-grid`, y
   `grilla.js` no importa ningún CSS de AG Grid a propósito (lo explica ahí).
   Encima `.ag-floating-bottom` es el nombre viejo: en AG Grid 36 la fila fija
   es `.ag-grid-pinned-bottom-rows`. O sea que el aspecto del resumen sale del
   tema por defecto de AG Grid y estas reglas no pintaban nada. Medido el
   2026-09-06, borrado tras comprobarlo otra vez el 09-07. */
.finca-dot{display:inline-block;width:7px;height:7px;border-radius:50%;
  margin-right:6px;vertical-align:middle;flex:none}
.desp-buscador{height:30px;box-sizing:border-box;padding:0 12px;border:1px solid var(--borde);
  border-radius:8px;font-family:inherit;font-size:13px;min-width:180px;width:180px;
  color:var(--texto);flex:none}
/* El `:focus` propio que tenía se fue: `input:focus` (global) ya pone el
   mismo borde, y el guardián de estilo exige que haya UNO solo — dos
   selectores de foco son dos comportamientos que empiezan a divergir. */
.desp-filtro-fincas{display:flex;align-items:center;gap:12px;font-size:12.5px;
  color:var(--tenue);white-space:nowrap;flex:none}
.desp-filtro-fincas label{display:flex;align-items:center;gap:5px;cursor:pointer}
.desp-filtro-fincas input[type=checkbox]{cursor:pointer}
select.desp-control{width:120px;min-width:0;max-width:120px;font-weight:600;
  color:var(--tenue);text-overflow:ellipsis;overflow:hidden;white-space:nowrap}

/* Casilla de selección de fila: sin el espacio en blanco que dejaba el
   ancho por defecto de AG Grid alrededor del check. */
.ag-theme-alpine .ag-cell.ag-selection-checkbox-cell{justify-content:center;padding:0}

/* Editor de celda con buscador (reemplaza el <select> simple de AG Grid
   para los campos de catálogo: cliente/variedad/finca/proveedor). */
.buscador-editor{background:#fff;border:1px solid var(--borde);border-radius:8px;
  box-shadow:0 6px 20px rgba(0,0,0,.12);min-width:240px;max-width:360px;
  font-family:inherit;overflow:hidden}
.buscador-editor-input{width:100%;box-sizing:border-box;padding:9px 12px;border:none;
  border-bottom:1px solid var(--borde);font-family:inherit;font-size:13.5px;outline:none}
/* Sin esto, el buscador de la celda no muestra nada al recibir el foco: se
   abre enfocado, pero quien llega con Tab no sabe dónde está. */
.buscador-editor-input:focus-visible{border-bottom-color:var(--vino);
  box-shadow:inset 0 -1px 0 var(--vino)}
.buscador-editor-lista{max-height:220px;overflow-y:auto}
.buscador-editor-item{padding:8px 12px;font-size:13.5px;cursor:pointer;white-space:nowrap}
.buscador-editor-item.resaltado{background:var(--vino-tinte);color:var(--vino)}
.buscador-editor-vacio{padding:10px 12px;font-size:13px;color:var(--tenue)}

/* Presencia: iniciales de quienes están conectados ahora mismo. */
.desp-presencia{display:flex;align-items:center}
.desp-avatar{width:28px;height:28px;border-radius:50%;display:flex;align-items:center;
  justify-content:center;color:#fff;font-size:12px;font-weight:700;
  font-family:inherit;border:2px solid #fff;margin-left:-8px;
  box-shadow:0 0 0 1px rgba(0,0,0,.06)}
.desp-avatar:first-child{margin-left:0}
.desp-toast.mostrar{opacity:1;transform:translateY(0)}

/* Pantalla completa: solo el buscador + la grilla del día activo. */
/* Pantalla completa: el mismo reparto por flex, sin descontar la barra —que
   ahí no se ve—. Antes esto tenía el TERCER `calc(100vh - N)` a mano. */
.desp-tabla-area:fullscreen{background:#fff;padding:24px;box-sizing:border-box;
  height:100dvh}
.modal-facturas-overlay.mostrar{display:flex;animation:modalFondo .18s ease}
.modal-facturas-overlay.mostrar .modal-facturas{animation:modalPop .32s var(--ease-spring)}
@keyframes modalFondo{from{opacity:0}to{opacity:1}}
@keyframes modalPop{from{opacity:0;transform:translateY(10px) scale(.96)}
  to{opacity:1;transform:translateY(0) scale(1)}}
.modal-facturas{background:#fff;border-radius:16px;width:100%;max-width:420px;
  max-height:82vh;overflow:hidden;display:flex;flex-direction:column;
  box-shadow:0 24px 60px rgba(0,0,0,.28);font-family:inherit}
.modal-facturas-acciones{display:flex;align-items:center;gap:8px;margin-bottom:12px}
.modal-facturas-contador{margin-left:auto;font-size:12px;font-weight:700;
  color:var(--vino);background:var(--vino-tinte);padding:4px 10px;border-radius:20px;
  white-space:nowrap}
/* ⚠️ El tope en `min()` y no en 280 px fijos (2026-09-16): el modal puede medir
   hasta 85vh y con 280 se veían siete clientes con media ventana vacía debajo,
   más una segunda barra de scroll dentro de la del modal. El `min()` deja que
   crezca con el monitor pero sin comerse el alto que necesitan la cabecera del
   modal y sus botones, que es lo que pasaría sin tope. */
.modal-facturas-lista{display:flex;flex-direction:column;gap:4px;
  max-height:min(58vh,560px);overflow-y:auto}
.modal-facturas-lista label{display:flex;align-items:center;gap:10px;font-size:13.5px;
  padding:8px 10px;border-radius:10px;cursor:pointer;border:1px solid transparent;
  transition:.12s;color:var(--texto)}
.modal-facturas-lista label:hover{background:var(--fondo)}
.modal-facturas-lista label:has(input:checked){background:var(--vino-tinte);
  border-color:var(--vino)}
.modal-facturas-lista input[type=checkbox]{width:16px;height:16px;accent-color:var(--vino);
  cursor:pointer;flex:none}
.modal-facturas-avatar{display:flex;align-items:center;justify-content:center;
  width:26px;height:26px;border-radius:50%;background:var(--fondo);color:var(--tenue);
  font-size:11px;font-weight:800;flex:none;transition:.12s}
.modal-facturas-lista label:has(input:checked) .modal-facturas-avatar{
  background:var(--vino);color:#fff}
.modal-facturas-nombre{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.transporte-errores{margin:0 0 14px 18px;padding:0;font-size:13px;line-height:1.6}
/* Colores de los avatares de presencia (Despachos).
   Diez colores fuertes y bien separados entre sí: no son colores de marca ni
   dicen nada del negocio — su único trabajo es que dos personas conectadas a
   la vez no se confundan de un vistazo. Por eso NO salen de los tokens del
   tema (que son vino/lila y grises) y viven acá como una paleta aparte.
   ⚠️ El color de cada persona se elige con un hash de su nombre, así que le
   toca siempre el mismo. Cambiar el ORDEN de esta lista le cambia el color a
   todo el mundo. */
.desp-avatar.desp-c0{background:#e63946}
.desp-avatar.desp-c1{background:#f4a261}
.desp-avatar.desp-c2{background:#2a9d8f}
.desp-avatar.desp-c3{background:#457b9d}
.desp-avatar.desp-c4{background:#9b5de5}
.desp-avatar.desp-c5{background:#f15bb5}
.desp-avatar.desp-c6{background:#00b4d8}
.desp-avatar.desp-c7{background:#fb8500}
.desp-avatar.desp-c8{background:#588157}
.desp-avatar.desp-c9{background:#e76f51}

/* Nombre destacado dentro de una tabla (el enlace principal de la fila).
   Genérica a propósito: es lo que la lista de Empleados necesitaba, y las
   otras tablas del sistema tenían el mismo `font-weight:600` escrito a mano
   en un `style=` dentro de la plantilla. */
.td-fuerte{font-weight:600}
/* El nombre de una persona o de un registro que abre su ficha, dentro de una
   tabla (estilo compacto de Nómina, 2026-10-05). En Empleados y en Novedades
   salía como enlace azul subrayado del navegador: no había ninguna regla para
   un `<a>` en una celda. Se lee como el nombre en negrita de las otras listas
   y avisa que es enlace al pasar el mouse. */
.enlace-nombre{color:var(--texto);font-weight:700;text-decoration:none}
.enlace-nombre:hover{color:var(--vino);text-decoration:underline}
.enlace-nombre:focus-visible{outline:2px solid var(--vino);outline-offset:2px;border-radius:3px}

/* ── Nómina ───────────────────────────────────────────────────────────────
   Solo lo que la portada necesita y no existía. El resto de la pantalla usa
   las clases del sistema (`.panel`, `.kpi`, `.badge-status`, `.tabla-*`). */

/* El aviso de cumpleaños. Va arriba de todo porque es de las pocas cosas del
   sistema que piden hacer algo HOY.

   ⚠️ **La pantalla en Jinja lo marcaba con `border-left:4px`, y eso NO se
   copió**: `DESIGN.md` prohíbe las franjas de acento de más de 1 px, y el
   guardián `test_ninguna_franja_de_acento_de_mas_de_1px` lo cazó al moverlo
   (la plantilla lo tenía en un `<style>` propio, donde nunca lo vio nadie).
   Se resuelve como el proyecto ya resolvió el caso equivalente en Logística:
   **tinte de fondo suave**, con un color que ya está en la paleta. */
.nom-cumple{background:var(--t-durazno);border:1px solid transparent;
  margin-bottom:20px;padding:16px 20px;font-size:15px}
.nom-cumple div + div{margin-top:6px}
.nom-cumple-nombre{font-weight:700;color:var(--vino)}

/* Los KPI que además son enlaces a su lista ya filtrada. */
/* ⚠️ `.nom-kpi-enlace` y `.nom-kpi-alerta` se fueron el 2026-09-16: el enlace y
   el semáforo los trae `KpiEnlace`, que ahora es del sistema y no de Nómina. */
a.kpi{text-decoration:none}

/* La pantalla vacía: solo se ve cuando no hay NINGÚN empleado cargado, o sea
   una vez en la vida del sistema. */
.nom-vacio{text-align:center;padding:36px}
/* El perfil del empleado en estilo compacto (2026-10-05): las pestañas como
   chips y los datos en cuatro columnas, con la letra de las tablas. */
.nom-perfil-tabs{margin:2px 0 12px}
.grid.nom-campos{grid-template-columns:repeat(4,1fr);row-gap:12px}
.nom-campos .field>div{font-size:14px}
@media (max-width:1100px){.grid.nom-campos{grid-template-columns:repeat(2,1fr)}}
.nom-vacio-titulo{font-size:15px;font-weight:600;margin-bottom:6px}
.nom-vacio-acciones{display:flex;gap:8px;justify-content:center;margin-top:16px}

/* Los filtros de las pantallas de novedades. La barra en Jinja era un `<form>`
   GET; en React los filtros viven en la URL y se aplican al cambiarlos, así
   que el `<form>` desapareció pero el aspecto es el mismo. */
.nom-filtros{margin-bottom:16px}
/* Los botones de aprobar/rechazar van juntos en la misma celda. */
.nom-acciones{display:flex;gap:6px}

/* ── Nómina · calendario, organigrama, catálogos e importación ────────────
   Movido desde los `<style>` de `calendario.html` y `organigrama.html` al
   migrarlas (2026-09-02). Mismo CSS, con un cambio: la franja de 3 px de la
   tarjeta del organigrama pasó a 1 px — `DESIGN.md` prohíbe los acentos más
   gruesos y el guardián de estilo lo caza (igual que pasó con el aviso de
   cumpleaños). */

/* El calendario del mes. */
table.cal{width:100%;border-collapse:collapse;table-layout:fixed}
table.cal th{font-size:11px;color:var(--tenue);text-transform:uppercase;
  padding:6px;text-align:left;font-weight:700}
/* ⚠️ 76 px y no 96 (2026-09-16): seis semanas de calendario eran ~580 px de
   grilla para unos pocos eventos, y el mes no entraba en la ventana. Con 76
   siguen cabiendo dos eventos por día sin recortar, que es el caso normal. */
td.cal-day{border:1px solid var(--borde);vertical-align:top;height:86px;
  padding:5px;border-radius:8px}
td.cal-empty{border:none}
td.cal-today{border:1.5px solid var(--vino);background:var(--vino-tinte,#F5E9EA)}
.cal-num{font-weight:700;font-size:13px;margin-bottom:3px}
.cal-evt{font-size:10.5px;line-height:1.25;margin-bottom:2px;padding:1px 5px;
  border-radius:5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Un color por tipo de evento: el calendario se lee de un vistazo, sin leyenda. */
.cal-cumple{background:#FFF4E5}
.cal-vacaciones{background:#E7F6EC}
.cal-incapacidad{background:#FCE9EC}
.cal-permiso{background:#EEF0F3}
.cal-vence{background:#FDF3D6}
.cal-evento{background:var(--vino-tinte,#F5E9EA);color:var(--vino);font-weight:600}
.nom-cal-panel{padding:12px}
/* El alta de Configuración en la franja (2026-10-05): campos de 34 px como los
   filtros de las otras barras compactas. */
.nom-cat-nuevo{display:flex;align-items:center;gap:8px;margin:0}
.tema-bento .nom-cat-nuevo input:not([type=hidden]){height:34px;width:220px}
.tema-bento .nom-cat-nuevo .desp-btn{height:34px}
.nom-cal-nav{display:flex;gap:8px;align-items:center}
.nom-cal-mes{min-width:150px;text-align:center;text-transform:capitalize}
.nom-cal-evento{margin-bottom:14px}
.nom-cal-form{display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap;
  margin-top:10px;padding:14px;border:1px solid var(--borde);border-radius:10px}
.nom-cal-ancho{flex:1;min-width:200px}

/* El organigrama: un árbol con sangría y una línea que marca la rama. */
.org-root,.org-root ul{list-style:none;margin:0;padding:0}
/* ⚠️ 1 px y no 2: `DESIGN.md` prohíbe las franjas más gruesas y el
   guardián lo caza. Es la línea que marca la rama del árbol, y a 1 px se
   lee igual. */
.org-root ul{margin-left:22px;padding-left:16px;border-left:1px solid var(--borde)}
.org-root li{margin:8px 0;position:relative}
.org-card{display:inline-block;text-decoration:none;border:1px solid var(--borde);
  border-left:1px solid var(--vino);border-radius:8px;padding:8px 14px;
  background:var(--panel);min-width:210px;transition:border-color .12s}
.org-card:hover{border-color:var(--vino)}
.org-nombre{font-weight:700;font-size:13.5px;color:var(--texto)}
.org-cargo{font-size:12px;color:var(--vino)}
.org-meta{font-size:11px;color:var(--tenue)}
/* Jefatura en círculo: ámbar, el mismo «mirá esto» del resto del sistema. El
   vino queda reservado al acento de marca. */
.org-card-ciclo{border-left-color:var(--alerta,#B8860B)}
.org-aviso{font-size:11px;color:var(--alerta,#B8860B);font-weight:600;margin-top:4px}
.nom-org-scroll{overflow-x:auto}
/* Estilo compacto (2026-10-05): las raíces en grilla, de a cinco o seis por
   fila, cada una con su equipo debajo. La tarjeta ocupa el ancho de su celda y
   pierde el aire de arriba: con 45 personas la página medía 3.226 px. */
.org-compacto .org-root{display:grid;gap:8px;align-items:start;
  grid-template-columns:repeat(auto-fill,minmax(210px,1fr))}
.org-compacto .org-root>li{margin:0}
.org-compacto .org-root>li>.org-card{display:block}

/* Catálogos: renombrar es un campo por fila, con su ✓ al lado. */
.nom-cat-rename{display:flex;gap:8px;align-items:center;margin:0}
.nom-cat-rename input{min-width:180px}
/* ⚠️ El campo con ancho propio: sin él se estira y empuja al botón a una
   segunda línea, así que los cuatro bloques de la pantalla mostraban
   «Nombre…» arriba y «Agregar» debajo, escalonados contra el borde derecho
   (2026-09-16). */
.nom-cat-nuevo{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin:0}
.nom-cat-nuevo input[name=nombre]{width:200px;min-width:0}

/* Importación. */
.nom-imp-titulo{margin-top:0}
.nom-imp-texto{margin-bottom:10px}
.nom-imp-archivo{max-width:420px}
.nom-imp-inicial{display:flex;gap:9px;align-items:flex-start;cursor:pointer;
  font-weight:400;margin-top:14px;max-width:640px}
.nom-imp-acts{margin-top:16px}
.nom-imp-confirmar{margin-top:16px}
.nom-imp-nota{margin-top:8px}

/* ── Nómina · perfil del empleado y su formulario ─────────────────────────
   Movido de `empleado_perfil.html` y `empleado_form.html` al migrarlas
   (2026-09-02), que eran las dos últimas plantillas del módulo. */
.nom-perfil-cabeza{display:flex;gap:16px;align-items:center}
.nom-perfil-foto{width:64px;height:64px;border-radius:50%;object-fit:cover;
  border:1px solid var(--borde);font-size:24px}
.nom-perfil-nombre{margin-bottom:2px}
.nom-perfil-estado{margin-left:8px}
.nom-perfil-acciones{margin-top:18px;display:flex;gap:10px;flex-wrap:wrap}

/* Un formulario que se abre con su botón. En Jinja era un `<details>`: evita
   que una pestaña arranque con nueve campos vacíos ocupando la pantalla. */
.nom-desplegar{margin:10px 0 14px}
.nom-desplegar-caja{margin-top:12px;padding:14px;border:1px solid var(--borde);
  border-radius:10px}
.nom-form-linea{display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap;margin:0}
.nom-form-ancho{flex:1;min-width:220px}
.nom-form-titulo{margin-top:0}
.nom-form-acts{margin-top:16px}
.nom-form-check{display:flex;gap:8px;align-items:center;cursor:pointer;
  font-weight:400}
/* Los certificados que genera el sistema, en una línea aparte del expediente. */
.nom-generables{display:flex;gap:8px;align-items:center;flex-wrap:wrap;
  margin:14px 0}

/* ⚠️ `.panel` NO tiene relleno, y es a propósito: `Tabla` lo usa así para que
   la tabla llegue al borde de la caja. Las pantallas que ponen texto suelto
   adentro —el perfil del empleado y su formulario— necesitan el suyo, o la
   primera línea queda pegada al borde superior y se ve cortada (medido en el
   navegador el 2026-09-02: el panel y su primer campo empezaban los dos en
   y=226). */
.nom-panel{padding:16px 18px}
.nom-panel > h3:first-child{margin-top:0}

/* ── Despachos · la suma rápida al arrastrar ──────────────────────────────
   Pedido del usuario (2026-09-03): arrastrar sobre celdas de números y ver la
   suma «en color llamativo», como en Excel. Va arriba, fija y a la vista: el
   sentido de la función es no tener que sacar la cuenta a mano, así que si
   hubiera que buscar el número no serviría de nada. */
/* ⚠️⚠️ **FLOTANTE, y no es una decisión estética.** La primera versión iba en
   el flujo normal, y al aparecer EMPUJABA la grilla hacia abajo: el puntero
   quedaba por encima de las filas y el arrastre se rompía a mitad de camino.
   Trazado en el navegador el 2026-09-03: la fila detectada iba 0 → 1 y después
   volvía a 0 y se quedaba ahí, con el mouse bajando. O sea que la barra que
   muestra la suma impedía hacer la suma.
   `position:fixed` la saca del flujo: aparece y desaparece sin mover un píxel
   de la tabla. */
/* ⚠️ **Abajo a la derecha, no arriba.** Se pidió «en la parte superior», y
   así se hizo primero — pero medido en el navegador tapaba el buscador y el
   botón de Transporte, que están justo ahí. Abajo a la derecha no se cruza con
   ningún control, es donde Excel muestra su suma, y queda igual de a la vista
   porque el fondo de color la separa de todo. */
.desp-suma{position:fixed;bottom:18px;right:24px;
  z-index:60;display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;
  padding:8px 16px;border-radius:10px;
  background:var(--vino);color:#fff;box-shadow:0 6px 20px rgba(0,0,0,.18);
  /* No se puede seleccionar ni recibir el mouse: está encima de la pantalla
     mientras se arrastra, y no debe interceptar nada. */
  pointer-events:none;user-select:none}
.desp-suma-valor{font-size:19px;font-weight:800;font-variant-numeric:tabular-nums}
.desp-suma-etiqueta{font-size:13px;font-weight:600;opacity:.95}
.desp-suma-detalle{font-size:12px;opacity:.8;margin-left:auto}

/* Las celdas dentro del rango que se está sumando. El fondo es el tinte del
   acento —no el acento pleno— para que el número siga leyéndose. */
.celda-sumando{background:var(--vino-tinte,#EFEAFE) !important;
  box-shadow:inset 0 0 0 1px var(--vino)}


/* ── Ventana «Generar estado de cuenta» (Cartera, 2026-09-07) ──────────────
   Reemplaza a la pantalla del módulo `estados_cuenta`, eliminado ese día. Sin
   un solo color a mano: todo por tokens, que es lo que exige el guardián de
   estilo (`test_ningun_color_escrito_a_mano`). */
.ec-modal .field label{font-size:11px;font-weight:700;letter-spacing:.06em;
  text-transform:uppercase;color:var(--tenue)}
.ec-modal input[type=date]{font:inherit;padding:7px 9px;border:1px solid var(--borde);
  border-radius:8px;background:var(--panel);color:var(--texto)}
/* El total que dice qué va a decir el documento. Va destacado porque es el dato
   que evita mandarle al cliente un PDF sin haberlo mirado. */
.ec-cuenta{margin-top:16px;padding:12px 14px;border-radius:9px;
  background:var(--vino-tinte);display:flex;align-items:baseline;
  justify-content:space-between;gap:12px;flex-wrap:wrap}
.ec-total{font-weight:800;font-size:18px;color:var(--vino);
  font-variant-numeric:tabular-nums}
.ec-error{color:var(--peligro-texto,var(--peligro));font-weight:600;font-size:13px}


/* ── «Ver necesidad»: la necesidad de un día (Movimiento ICA) ─────────────── */
/* El modal se parte en dos: el calendario del mes a la izquierda y el
   resultado del día elegido a la derecha. Se apila en pantallas angostas. */
.nec-cuerpo{display:grid;grid-template-columns:250px 1fr;gap:18px;
  align-items:start}
.nec-cal{border-right:1px solid var(--borde);padding-right:16px}
.nec-res{min-width:0}
@media (max-width:760px){
  .nec-cuerpo{grid-template-columns:1fr}
  .nec-cal{border-right:0;border-bottom:1px solid var(--borde);
    padding-right:0;padding-bottom:14px}
}

.nec-cal-nav{display:flex;align-items:center;gap:6px;margin-bottom:10px}
.nec-cal-mes{flex:1;text-align:center;font-size:13.5px;text-transform:capitalize}
.nec-cal-dias,.nec-cal-grid{display:grid;
  grid-template-columns:repeat(7,minmax(0,1fr));gap:3px}
.nec-cal-dias span{text-align:center;font-size:10.5px;font-weight:700;
  color:var(--tenue)}
/* ⚠️ Altura fija, NO `aspect-ratio`: con `aspect-ratio` el ancho mínimo de la
   celda se deriva de la altura de la fila y la cuadrícula desborda su columna
   —se ven 3 de las 7 columnas—. Visto al maquetar esto el 2026-09-09. */
.nec-dia,.nec-dia-hueco{height:32px;min-width:0}
.nec-dia{position:relative;display:flex;align-items:center;justify-content:center;
  border:1px solid transparent;background:transparent;border-radius:9px;
  font:inherit;font-size:13px;font-weight:600;color:var(--texto);cursor:pointer;
  font-variant-numeric:tabular-nums;padding:0}
.nec-dia:hover:not(:disabled){background:var(--vino-tinte)}
.nec-dia:disabled{color:var(--borde);cursor:default}
.nec-dia-flojo{font-weight:400;color:var(--tenue)}
.nec-dia-hoy{border-color:var(--linea-control,var(--tenue))}
.nec-dia-elegido{background:var(--vino);color:#fff;font-weight:800}
.nec-dia-elegido.nec-dia-flojo{color:#fff}
.nec-dia-punto{display:inline-block;width:5px;height:5px;border-radius:50%;
  background:var(--vino)}
.nec-dia .nec-dia-punto{position:absolute;bottom:4px;left:50%;
  transform:translateX(-50%)}
.nec-dia-elegido .nec-dia-punto{background:#fff}
.nec-cal-pie{margin-top:12px;padding-top:10px;border-top:1px solid var(--borde);
  font-size:11.5px;color:var(--tenue);display:flex;align-items:center;gap:6px}

.nec-tit{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;
  margin-bottom:14px}
.nec-tit strong{font-size:15px}
.nec-tit span{font-size:12.5px;color:var(--tenue)}
.nec-grupos{display:grid;grid-template-columns:repeat(3,1fr);gap:9px}
@media (max-width:560px){.nec-grupos{grid-template-columns:1fr}}
/* El fondo lo pone el JSX con el color del grupo, que manda la API: es código
   fijo de negocio y tiene una sola fuente de verdad (`informes.color_grupo`). */
.nec-grupo{border-radius:11px;padding:11px 12px;display:flex;
  flex-direction:column;gap:8px}
.nec-grupo-falta{font-size:23px;font-weight:800;line-height:1.05;
  font-variant-numeric:tabular-nums}
.nec-grupo-falta small{display:block;font-size:11px;font-weight:700;
  color:var(--tenue);margin-top:3px}
.nec-grupo-ok{font-size:15px;font-weight:800;color:var(--ok)}
.nec-grupo-vacio{font-size:13px;color:var(--tenue)}
.nec-grupo-detalle{display:flex;flex-direction:column;gap:2px;font-size:11.5px;
  color:var(--tenue);font-variant-numeric:tabular-nums}
.nec-grupo-detalle strong{color:var(--texto)}

.nec-subtit{margin:18px 0 8px;font-size:11px;font-weight:800;letter-spacing:.08em;
  text-transform:uppercase;color:var(--tenue)}
.nec-tabla{border:1px solid var(--borde);border-radius:11px;overflow:hidden}
.nec-fila-sin-finca td{color:var(--tenue)}
.nec-aviso{margin-top:12px;border-radius:10px;padding:10px 12px;font-size:12.5px;
  background:var(--t-durazno,var(--fondo));color:var(--texto)}
.nec-vacio{padding:38px 10px;text-align:center;color:var(--tenue);font-size:13.5px}
.nec-vacio strong{display:block;color:var(--texto);font-size:14.5px;
  margin-bottom:4px}


/* ── Perfil de cliente (Ajustes → Clientes) ──────────────────────────────── */
/* Dos columnas: la ficha del cliente siempre a la vista a la izquierda, y a la
   derecha lo que cambia (precios, actividad, facturas). Se apila en angosto. */
.perf-cuerpo{display:grid;grid-template-columns:290px 1fr;gap:20px;
  align-items:start}
@media (max-width:900px){.perf-cuerpo{grid-template-columns:1fr}}
.perf-col{display:flex;flex-direction:column;gap:18px;min-width:0}

.perf-ficha{background:var(--panel);border:1px solid var(--borde);
  border-radius:var(--r-tile,12px);padding:15px 16px}
.perf-ficha-cab{display:flex;align-items:center;gap:8px;font-size:11.5px;
  font-weight:700;color:var(--tenue);margin-bottom:12px}
.perf-ficha-cab span{flex:1}
.perf-grupo+.perf-grupo{margin-top:14px;padding-top:12px;
  border-top:1px solid var(--borde)}
.perf-grupo-nom{font-size:11px;font-weight:800;letter-spacing:.06em;
  text-transform:uppercase;color:var(--vino);margin-bottom:8px}
.perf-campo+.perf-campo{margin-top:9px}
.perf-campo-r{font-size:10.5px;font-weight:700;letter-spacing:.03em;
  text-transform:uppercase;color:var(--tenue)}
.perf-campo-v{font-size:13.5px;font-weight:600;word-break:break-word}
.perf-campo-v.vacio{color:var(--tenue);font-weight:400;font-style:italic}

/* Las cifras del cliente en una línea (estilo compacto, 2026-10-05): eran
   cinco tarjetas de 74 px. Usan la `<Cifra>` de la barra de trabajo, así que
   se leen como las de cualquier otra pantalla compacta. */
.perf-cifras{display:flex;flex-wrap:wrap;align-items:baseline;gap:8px 22px;
  padding:4px 2px 10px;border-bottom:1px solid var(--borde)}
.perf-cifras .barra-nota a{color:var(--vino);font-weight:700}
/* Los campos vacíos de un grupo, en un renglón (2026-10-05). */
.perf-vacios{font-size:12px;color:var(--tenue);font-style:italic;margin-top:6px}

/* La variedad, con su badge de grupo y el aviso de fuera de catálogo. */
.perf-var{display:inline-flex;align-items:center;gap:7px;flex-wrap:wrap}
.perf-sinprecio{color:var(--tenue);font-style:italic}
/* Las variedades facturadas sin precio van en la misma tabla, resaltadas: son
   el trabajo pendiente sobre esta lista, no otra lista. */
.perf-fila-sin td{background:var(--t-durazno,var(--fondo))}

/* El precio se escribe y se corrige en la propia fila (2026-09-16). Va en la
   celda del valor, así que el campo tiene que ser angosto y alineado a la
   derecha como el número que reemplaza: una fila de precios se lee comparando
   la columna hacia abajo. */
.perf-precio-fila{display:inline-flex;align-items:center;gap:7px;
  justify-content:flex-end}
.perf-precio-fila input[name="valor_tallo"]{width:92px;text-align:right}

/* La fila de alta, pegada al pie de la tabla de precios. */
.perf-alta{display:flex;align-items:center;gap:9px;flex-wrap:wrap;
  margin-top:10px;padding:11px 13px;border:1px dashed var(--linea-control);
  border-radius:var(--r-tile,12px);background:var(--fondo)}
.perf-alta>*:first-child{flex:1;min-width:200px}
.perf-alta input[name="valor_tallo"]{width:110px}

.perf-barra-caja{display:inline-block;vertical-align:middle;width:100px;
  height:7px;border-radius:4px;background:var(--fondo);overflow:hidden}
.perf-barra{display:block;height:100%;border-radius:4px;background:var(--vino);
  min-width:3px}

/* ══════════════════════════════════════════════════════════════════════════
   LA FACTURA COMO HOJA — `modules/logistica/FacturaForm.jsx`
   ──────────────────────────────────────────────────────────────────────────
   Decisión del usuario del 2026-09-14, elegida sobre otras dos maquetas
   (pestañas, e índice lateral). La pantalla de crear/editar/clonar una factura
   deja de ser un mosaico de seis tiles apilados y pasa a leerse como el Invoice
   que sale impreso: una hoja centrada, el número arriba a la derecha, los datos
   en cuatro bloques de dos columnas, la tabla de ítems y los totales al pie.

   El motivo del usuario fue «quiero mucho más orden»: los tres tiles de campos
   pedían bajar tres veces para ver trece datos, y el total y el botón de guardar
   vivían al final de la página.

   ⚠️ El campo NO es una caja: es un valor con una línea abajo. La caja aparece
   al enfocarlo, y el foco sigue siendo el GLOBAL del tema (`outline` en el
   acento) — acá no se redefine, justamente para no volver a tener tres
   tratamientos de foco distintos como en agosto.
   ══════════════════════════════════════════════════════════════════════════ */
.tema-bento .fh-fs{border:none;padding:0;margin:0;min-width:0}
.tema-bento .fh-hoja{max-width:1120px;margin:0 auto 14px;background:var(--panel);
  border:1px solid var(--borde);border-radius:var(--r-tile);padding:18px 28px 20px}

/* El encabezado en UNA línea (2026-10-02, el rediseño de densidad llegado a la
   hoja): el título a la izquierda y el estado con el número a la derecha, sobre
   la misma base. Antes eran dos renglones a cada lado, con un subtítulo que se
   leía una vez. */
.tema-bento .fh-enc{display:flex;align-items:baseline;gap:16px;flex-wrap:wrap;
  border-bottom:2px solid var(--texto);padding-bottom:8px;margin-bottom:12px}
.tema-bento .fh-enc-t{font-size:16px;font-weight:800;letter-spacing:-.02em;margin:0}
.tema-bento .fh-enc-n{margin-left:auto;display:flex;align-items:baseline;gap:10px}
.tema-bento .fh-n-l{font-size:11px;font-weight:700;letter-spacing:.06em;
  text-transform:uppercase;color:var(--tenue)}
.tema-bento .fh-n-v{font-size:20px;font-weight:800;letter-spacing:-.02em;
  line-height:1.1;font-variant-numeric:tabular-nums}
.tema-bento .fh-sellos{display:flex;gap:7px;flex-wrap:wrap;margin-bottom:14px}

/* ⚠️ Los cuatro bloques se apoyan cada uno en su propia FICHA (decisión del
   usuario del 2026-09-15, elegida sobre otras dos pieles). Lo único que se
   agregó es el fondo: **nada se movió de lugar**, era la consigna. El fondo es
   lo que agrupa, así que de un vistazo se ve que son cuatro cosas distintas sin
   leer los títulos. */
.tema-bento .fh-bloques{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:12px}
.tema-bento .fh-bl{min-width:0;background:var(--fondo);
  border-radius:var(--r-control);padding:12px 14px 10px}
.tema-bento .fh-bl-t{font-size:11px;font-weight:800;letter-spacing:.08em;
  text-transform:uppercase;color:var(--tenue);margin:0 0 6px}

/* El renglón: rótulo a la izquierda, valor a la derecha. */
.tema-bento .fh-par{display:grid;grid-template-columns:100px minmax(0,1fr);
  align-items:center;gap:10px;padding:2px 0}
.tema-bento .fh-k{font-size:12px;font-weight:700;color:var(--tenue)}
.tema-bento .fh-v{min-width:0}
.tema-bento .fh-v .combo{gap:0}
/* ⚠️ Esto NO sobra aunque `Desplegable` ya se ponga `width:100%` en línea:
   `.desp` es `display:inline-block`, y un inline-block deja el hueco del
   descendente debajo, que desalinea el control del renglón de al lado. */
.tema-bento .fh-v .desp{display:block;width:100%}
/* ⚠️ Tres clases de especificidad para ganarle a la regla general de controles
   del tema (`.tema-bento input`), que fija radio de 14px y alto de 38.
   El campo es BLANCO sobre la ficha gris, y de ahí sale la regla que hace legible
   la hoja sin leer nada: **lo blanco se escribe, lo que no tiene caja no**. El
   radio baja de 14 a 9 para que no se vea blando; los botones se quedan en 14,
   así que el radio también separa lo que se escribe de lo que se aprieta. */
.tema-bento .fh-v input,.tema-bento .fh-v .desp-btn{
  height:30px;padding:3px 9px;font-weight:600;background:var(--panel);
  border:1px solid var(--linea-control);border-radius:9px}
.tema-bento .fh-v .desp-btn:hover{background:var(--t-lila)}
/* ⚠️ La regla de arriba gana por especificidad (0,2,1) sobre
   `input.campo-invalido` (0,1,1) Y va después en el archivo, así que el campo
   fuera de catálogo perdía sus DOS señales visuales: el borde rojo y el fondo
   rosado. Quedaba solo el `.hint` de 11 px debajo, y al desenfocarlo el campo
   se veía idéntico a uno válido. Medido en pantalla el 2026-09-14. */
.tema-bento .fh-v input.campo-invalido{border-color:var(--peligro);
  background:var(--t-durazno)}
/* El desplegable del aeropuerto es el único control obligatorio de la hoja que
   no es un `<input>`, así que `input.campo-invalido` no lo alcanzaba y era el
   único campo que no podía pintarse cuando falta. */
.tema-bento .fh-v .desp-btn.campo-invalido{border-color:var(--peligro);
  background:var(--t-durazno)}
/* La lista de lo que falta, dentro del aviso de arriba de la hoja. */
.tema-bento .fh-faltan{margin:6px 0 0;padding-left:18px}
.tema-bento .fh-faltan li{margin-top:2px}

/* El dato que el sistema DEDUCE y nadie escribe (hoy, la aerolínea de la guía
   madre). Va como texto y no como campo: un campo que no se puede escribir es
   una promesa que no se cumple.
   ⚠️ **Sin caja, y eso es lo que lo dice.** Desde que el bloque es una ficha
   gris y el campo una caja blanca, la ausencia de caja ya significa «esto no se
   toca», sin necesidad de un borde punteado que compita con el de una factura
   bloqueada. Alinea con los campos de al lado por el padding, no por el borde. */
.tema-bento .fh-fijo{display:block;font-size:13px;font-weight:600;
  color:var(--tenue);padding:0 9px;line-height:28px;min-height:30px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* La tabla de ítems, adentro de la hoja: más apretada que la del mosaico, y el
   encabezado pegajoso sobre el blanco de la hoja y no sobre el gris de fondo. */
.tema-bento .fh-items{margin-top:6px}
.tema-bento .fh-items table td{padding:5px 7px}
.tema-bento .fh-items table th{padding:0 7px 6px}
/* ⚠️ El rótulo se alinea con el TEXTO de su burbuja, no con el borde de la
   caja. Medido en pantalla el 2026-09-14: el input mete 1 px de borde más 11 de
   padding, así que el valor arrancaba **12 px** a la derecha del rótulo y la
   columna se leía corrida en las siete que se escriben.
   Va por `text-indent` y no por `padding` a propósito: con columnas de 62 px,
   12 px de padding le parten el rótulo en dos líneas.
   Las de solo lectura (Tallos, Total) **no llevan burbuja**, así que su valor ya
   cae donde el rótulo — por eso la segunda regla las devuelve a cero.
   `Tabla` marca esas columnas con `style={{textAlign}}`, que es de dónde las
   agarra el selector (mismo patrón que la regla de cifra tabular del tema). */
.tema-bento .fh-items table th{text-indent:12px}
.tema-bento .fh-items table th[style*="right"]{text-indent:0}
.tema-bento .fh-items .tabla-scroll thead th{background:var(--panel)}
.tema-bento .fh-items input,.tema-bento .fh-items .desp-btn{height:29px;
  border-radius:9px}
/* ⚠️ La variedad es el dato que se busca al recorrer la tabla, y se medía
   **65 px** en las cuatro anchuras: sus vecinas declaran `width` en línea y
   `ComboEstricto` no, así que se comía el sobrante. Se veía «RUSCl» donde
   decía «RUSCUS TEST». La tabla ya tiene scroll propio, así que esto corre
   ese scroll y no toca el ancho de la hoja. Medido el 2026-09-14. */
.tema-bento .fh-items .combo{min-width:170px}
/* Y desde el 2026-10-02 se lleva TODO lo que sobra (pedido del usuario): las
   demás columnas quedan al ancho de su campo, que se recortó a lo que miden sus
   datos en producción. `width:100%` en una tabla de ancho automático le da a
   esa columna el resto y deja a las otras en su contenido. Sin esto, la tabla
   repartía el sobrante entre las diez y Variedad se quedaba en sus 170 px. */
.tema-bento .fh-items th.col-variedad{width:100%}
/* ⚠️ `.c_desc` —el «(−150)» de tallos descartados— existía SOLO como
   `table.items .c_desc`, y `ui/Tabla.jsx` dibuja la tabla SIN clase: el marcador
   salía en 14px/400 y del mismo color que el número de al lado, o sea «350
   (−150)» se leía como un solo dato. El pie de totales sí lo distingue (lleva el
   color en línea), así que la misma factura mostraba el descarte de dos formas.
   Medido en pantalla el 2026-09-14. Mismos valores que la regla original. */
.tema-bento .fh-items .c_desc{color:var(--vino);font-size:11px;font-weight:600}
.tema-bento .fh-items-pie{display:flex;align-items:center;gap:10px;
  flex-wrap:wrap;margin-top:9px}
.tema-bento .fh-items-pie .sub{margin-left:auto}

/* El pie del documento: notas a la izquierda, totales a la derecha. */
.tema-bento .fh-pie{display:flex;gap:26px;margin-top:16px;flex-wrap:wrap;
  align-items:flex-start}
.tema-bento .fh-notas{flex:1;min-width:250px}
.tema-bento .fh-notas .fh-k{display:block;margin-bottom:5px}
/* ⚠️ `rows={3}` no alcanza: `.tema-bento textarea` fija `height:38px` y gana.
   La caja de notas salía de UNA línea, y es donde se escribe la leyenda de
   aduana, que casi nunca entra en un renglón. Medido el 2026-09-14. */
.tema-bento .fh-notas textarea{width:100%;height:auto;min-height:74px;
  border-radius:9px}
.tema-bento .fh-notas .chips{margin-bottom:7px}
.tema-bento .fh-tots{width:250px;flex:none;background:var(--fondo);
  border-radius:var(--r-control);padding:12px 14px;align-self:flex-start}
.tema-bento .fh-tot{display:flex;justify-content:space-between;gap:12px;
  font-size:12.5px;padding:3px 0}
.tema-bento .fh-tot-k{color:var(--tenue)}
.tema-bento .fh-tot-v{font-weight:700;font-variant-numeric:tabular-nums}
/* Dentro de la ficha gris, una línea de 2px en tinta pesa de más: separa igual
   con el gris del contorno de control. */
.tema-bento .fh-gran{display:flex;justify-content:space-between;
  align-items:baseline;gap:12px;border-top:2px solid var(--linea-control);
  margin-top:7px;padding-top:8px}
.tema-bento .fh-gran .fh-tot-k{font-size:11px;font-weight:800;letter-spacing:.07em;
  text-transform:uppercase;color:var(--texto)}
.tema-bento .fh-gran .fh-tot-v{font-size:22px;font-weight:800;
  letter-spacing:-.02em;color:var(--vino)}

/* ⚠️ **Una factura ya facturada en Siigo va con `<fieldset disabled>`, y con el
   campo subrayado eso dejaba de notarse.** Antes el estado se leía solo porque
   el navegador grisá el FONDO de la caja; sin caja no hay nada que grisar, y el
   campo quedaba idéntico a uno editable. Medido el 2026-09-14 forzando el estado
   en pantalla: la única señal era el botón «+ Agregar ítem» apagado, y el aviso
   de arriba. La línea pasa a punteada y el valor a `--tenue` — se sigue leyendo,
   y se ve que no se toca. */
.tema-bento .fh-fs:disabled input,
.tema-bento .fh-fs:disabled textarea,
.tema-bento .fh-fs:disabled .desp-btn{color:var(--tenue);cursor:not-allowed}
.tema-bento .fh-fs:disabled .fh-v input,
.tema-bento .fh-fs:disabled .fh-v .desp-btn{border-style:dashed}
.tema-bento .fh-fs:disabled .fh-items input,
.tema-bento .fh-fs:disabled .fh-items .desp-btn,
.tema-bento .fh-fs:disabled textarea{border-style:dashed}

/* ⚠️ Acá vivía `.fh-docs`, la tira de los cinco papeles al pie de la página.
   Los botones subieron a la barra de acciones el 2026-09-14 a pedido del usuario
   y la clase se quedó sin nadie que la use. */
/* El corte entre los papeles y lo que MODIFICA la factura. Un pelo de línea
   alcanza: son dos familias de acción en la misma barra, no dos secciones. */
.tema-bento .fh-acts-sep{width:1px;align-self:stretch;margin:2px 3px;
  background:var(--borde)}

/* La barra de acciones: afuera de la hoja y PEGADA ABAJO, para que guardar no
   dependa de haber llegado al final de la página — que es el problema que esta
   pantalla vino a arreglar. */
.tema-bento .fh-acts{position:sticky;bottom:0;z-index:5;display:flex;
  align-items:center;gap:8px;flex-wrap:wrap;max-width:1120px;margin:0 auto;
  padding:10px 0;background:var(--fondo);border-top:1px solid var(--borde)}
.tema-bento .fh-acts-der{margin-left:auto;display:flex;align-items:center;gap:8px}

@media (max-width:820px){
  .tema-bento .fh-bloques{grid-template-columns:1fr}
  .tema-bento .fh-hoja{padding:18px 16px}
  .tema-bento .fh-par{grid-template-columns:88px minmax(0,1fr)}
  .tema-bento .fh-tots{width:100%}
}

/* ══════════════════════════════════════════════════════════════════════════
   MODO NOCHE — el color semántico (2026-09-17)

   🔴 **Por qué existe este bloque y no se tokenizó cada regla.** Los neutros y
   los tintes los manda el servidor y cambian solos con el tema; el problema son
   las ~57 reglas que pintan una superficie con un hex CLARO escrito a mano
   —badges de estado, avisos, calendario de Nómina, barras de Despachos, filas
   con tinte—. De noche seguían siendo pastel con el texto claro del tema
   encima: la tabla de `/facturas` medía **1,02:1**, o sea texto invisible.

   Reescribirlas para que leyeran tokens habría movido el modo de día, que el
   usuario no pidió cambiar y hoy está medido y bien. Colgando el color de noche
   de `data-tema` no se toca ni una regla existente: **sin el atributo, nada de
   este bloque existe**, y borrarlo entero devuelve el sistema a como estaba.

   ⚠️ El atributo lo pone el `<html>` de las cuatro plantillas desde
   `tema_actual` (context processor en `app/web/__init__.py`). Si una plantilla
   nueva se olvida de ponerlo, su pantalla queda en colores de día: lo caza
   `test_las_plantillas_llevan_data_tema`.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Superficies que estaban en blanco puro ─────────────────────────────── */
:root[data-tema="oscuro"] .sidebar,
:root[data-tema="oscuro"] .desp-topbar,
:root[data-tema="oscuro"] .desp-topbar .back,
:root[data-tema="oscuro"] .desp-tab-btn,
:root[data-tema="oscuro"] .desp-control,
:root[data-tema="oscuro"] .desp-foto .desp-foto-btn,
:root[data-tema="oscuro"] .buscador-editor,
:root[data-tema="oscuro"] .desp-tabla-area:fullscreen,
:root[data-tema="oscuro"] .modal-facturas,
:root[data-tema="oscuro"] .confirm-toast-barra,
:root[data-tema="oscuro"] input,
:root[data-tema="oscuro"] select,
:root[data-tema="oscuro"] textarea,
:root[data-tema="oscuro"] .filterbar select{background:var(--panel);color:var(--texto)}
/* El punto del tab activo y el de la fecha elegida son marcas sobre un relleno
   de marca: siguen al texto que va encima de ese relleno. */
:root[data-tema="oscuro"] .desp-tab-btn.activo .desp-punto,
:root[data-tema="oscuro"] .nec-dia-elegido .nec-dia-punto{background:var(--sobre-marca)}
/* 🔴 **Los estados ACTIVOS, con su fondo de marca repetido acá.** Las dos
   reglas de arriba son más específicas que la que le da el relleno de marca al
   estado activo (`.desp-tab-btn.activo`, dos clases), así que le ganaban el
   fondo y le dejaban el `--panel`: el día elegido en Despachos quedó con texto
   OSCURO sobre panel oscuro, **1,1:1**, peor que antes de tocarlo. Dos reglas
   mías peleando, no un color mal elegido.
   ⚠️ El fondo va junto al texto en la misma regla a propósito: así el par no
   depende de cuál de las dos gane. Lo cazó el verificador en la corrida previa
   al deploy — y por eso el deploy se para cuando alguno de los tres falla. */
:root[data-tema="oscuro"] .desp-tab-btn.activo{
  background:var(--vino);border-color:var(--vino);color:var(--sobre-marca)}
:root[data-tema="oscuro"] .desp-foto.falta .desp-foto-btn{
  background:var(--vino);color:var(--sobre-marca)}

/* ── Texto encima del relleno de marca ──────────────────────────────────── */
/* 🔴 Los 40 colores del panel fallan en noche con blanco encima (3,39-3,85:1) y
   los 40 pasan con texto oscuro (4,98-5,65). No es el caso raro de una empresa:
   es el modo noche entero. Ver `config.texto_sobre()`. */
/* ⚠️ `:not()` y no `.btn` a secas: la primera versión de esta regla pintaba de
   oscuro TODOS los botones, y `ghost`, `danger` y `gray` son transparentes o
   grises — «Abrir» pasó de 3,09:1 a **2:1**, peor que antes del arreglo. Solo
   llevan `--sobre-marca` los que tienen el relleno de marca debajo. */
:root[data-tema="oscuro"] .skip-link,
:root[data-tema="oscuro"] .btn:not(.ghost):not(.danger):not(.gray):not(.plano),
:root[data-tema="oscuro"] .btn.on,
:root[data-tema="oscuro"] .btn.active,
:root[data-tema="oscuro"] .proc-card.malo .badge-status.danger,
/* Las otras doce, encontradas de una sola vez barriendo el CSS en busca de
   `background: var(--vino…)` junto a `color:#fff` — de a una se habrían ido
   encontrando por meses, y cada una es un rótulo en 3,5:1. Lo cuida ahora
   `test_ninguna_regla_pone_blanco_sobre_la_marca_sin_cubrirla`. */
:root[data-tema="oscuro"] .dots-panel a.dots-descargar:hover,
:root[data-tema="oscuro"] .doctab.on,
:root[data-tema="oscuro"] .vista-btn.activo,
:root[data-tema="oscuro"] .cartera-banner.favor,
:root[data-tema="oscuro"] .tabla-facturas .btn-nc:hover,
:root[data-tema="oscuro"] .tabla-historial .mas-facturas:hover,
:root[data-tema="oscuro"] .cobro-panel .cab,
:root[data-tema="oscuro"] .desp-tab-btn.activo,
:root[data-tema="oscuro"] .desp-foto.falta .desp-foto-btn,
:root[data-tema="oscuro"] .modal-facturas-lista label:has(input:checked) .modal-facturas-avatar,
:root[data-tema="oscuro"] .desp-suma,
:root[data-tema="oscuro"] .nec-dia-elegido{color:var(--sobre-marca)}
:root[data-tema="oscuro"] .btn.ghost{color:var(--marca-acento);border-color:var(--marca-acento)}
:root[data-tema="oscuro"] .btn.ghost:hover{color:var(--texto)}
/* 📌 El botón plano —«Abrir», en cada fila de Facturas— no tiene relleno: es
   el acento sobre lo que haya debajo, y encima de una fila resaltada daba
   **4,46:1** contra los 4,5 que pide. Usa el tono de hover de la marca, un
   punto más claro: medido en las 40 paletas, ninguna baja del mínimo ni sobre
   la fila, ni sobre el panel, ni sobre el fondo. Bajar la fila en su lugar
   habría costado que dejara de notarse como fila resaltada. */
:root[data-tema="oscuro"] .btn.plano{color:var(--marca-h)}

/* ── Badges de estado y de grupo botánico ───────────────────────────────── */
/* ⚠️ El código de color por grupo es NORMA del ICA, no gusto: acá cambia la
   luminosidad, nunca el hue. Aves sigue ámbar, Ruscus verde e Hydrangea azul,
   que es lo que la norma pide distinguir. */
:root[data-tema="oscuro"] .badge-grupo.aves{background:var(--t-ambar);color:var(--a-ambar)}
:root[data-tema="oscuro"] .badge-grupo.ruscus{background:var(--t-menta);color:var(--a-menta)}
:root[data-tema="oscuro"] .badge-grupo.hydrangea{background:var(--t-cielo);color:var(--a-cielo)}
:root[data-tema="oscuro"] .badge-grupo.otro{background:var(--t-gris);color:var(--a-gris)}
/* ⚠️ El ESTADO ya no lleva fondo (2026-09-30): solo se le cambia el tono, que
   de noche tiene que ser el claro del par y no el oscuro. Sobre la fila
   (`#171A21`) el peor de los seis da **8,53:1**, casi el doble que cuando
   estaban sobre su tinte. */
:root[data-tema="oscuro"] .badge-status.warn{color:var(--a-ambar)}
:root[data-tema="oscuro"] .badge-status.ok{color:var(--a-menta)}
:root[data-tema="oscuro"] .badge-status.muted{color:var(--a-gris)}
:root[data-tema="oscuro"] .badge-status.entrega{color:var(--a-amarillo)}
:root[data-tema="oscuro"] .badge-status.danger{color:var(--a-durazno)}
:root[data-tema="oscuro"] .badge-status.info{color:var(--a-cielo)}

/* ── Avisos, alertas y franjas ──────────────────────────────────────────── */
:root[data-tema="oscuro"] .alerta-backup,
:root[data-tema="oscuro"] .alerta-admin,
:root[data-tema="oscuro"] .proc-card.malo,
:root[data-tema="oscuro"] .cartera-alerta.danger,
:root[data-tema="oscuro"] .cobro-aviso.mal{background:var(--t-durazno);color:var(--texto)}
:root[data-tema="oscuro"] .cartera-alerta.warn,
:root[data-tema="oscuro"] .cartera-banner.negativo,
:root[data-tema="oscuro"] .cobro-aviso.av,
:root[data-tema="oscuro"] .reparto .ln.parcial,
:root[data-tema="oscuro"] .form-edicion .edicion-aviso:not(:empty){background:var(--t-ambar);color:var(--texto)}
:root[data-tema="oscuro"] .reparto .ln.completa,
:root[data-tema="oscuro"] .cobro-aviso.ok{background:var(--t-menta);color:var(--texto)}
:root[data-tema="oscuro"] .btn.danger:hover{background:var(--t-durazno);color:var(--a-durazno)}
:root[data-tema="oscuro"] .btn.gray{background:var(--t-gris);color:var(--a-gris)}
:root[data-tema="oscuro"] input.campo-invalido{background:var(--t-rosa);color:var(--texto)}

/* ── Tablas: cabeceras, hover, totales ──────────────────────────────────── */
:root[data-tema="oscuro"] .pc-tbl thead th,
:root[data-tema="oscuro"] .pc-tbl tbody tr:hover td,
:root[data-tema="oscuro"] .pc-tbl tfoot td,
:root[data-tema="oscuro"] .pf-table thead th,
:root[data-tema="oscuro"] .pf-table tbody .pf-row:hover td,
:root[data-tema="oscuro"] .skel-fila{background:var(--t-gris-suave)}
/* Lo que entró el 2026-09-23 con la pasada de Siigo. El `tfoot` fijo y la
   barra de emisión son superficies NUEVAS: sin esto se quedan con el fondo
   claro cuando se apaga la luz, que es el 1,02:1 que ya pasó en `/facturas`. */
:root[data-tema="oscuro"] .aviso-version{background:var(--bn-panel);
  border-color:var(--bn-borde)}
:root[data-tema="oscuro"] .tema-bento .tabla-scroll tfoot td,
:root[data-tema="oscuro"] .tema-bento .tabla-scroll tfoot td.col-fija{background:var(--bn-panel)}
:root[data-tema="oscuro"] .pv-barra{background:var(--bn-panel);
  border-color:var(--bn-borde);box-shadow:0 -2px 12px rgba(0,0,0,.35)}
:root[data-tema="oscuro"] .kpis-seg .kseg-filtro:hover{background:var(--t-gris)}
/* Mismo motivo que la de arriba: `--fondo` de noche es casi el papel y el
   hover no se ve. La cifra-filtro de la barra de trabajo lo hereda. */
:root[data-tema="oscuro"] .barra-cifra-filtro:hover{background:var(--t-gris)}
/* 🔴 De noche la tarjeta abierta se queda **sin fondo propio**: con `--t-gris`
   detrás, su nombre en el acento de la empresa daba 2,61:1 —medido el
   2026-09-23— y AA pide 4,5. La barra de la izquierda ya la distingue sola, que
   es para lo que está; el fondo era el refuerzo, no la señal. */
:root[data-tema="oscuro"] .sg-item.abierta{background:none}
/* 🔴 Y el ámbar de «1 alerta(s) · 2 aviso(s)»: `--alerta` es #8A4F00, un marrón
   pensado para fondo CLARO. De noche daba **1,4:1**, o sea ilegible justo en la
   proforma que pide atención. Va su acento de noche, como el resto del sistema
   (`.badge-status.warn`). */
:root[data-tema="oscuro"] .sg-item-estado.t-warn{color:var(--a-ambar)}
:root[data-tema="oscuro"] .pc-pill.fito{background:var(--t-cielo);color:var(--a-cielo)}
:root[data-tema="oscuro"] .pc-pill.rem{background:var(--t-gris);color:var(--a-gris)}
/* 🔴 Las filas con tinte de `.tema-bento`: acá estaba el 1,02:1 de `/facturas`.
   El fondo pastel se quedaba y el texto pasaba a claro. */
:root[data-tema="oscuro"] .tema-bento tbody tr.cielo td{background:var(--t-fila-cielo)}
:root[data-tema="oscuro"] .tema-bento tbody tr.cielo:hover td{background:var(--t-fila-cielo-h)}
:root[data-tema="oscuro"] .tema-bento tbody tr.lila:hover td{background:var(--marca-tinte)}
:root[data-tema="oscuro"] .tema-bento .desp-btn:hover{background:var(--t-gris)}
:root[data-tema="oscuro"] .celda-sumando{background:var(--t-cielo)}
:root[data-tema="oscuro"] .celda-bloqueada{background:var(--t-ambar)}

/* ── Calendario de Nómina ───────────────────────────────────────────────── */
:root[data-tema="oscuro"] td.cal-today,
:root[data-tema="oscuro"] .cal-evento{background:var(--t-rosa);color:var(--texto)}
:root[data-tema="oscuro"] .cal-cumple{background:var(--t-ambar);color:var(--texto)}
:root[data-tema="oscuro"] .cal-vacaciones{background:var(--t-menta);color:var(--texto)}
:root[data-tema="oscuro"] .cal-incapacidad{background:var(--t-durazno);color:var(--texto)}
:root[data-tema="oscuro"] .cal-permiso{background:var(--t-gris);color:var(--texto)}
:root[data-tema="oscuro"] .cal-vence{background:var(--t-amarillo);color:var(--texto)}
:root[data-tema="oscuro"] .desp-foto.falta{background:var(--t-rosa)}

/* ── La grilla de Despachos (AG Grid) ───────────────────────────────────── */
/* 🔴 AG Grid v33 se pinta sola: el proyecto **no importa ninguna hoja suya** a
   propósito (ver `frontend/src/modules/despachos/grilla.js`), así que de noche
   la grilla entera se quedaba **blanca** — 2,6:1 en los números de cajas, que
   es la pantalla donde más se escribe. Su tema por defecto sí lee las variables
   `--ag-*`, y por eso el arreglo son variables y no reglas: no se pelea con el
   CSS de la librería, se le dice en su propio idioma. */
:root[data-tema="oscuro"] .ag-root-wrapper,
:root[data-tema="oscuro"] .ag-theme-alpine{
  --ag-background-color:var(--panel);
  --ag-foreground-color:var(--texto);
  /* Igual que con el encabezado: `foregroundColor` (v32) pasó a `textColor`
     (v33). Con el viejo solo, el fondo se oscureció y el texto de las celdas se
     quedó en el casi negro del tema claro: 1,02:1. */
  --ag-text-color:var(--texto);
  --ag-cell-text-color:var(--texto);
  --ag-header-background-color:var(--t-gris-suave);
  /* ⚠️ Los DOS nombres: `headerForegroundColor` es el de la v32 y
     `headerTextColor` el de la v33, que es la que está instalada. Con solo el
     viejo, el encabezado se quedó con su color derivado del tema claro —texto
     casi negro sobre el fondo oscuro, **1,09:1**— mientras el cuerpo de la
     grilla ya estaba bien. */
  --ag-header-foreground-color:var(--texto);
  --ag-header-text-color:var(--texto);
  --ag-border-color:var(--borde);
  --ag-row-border-color:var(--borde);
  --ag-odd-row-background-color:var(--panel);
  --ag-row-hover-color:var(--t-gris-suave);
  --ag-selected-row-background-color:var(--t-fila-cielo);
  --ag-control-panel-background-color:var(--panel);
  --ag-input-disabled-background-color:var(--t-gris-suave);
  --ag-secondary-foreground-color:var(--tenue);
  --ag-data-color:var(--texto);
  --ag-modal-overlay-background-color:rgba(13,15,20,.72);
  background:var(--panel);color:var(--texto)}
/* Red de seguridad: el nombre del parámetro es de la librería y puede volver a
   cambiar de versión. El texto del encabezado no puede depender de eso. */
:root[data-tema="oscuro"] .ag-header-cell-text,
:root[data-tema="oscuro"] .ag-header-group-text,
:root[data-tema="oscuro"] .ag-cell,
:root[data-tema="oscuro"] .ag-cell-value{color:var(--texto)}
/* ⚠️ Y el fondo de las FILAS va por regla, no por variable: de las cinco
   variables de fondo que acepta, la v33 usa las suyas para el grid y pinta la
   fila blanca igual. Con el texto ya claro encima quedaba en **1,16:1**, peor
   que antes de tocar nada — dejar una grilla a medio convertir no es mejorarla,
   es volverla ilegible. */
:root[data-tema="oscuro"] .ag-row,
:root[data-tema="oscuro"] .ag-row-odd,
:root[data-tema="oscuro"] .ag-row-even{background-color:var(--panel)}
:root[data-tema="oscuro"] .ag-row-hover{background-color:var(--t-gris-suave)}
:root[data-tema="oscuro"] .ag-header,
:root[data-tema="oscuro"] .ag-header-row{background-color:var(--t-gris-suave)}
/* El lienzo que queda DETRÁS de las filas: con menos filas que alto de
   ventana, era una franja blanca debajo de la última — el único blanco que
   quedaba en la pantalla. */
:root[data-tema="oscuro"] .ag-grid-scrolling-rows,
:root[data-tema="oscuro"] .ag-body-viewport,
:root[data-tema="oscuro"] .ag-center-cols-viewport{background-color:var(--panel)}
/* Las filas fijas: la de arriba y la del resumen de abajo, que en AG Grid 33
   se llaman `ag-grid-pinned-*-rows` y no `ag-floating-*` (el nombre viejo ya
   había dejado reglas muertas en este archivo hasta el 2026-09-07). */
:root[data-tema="oscuro"] .ag-grid-pinned-top-rows,
:root[data-tema="oscuro"] .ag-grid-pinned-bottom-rows,
/* ⚠️ Y los `-container`, que es el div que de verdad pinta: sin ellos la fila
   de «Resumen: 5 fila(s)» quedaba en blanco puro debajo de la grilla oscura.
   Aparecieron en la corrida previa al deploy, no en la primera medición: la
   grilla tenía otra cantidad de filas y el contenedor no estaba visible. */
:root[data-tema="oscuro"] .ag-grid-pinned-top-rows-container,
:root[data-tema="oscuro"] .ag-grid-pinned-bottom-rows-container{background-color:var(--t-gris-suave)}
/* 🔴 **La barra de scroll de la grilla y las casillas**, que se pintan solas.
   La barra es una franja de 1398×16 abajo de la tabla: en la captura se veía
   como un **rectángulo blanco** al pie de una pantalla oscura, y el medidor de
   superficies no la denunció porque su umbral de área la dejaba pasar. Se
   encontró MIRANDO la captura y después buscando todo lo blanco con una
   consulta aparte — el umbral ya está bajado, pero el orden fue ese. */
:root[data-tema="oscuro"] .ag-body-horizontal-scroll-viewport,
:root[data-tema="oscuro"] .ag-body-vertical-scroll-viewport,
:root[data-tema="oscuro"] .ag-horizontal-left-spacer,
:root[data-tema="oscuro"] .ag-horizontal-right-spacer{background-color:var(--panel)}
:root[data-tema="oscuro"] .ag-checkbox-input-wrapper{
  background-color:var(--panel);box-shadow:inset 0 0 0 1px var(--linea-control)}

/* ── El login y la pantalla de error ────────────────────────────────────── */
/* 🔴 Son las dos pantallas que se ven SIN sesión, así que su modo lo decide la
   EMPRESA y no la persona — y estaban rotas: la tarjeta es vidrio esmerilado
   blanco (`rgba(255,255,255,.82)`), que de noche queda gris claro con el texto
   claro encima: **1,28:1** en «Iniciar sesión». Es la primera pantalla que ve
   alguien que llega.
   ⚠️ Tardaron en medirse porque el verificador las abría con la sesión puesta y
   `tema_efectivo()` devolvía el tema de la PERSONA; hubo que moverle el tema a
   la empresa para verlas de verdad. Sus estilos viven en el `<style>` de la
   propia plantilla, no acá, y por eso el barrido de `style.css` no los vio. */
:root[data-tema="oscuro"] .login-card{
  background:rgba(23,26,33,.82);border-color:rgba(255,255,255,.10)}
:root[data-tema="oscuro"] .login-field input:focus{background:var(--panel)}
:root[data-tema="oscuro"] .login-submit{color:var(--sobre-marca)}
:root[data-tema="oscuro"] .brand-sep{background:var(--borde)}
/* Las tres preferencias de accesibilidad del sistema apagan la transparencia o
   suben el contraste y vuelven a pintar la tarjeta de blanco. De noche también
   tienen que quedar oscuras: quien pide más contraste no está pidiendo que se
   le encandile. */
@media (prefers-reduced-transparency: reduce){
  :root[data-tema="oscuro"] .login-card{background:var(--panel)}
}
@media (prefers-contrast: more){
  :root[data-tema="oscuro"] .login-card{background:var(--panel)}
  :root[data-tema="oscuro"] .login-field input{background:var(--panel)}
}

/* ── Reporte trimestral ICA (modal de Movimiento ICA, 2026-09-18) ─────────── */
/* Los meses van en cuatro columnas, una por trimestre, porque así se elige un
   trimestre completo sin leer nombres: es lo que se pide el 95% de las veces.
   El título de cada columna es un botón que marca o desmarca sus tres meses. */
.rti-periodo{margin-bottom:14px}
/* El rótulo va ARRIBA y en bloque: `fh-k` es en línea y el desplegable se le
   montaba encima, tapando la «o» de «Año». */
.rti-periodo label{display:block;margin-bottom:5px}
.rti-meses{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px}
@media (max-width:640px){.rti-meses{grid-template-columns:repeat(2,minmax(0,1fr))}}
.rti-trim{border:1px solid var(--borde);border-radius:10px;padding:10px}
.rti-trim-tit{display:block;width:100%;margin-bottom:6px;padding:0;border:0;
  background:none;text-align:left;cursor:pointer;font:inherit;font-size:11px;
  font-weight:800;letter-spacing:.04em;text-transform:uppercase;
  color:var(--tenue)}
.rti-trim-tit:hover{color:var(--marca)}
.rti-mes{display:flex;align-items:center;gap:7px;padding:3px 0;cursor:pointer}
.rti-mes span{font-size:13px;text-transform:capitalize}

.rti-resumen{display:flex;flex-wrap:wrap;gap:20px;margin-top:16px;
  padding:12px 14px;border-radius:10px;background:var(--fondo);
  border:1px solid var(--borde);font-size:13px}
.rti-resumen strong{font-size:17px;font-weight:800}

/* Los avisos: el de descuadre en rojo porque frena el botón, los otros dos en
   neutro porque informan pero no impiden generar. */
.rti-aviso{margin-top:14px;padding:12px 14px;border-radius:10px;
  border:1px solid var(--borde);background:var(--fondo);font-size:13px}
.rti-aviso-tit{font-weight:800;margin-bottom:5px}
.rti-aviso p{margin:0 0 7px;color:var(--tenue)}
.rti-aviso ul{margin:0;padding-left:18px;max-height:170px;overflow:auto}
.rti-aviso li{font-size:12.5px;color:var(--tenue);line-height:1.7}
.rti-aviso-peligro{border-color:var(--peligro)}
.rti-aviso-peligro .rti-aviso-tit{color:var(--peligro-texto,var(--peligro))}

/* ── Gastos (2026-09-18) ──────────────────────────────────────────────────── */
/* Los filtros en una fila que se envuelve: son cuatro desplegables y un
   buscador, y en pantallas angostas se apilan sin apretarse. */
.ga-filtros{display:flex;flex-wrap:wrap;gap:10px;align-items:flex-end;
  margin-bottom:16px}
.ga-filtros>*{flex:0 0 auto}
.ga-filtros>:last-child{flex:1 1 220px;min-width:180px}

/* El formulario del modal: dos columnas, y los campos largos ocupan las dos.
   Se apila a una sola columna en pantallas angostas. */
.ga-form{display:grid;grid-template-columns:1fr 1fr;gap:14px 16px}
@media (max-width:560px){.ga-form{grid-template-columns:1fr}}
.ga-campo{display:flex;flex-direction:column;gap:5px;min-width:0}
.ga-ancho{grid-column:1 / -1}
.ga-campo input{width:100%}

/* La factura de venta local, dentro del modal de Cartera. El pie va ADENTRO
   del <form> y no en el slot `pie` del Modal, porque el botón de guardar tiene
   que ser el submit del formulario: lo que escribe es Flask, no un fetch. */
.fm-local .sub{margin:0 0 14px;color:var(--tenue);font-size:13px;line-height:1.5}
.fm-local .field{margin-bottom:12px}
.fm-local .field input{width:100%}
.fm-fila{display:grid;grid-template-columns:1fr 110px;gap:10px}
.mdl-pie{display:flex;justify-content:flex-end;gap:8px;margin-top:18px}

/* El interruptor «Mostrar IVA» de Reportes de Cartera, en la cabecera junto a
   los dos desplegables. */
.rep-iva{display:flex;align-items:center;gap:7px;font-size:13px;
  color:var(--texto);white-space:nowrap;cursor:pointer;user-select:none}
.rep-iva input{cursor:pointer}

/* Un texto de tabla que se recorta con «…» a lo que quepa en su columna.
   El nombre entero va en el `title` de quien la usa.

   ⚠️ Va en el hijo y no en el `<td>` porque `text-overflow` necesita un bloque
   con ancho, y una celda de tabla en layout automático se estira a su
   contenido: sin el `max-width` la columna crece hasta partir la fila, que es
   justo el bug que esto vino a cerrar (el nombre del cliente en /facturas a
   1280 px o menos). El `max-width` es un tope, no un ancho: si la ventana
   aprieta, la columna se hace más angosta y el recorte la sigue. */
.celda-recorte{display:block;max-width:260px;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
/* ⚠️ El tope baja con la ventana, y no es cosmético: medido a 1280 px, con 260
   la columna se lleva 288 px y empuja la de acciones a apilar «Abrir» sobre el
   menú, con lo que la fila pasa de 44 px a 77. Recortar el nombre no alcanzaba
   si el ancho que se queda se lo saca a otro. */
@media (max-width: 1400px){ .celda-recorte{max-width:190px} }
@media (max-width: 1200px){ .celda-recorte{max-width:150px} }

/* La columna que se queda con el espacio que sobra y recorta SOLO lo que no
   entra. Nació el 2026-10-01 en la lista de `/siigo`, donde a 1152 px el
   nombre largo y el total se partían en dos líneas.
   ⚠️ **Por qué no `.celda-recorte`**: su tope fijo (260/190/150) se calibró
   para la tabla de `/facturas`. Puesto acá cortaba el nombre más largo hasta
   en 1920 px, donde antes entraba entero, y a 1024 la tabla igual se salía de
   su caja. Sin ningún tope, se salía 42 px a 1152 y 170 a 1024.
   🔑 El truco es la celda y no el texto: `width:100%` le da a esta columna
   todo el ancho libre, y `max-width:0` hace que su contenido no empuje la
   tabla. El recorte lo pone el ancho que quedó, no un número.
   Lo mide `scripts/verificar_alto_fila_siigo.py`, a cuatro anchos. */
.celda-fluida{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
td:has(> .celda-fluida){width:100%;max-width:0}

/* Dos textos que de noche quedaban ilegibles, encontrados el 2026-09-19 al
   ampliar `verificar_modo_noche.py` de 23 a 51 pantallas. Los dos son el mismo
   error: un fondo claro escrito a mano (blanco o gris) que de día contrasta con
   la tinta del acento y de noche no, porque la tinta no cambia pero el fondo
   sigue siendo claro sobre una pantalla oscura.

   · `.chip-conteo` dentro de un chip activo, en /documentos: el «80» daba
     1,44:1 contra el 4,5 que pide la norma (5,47:1 de día).
   · `.clear-filters`, el «Limpiar» de /nomina/reportes: 1,51:1. Ese además ya
     estaba flojo de día, en 4,52:1, apenas por encima del mínimo. */
/* ⚠️ **El par lila es el único de los cuatro sin variante de noche**:
   `--a-lila` sale de `--marca-acento`, el color de la empresa, y ese no cambia
   al apagar la luz. Por eso los dos se arreglan con el neutro `--bn-texto` y no
   con otro acento: probado primero con `--a-lila`, que no movió el número ni un
   punto. El conteo, además, suelta su fondo blanco y hereda el color del chip
   que lo contiene, que ya pasa la norma. */
:root[data-tema="oscuro"] .chip-op.activo .chip-conteo{
  background:transparent;color:inherit}
:root[data-tema="oscuro"] .filterbar .clear-filters{color:var(--bn-texto);
  text-decoration:underline}

