@font-face{font-family:'Poppins';font-weight:600;src:url('/public/brand/poppins-600.woff2') format('woff2');font-display:swap}
@font-face{font-family:'Poppins';font-weight:700;src:url('/public/brand/poppins-700.woff2') format('woff2');font-display:swap}
/* Texto y cifras del sistema: IBM Plex Sans (05-sep-2026, decisión de Alejandro en el lienzo "Tipografía del
   sistema": opción A). Un solo archivo VARIABLE (pesos 100–700) del subconjunto latin (ñ, acentos, ¿¡, €):
   lo que caiga fuera (≤ ⚠ → flechas) baja a Segoe UI por el stack, sin tofu. Empaquetada en public/brand
   (OFL 1.1, licencia al lado) como Poppins: sin internet se ve igual. */
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:100 700;src:url('/public/brand/ibm-plex-sans.woff2') format('woff2');font-display:swap;unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}

/* Tema: variables CSS. Modo Claro por defecto, Modo Oscuro con [data-theme="oscuro"].
   Paleta: naranja = acción/ubicación; navy-slate (--violet) = 2º color de marca (del logo);
   el morado estructural se retiró (headers de tabla claros + acento).
   Claro SIN blanco puro (pedido 05-jul: menos deslumbre): toda la rampa de claros baja un
   escalón (#fff→#f3f4f6, etc.) y los bordes se recalibran para seguir viéndose sobre las
   superficies atenuadas. El blanco #fff queda solo donde es contenido (fotos de producto, ticket).
   04-sep (pedido Alejandro: "el Modo Claro necesito que se incline más al gris, el blanco le quita
   ciertas características visuales"): la rampa baja OTRO escalón — superficie #f3f4f6→#e1e3e8,
   controles #edeff2→#d4d8df, fondo #e8eaee→#d2d6dd — con bordes más firmes, --muted un punto más
   oscuro y sombras algo más presentes para que tarjetas y paneles sigan despegándose del fondo. */
:root{
  /* 07-sep (pedido Alejandro: "que el Modo Claro se vea más grisáceo"): la rampa baja OTRO escalón —
     fondo #d2d6dd→#c6cad1, superficie #e1e3e8→#d5d8dd, controles #d4d8df→#c8ccd3 — bordes y hover en proporción. */
  --bg:#c6cad1; --surface:#d5d8dd; --surface-2:#c8ccd3; --text:#1f2430; --muted:#464c5b;
  --border:#aab1bd; --border-hover:#8892a4; --accent:#ee8c2a; --accent-ink:#14181f; --violet:#33456e;
  --row-hover:#c2c7d1; --th-bg:#c7ccd6; --th-text:#33456e;
  --shadow-sm:0 1px 2px rgba(16,24,40,.08);
  --shadow-md:0 1px 2px rgba(16,24,40,.09),0 2px 8px rgba(16,24,40,.12);
  --shadow:var(--shadow-sm);
  --ok:#157347; --no:#b02a1e; --accent-text:#c2410c; --info:#156c8a; --focus:#33456e;
  /* Identidad de cada moneda (chips .cur-*). Tokens PROPIOS y no --ok/--accent: el verde del dólar
     no debe leerse como "correcto" ni el chip de una moneda como algo clicable (el naranja es la
     acción de marca). Por eso COP va en púrpura y no en ámbar. */
  --cur-usd:#0f7a4d; --cur-ves:#1f5fa8; --cur-cop:#6d3fa8;
  /* Umbrales de existencia (0.158): la etiqueta de Estado (.ex-min / .ex-prom / .ex-max; también «Anulada» / «Completada» de los
     movimientos del día) y los números de sus columnas en «Existencias por umbral» comparten estos tonos. El verde del texto baja
     un punto (#1f7a34 → #1a6b2d) para leerse a 4,5:1 sobre las filas grises del modo claro. */
  --ex-min-bg:#fde2e2; --ex-min-tx:#9c0006; --ex-prom-bg:#e3f4e1; --ex-prom-tx:#1a6b2d; --ex-max-bg:#e2ecfb; --ex-max-tx:#284f9e;
  /* Botones (14-ago, decisión Alejandro): UN solo frame (el del "+Nuevo") y el color según la
     intención — naranja acción principal · slate acciones normales · rojo destructivas. */
  --btn-sec:#33456e; --btn-sec-hover:#3f5484; --btn-sec-ink:#fff;
  --btn-no-ink:#fff;
  /* Sistema de botones 05-sep (lienzo "Botones y Monedas": primario G Aurora + resto A Sólido con relieve):
     borde-filo del secundario, rampa clara del peligro y el brillo interior del nivel Sutil / chips. */
  --btn-sec-borde:#26365a; --no-claro:#c9372a; --no-borde:#8f2117; --sutil-brillo:rgba(255,255,255,.6); --inp-sombra:rgba(16,24,40,.08);
  --icon-1:#667085; --icon-2:#98a2b3;
  --ring:0 0 0 3px rgba(238,140,42,.20);
  --r-ctl:9px; --r-card:10px; --r-modal:14px;   /* 8→9 (05-sep, propuesta A) */
  /* (05-sep) IBM Plex Sans delante; Segoe UI queda de respaldo para los glifos que el subconjunto no trae. */
  --font-ui:'IBM Plex Sans',"Segoe UI Variable Text","Segoe UI",system-ui,-apple-system,sans-serif;
  --font-display:'IBM Plex Sans',"Segoe UI Variable Display","Segoe UI",system-ui,sans-serif;
  /* Encabezados/títulos en Poppins (pedido 2026-08-11: tipografía más notable y llamativa).
     La fuente YA viene empaquetada para el wordmark (600/700) — cero assets nuevos, offline igual. */
  --font-head:'Poppins',"Segoe UI Variable Display","Segoe UI",system-ui,sans-serif;
  --font-mono:"Cascadia Code",Consolas,monospace;
  color-scheme:light;
}
[data-theme="oscuro"]{
  --bg:#14161b; --surface:#1d2026; --surface-2:#252932; --text:#eceef2; --muted:#9aa3b2;
  --border:#343947; --border-hover:#4a5164; --accent:#ee8c2a; --accent-ink:#14161c; --violet:#93a7d4;
  --row-hover:#2a3040; --th-bg:#232e47; --th-text:#b9c6e4;
  --shadow-sm:0 1px 2px rgba(0,0,0,.35);
  --shadow-md:0 1px 2px rgba(0,0,0,.3),0 2px 10px rgba(0,0,0,.35);
  --ok:#34d399; --no:#f0655a; --accent-text:#f29b3f; --info:#6cc6e6; --focus:#93a7d4;
  --cur-usd:#3ddc97; --cur-ves:#7fb4f5; --cur-cop:#c3a2f5;
  --ex-min-bg:#3a1e22; --ex-min-tx:#f3a9a9; --ex-prom-bg:#1e3324; --ex-prom-tx:#a9d9b5; --ex-max-bg:#1e2c46; --ex-max-tx:#a9c9f5;
  --btn-sec:#3a4a6f; --btn-sec-hover:#485a85; --btn-sec-ink:#e8ecf5;
  --btn-no-ink:#14161c;
  --btn-sec-borde:#2c3a5c; --no-claro:#f47b71; --no-borde:#c94c42; --sutil-brillo:rgba(255,255,255,.06); --inp-sombra:rgba(0,0,0,.28);
  --icon-1:#8b94a6; --icon-2:#5f6779;
  --ring:0 0 0 3px rgba(242,155,63,.28);
  color-scheme:dark;
}

*{box-sizing:border-box}
html,body{margin:0;height:100%;overscroll-behavior-y:none}
/* Base 15px (pedido 2026-08-11: fuentes más notables — antes 14px): sube TODO lo que hereda
   (tablas, inputs, botones) un escalón de visibilidad sin tocar la densidad de filas. */
body{font-family:var(--font-ui);background:var(--bg);color:var(--text);font-size:15px;line-height:1.45;-webkit-font-smoothing:antialiased}
/* Foco visible (solo navegación por teclado) con el slate de marca */
:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
::selection{background:rgba(238,140,42,.3)}
/* Controles nativos en paleta de marca (checks, radios, date-pickers, scrollbars vía color-scheme) */
input[type="checkbox"],input[type="radio"]{accent-color:var(--accent);width:15px;height:15px}
/* Microfeedback: transición uniforme de componentes interactivos */
.btn-primary,.btn-ghost,.btn-danger,.btn-tema,.chip,.toggle,.btn-edit,.btn-del,.inp,select.inp{transition:background-color .12s ease,border-color .12s ease,color .12s ease,box-shadow .12s ease}

/* Iconos mono de 16px (reemplazan emojis en botones/cromo) */
.ic16{width:16px;height:16px;fill:currentColor;vertical-align:-3px;margin-right:5px;flex:none}
.btn-edit .ic16,.btn-del .ic16{margin-right:0;vertical-align:-2px}
.vm-head .ic16{width:18px;height:18px;fill:#fff;margin-right:2px}

/* Buscador con lupa integrada */
.search-wrap{position:relative;display:inline-flex;flex:0 1 380px;min-width:220px}
.search-wrap .ic16{position:absolute;left:10px;top:50%;transform:translateY(-50%);margin:0;fill:var(--muted);pointer-events:none}
.search-wrap .inp{flex:1;width:100%;min-width:0;padding-left:32px}

/* Spinner de carga en el botón que disparó la petición HTMX */
button.htmx-request{pointer-events:none;opacity:.75}
button.htmx-request::after{content:"";display:inline-block;width:11px;height:11px;margin-left:7px;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:ex-spin .6s linear infinite;vertical-align:-1px}
@keyframes ex-spin{to{transform:rotate(1turn)}}

/* Toasts (avisos flotantes; window.toast en app.js) */
.toast-host{position:fixed;right:18px;top:64px;z-index:60;display:flex;flex-direction:column;gap:8px}
.toast{background:var(--surface);border:1px solid var(--border);border-left:3px solid var(--ok);border-radius:var(--r-card);padding:10px 14px;font-size:14px;color:var(--text);box-shadow:var(--shadow-md);animation:toast-in .18s ease}
.toast.err{border-left-color:var(--no)}
@keyframes toast-in{from{opacity:0;transform:translateY(6px)}to{opacity:1}}

.app{display:flex;flex-direction:column;height:100vh}

/* Barra superior: mismo gradiente azul oscuro del INICIO (login/saludo) en todo el frame.
   Sobre ese fondo el texto va en blanco SIEMPRE — el gradiente es oscuro en tema claro y oscuro,
   así que estos colores se fijan aquí y no dependen de las variables del tema. Tamaños +10%. */
.appbar{display:flex;align-items:center;gap:14px;padding:9px 24px;border-bottom:1px solid rgba(255,255,255,.09);
  background:radial-gradient(1100px 700px at 75% -10%,#22335c 0%,#0c1730 60%,#0a1226 100%)}
.appbar-title{font-size:20px;margin:0;font-weight:600;letter-spacing:0;color:#fff;font-family:var(--font-head)}
/* El chip navy de la marca sobraba: el frame entero ya es navy. */
.brand{display:inline-flex;align-items:center;justify-content:center;background:transparent;border-radius:10px;padding:4px 2px}
.appbar .btn-tema{background:rgba(255,255,255,.10);color:#fff;border-color:rgba(255,255,255,.22)}
.appbar .btn-tema:hover{background:rgba(255,255,255,.18);border-color:rgba(255,255,255,.38)}
/* Pie del membrete (29-ago-2026, pedido Alejandro): nombre de ESTA caja + versión instalada, abajo a la derecha.
   El bloque derecho se estira a la altura del membrete: Salir arriba, el rótulo abajo, ambos pegados a la
   derecha. Mide lo mismo que el bloque de marca (33 + 4 + 11 = 48 px) → la barra no crece. Paleta del tagline. */
.appbar-der{display:flex;flex-direction:column;align-items:flex-end;justify-content:space-between;align-self:stretch;gap:4px}
.appbar-acc{display:flex;align-items:center;gap:6px}
/* Alternador de tema del membrete (09-sep, pedido Alejandro): SOLO en el teléfono; en la PC el tema se cambia en Ajustes.
   Muestra la luna en modo claro (lo que vas a activar) y el sol en modo oscuro. */
.tema-movil{display:none;padding:6px 8px}
.tema-movil .ic16{margin:0;vertical-align:-2px}
.tema-movil .tm-sun{display:none}
[data-theme="oscuro"] .tema-movil .tm-sun{display:inline}
[data-theme="oscuro"] .tema-movil .tm-moon{display:none}
.appbar-pie{display:inline-flex;align-items:baseline;gap:5px;font-family:var(--font-head);font-size:11px;line-height:1;white-space:nowrap;color:rgba(255,255,255,.5)}
.appbar-pie b{font-weight:600;color:#b9c6e4;letter-spacing:.04em}
.appbar-pie i{font-style:normal;color:rgba(255,255,255,.35)}
.appbar-pie .v{font-weight:600;letter-spacing:.02em}
/* 25-sep-2026 (Alejandro): aviso de ACTUALIZACIÓN lista en el membrete y en el login. El cascarón de Electron manda X-Exynol-Act
   (la versión descargada y verificada que espera el cierre en ESA PC); sin cascarón (navegador, teléfono) no hay aviso porque no
   es esa pantalla la que se actualiza. Clic → Ajustes › Actualizaciones. */
.appbar-act{display:inline-flex;align-items:center;gap:4px;margin-left:6px;padding:2px 8px;border-radius:999px;background:rgba(238,140,42,.18);border:1px solid rgba(238,140,42,.45);color:#ffd9b0;font-weight:700;letter-spacing:.02em;text-decoration:none;line-height:1.2;transition:background .2s}
.appbar-act:hover{background:rgba(238,140,42,.32);color:#fff}
.exlogo{display:inline-flex;align-items:flex-end;line-height:1}
.ex-exy{height:26px;width:auto;display:block}
/* 🛟 Aviso del SALVAVIDAS (20-sep-2026): «tienes trabajo sin aplicar aquí». Aparece arriba del
   formulario del módulo, nunca tapa nada y espera a que el usuario decida — restaurar por su
   cuenta sería otra forma de perderle el trabajo que tenga en pantalla. */
.borrador-aviso{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:0 0 12px;padding:10px 14px;
  background:rgba(238,140,42,.12);border:1px solid var(--accent);border-left-width:4px;border-radius:var(--r-card);
  font-size:13.5px;color:var(--text)}
.borrador-aviso .ba-ico{font-size:17px;line-height:1}
.borrador-aviso .ba-txt{flex:1;min-width:200px}
.borrador-aviso button{padding:5px 12px;font-size:13px}
.ex-nol{font-family:'Poppins',sans-serif;font-weight:600;font-size:22px;color:#E2882C;margin-left:2px;line-height:1;position:relative;top:-2px;display:inline-flex;align-items:flex-end}
/* La «o» del wordmark ES el símbolo de la Singularidad (Ruta 1 · Núcleo, elegida por Alejandro el
   19-sep-2026): núcleo macizo + órbita −24° + satélite posado. La caja mide 0,62em × 1em sobre el
   cuerpo de «nol», así que escala sola con el font-size — membrete (22px) y login (34px) sin un solo
   número por pantalla. overflow:visible porque la órbita desborda a propósito; los 0,085em de margen
   le dan aire ante la n y la l. El núcleo va siempre en el naranja de marca; órbita y satélite siguen
   al TEMA, salvo en la barra superior, que es navy en los dos. */
.ex-nol{--ex-orb:#5b6579;--ex-sat:#16264f}
[data-theme="oscuro"] .ex-nol{--ex-orb:#c4ccd8;--ex-sat:#f4f7fc}
.appbar .ex-nol{--ex-orb:#c4ccd8;--ex-sat:#f4f7fc}
.ex-o{width:.62em;height:1em;flex-shrink:0;margin:0 .085em;overflow:visible;display:block}
.ex-o-orb{stroke:var(--ex-orb)}
.ex-o-nuc{fill:#E2882C}
/* El satélite no se coloca con cx/cy sino con offset-path sobre la elipse completa (en unidades del
   viewBox, así que escala con el símbolo). En reposo va al 100 % del recorrido = posado arriba a la
   derecha. La capa .det va DETRÁS del núcleo y la .del DELANTE; en reposo manda la de delante. */
.ex-o-sat{fill:var(--ex-sat);offset-rotate:0deg;offset-distance:100%;
  offset-path:path('M70.46 35.53A43.2 17.7 -24 0 1 -8.46 70.67A43.2 17.7 -24 0 1 70.46 35.53')}
.ex-o-sat.det{opacity:0}
/* En la copia del destello el símbolo se blanquea como las letras (si no, la «o» se queda apagada
   mientras la banda de luz cruza el wordmark). */
.brillo-top .ex-o-nuc{fill:#fff3de}
.brillo-top .ex-o-orb{stroke:#fff}
.brillo-top .ex-o-sat{fill:#fff}
.ex-soft{font-family:'Poppins',sans-serif;font-weight:600;font-size:15px;color:#dbe4f7;margin-left:8px;letter-spacing:.01em;align-self:center;line-height:1}
.ex-erp{font-family:'Poppins',sans-serif;font-weight:700;font-size:12px;color:#fff;background:var(--accent);border-radius:5px;padding:2px 7px;margin-left:9px;letter-spacing:.05em;align-self:center}
/* Edición Exynol Lite: chip "LITE" junto al ERP, en el violeta del tagline ULTRACODE (el ERP ya es naranja); blanco sobre #9333ea 5,3:1 AA en ambos temas */
.ex-lite{font-family:'Poppins',sans-serif;font-weight:700;font-size:12px;color:#fff;background:#9333ea;border-radius:5px;padding:2px 7px;margin-left:6px;letter-spacing:.05em;align-self:center}
.brand-login{padding:20px;margin:0 0 6px}
.brand-login .ex-exy{height:40px}
.brand-login .ex-nol{font-size:34px;margin-left:3px;top:-3px}
.brand-login .ex-erp{font-size:15px;padding:2px 9px;margin-left:11px}
.brand-login .ex-lite{font-size:15px;padding:2px 9px;margin-left:8px}
/* LOCKUP APILADO (paquete de marca del 23-sep-2026, aprobado por Alejandro): «Exynol» y debajo el recuadro «Software ERP»
   (naranja #ee8c2a) o «Software ERP Lite» (morado #9333ea), del mismo ancho que la marca, texto centrado. Medidas en función
   de F = cuerpo de «nol»: separación 0,163F · chip 0,34F (Lite 0,296F) Poppins 700, padding 0,089F × 0,177F, radio 0,118F
   (en em del propio chip: .26/.52 y .348). El font-size de .ex-apilado ES F: 22 px de diario, 34 px en las pantallas de acceso. */
.ex-apilado{flex-direction:column;align-items:stretch;gap:.163em;font-size:22px}
.ex-marca{display:inline-flex;align-items:flex-end;line-height:1;align-self:center}
.ex-chip{display:flex;align-items:center;justify-content:center;font-family:'Poppins',sans-serif;font-weight:700;font-size:.34em;line-height:1;letter-spacing:.06em;color:#fff;background:#ee8c2a;border-radius:.348em;padding:.26em .52em;white-space:nowrap}
/* El recuadro Lite lleva también .ex-lite (pruebas y título); esa clase trae las medidas del chip VIEJO «LITE» (12/15 px,
   margen, align-self:center) y con la misma especificidad que .brand-login .ex-lite pisaba el lockup: aquí se impone. */
.ex-chip.ex-chip-lite{background:#9333ea;font-size:.296em;letter-spacing:.05em;padding:.375em .6em;margin-left:0;align-self:stretch}
.brand-login .ex-apilado{font-size:34px}
/* MEMBRETE con el lockup apilado (24-sep-2026, variante B del lienzo «Icono y membrete», decisión de Alejandro para las dos
   ediciones): F = 22 → el recuadro a .34F saldría de 7,5 px, ilegible: piso de 9 px (Lite 8 px). «Impulsado por ULTRACODE»
   pasa a la DERECHA del lockup, en dos líneas tras un filete, así el membrete apenas crece (66 → 69 px). El destello
   (.brillo-top) repite la misma fila. */
.appbar .exbrand,.appbar .brillo-base,.appbar .brillo-top{flex-direction:row;align-items:center;gap:0}
.appbar .ex-chip{font-size:9px}
.appbar .ex-chip.ex-chip-lite{font-size:8px}
.appbar .ex-tag{flex-direction:column;align-items:flex-start;gap:3px;margin-left:12px;padding-left:12px;border-left:1px solid rgba(255,255,255,.14)}
.appbar .brillo-top .ex-tag{border-color:transparent}
.appbar .ex-tag-txt{font-size:8px}
.appbar .ex-tag svg{height:9px}
/* «Exy» sobre fondo claro va oscurecida (brightness .3), como en el paquete de marca; sobre navy (membrete, tema oscuro) tal cual. */
.ex-exy{filter:brightness(.3)}
.appbar .ex-exy,[data-theme="oscuro"] .ex-exy{filter:none}
/* PRESENTACIÓN «del ICONO a la marca» (23-sep-2026, concepto de Alejandro sobre su paquete de marca final): «lo primero que
   aparezca sea el icono de escritorio; los dos corchetes que envuelven al orbitador son las dos mitades de la X de Exynol,
   así que se mueven y se ensamblan en su sitio, después aparecen las otras letras y después "Software ERP", todo mientras
   el orbitador gira». Corre UNA vez, solo en las pantallas de acceso (lockup apilado). Reloj: 4,4 s = 100 %.
     0 – 11 %   nace el icono: el símbolo (núcleo + órbita) y los dos corchetes a sus lados, centrados en la marca
     11 – 25 %  se sostiene (el satélite ya orbita)
     25 – 52 %  vuelo: los corchetes cruzan y se ensamblan en la X de «Exy»; el símbolo se desliza a su sitio de «o»
     52 – 64 %  aparecen E y y (exy.png entra en fundido mientras los corchetes se retiran: son los mismos píxeles)
     55 – 69 %  suben la n y la l
     68 – 80 %  entra el recuadro «Software ERP»
     0 – 82 %   el satélite da tres vueltas (delante en la primera mitad de cada una, detrás en la segunda) y se posa
   public/presentacion.js mide (dónde cae cada mitad en exy.png y dónde nace el icono) y pone .ex-p-va para arrancar;
   .ex-p-prep esconde las piezas mientras mide; .ex-p-quieto (reducir movimiento / animación apagada) = reposo directo.
   Sin JS: ni escenario ni animación, el logo aprobado quieto. Las medidas van en F = cuerpo de «nol» (LEEME del paquete). */
.ex-marca{position:relative}
.ex-p-stage{display:none}
.ex-p-x{position:absolute;display:block;pointer-events:none;will-change:transform,opacity}
.ex-p-xi,.ex-p-xd{filter:brightness(.3)}
.appbar .ex-p-x,[data-theme="oscuro"] .ex-p-x{filter:none}
.brillo-top .ex-p-x{filter:brightness(1.9) saturate(.4)}
.brand-login.ex-p-prep .ex-exy,.brand-login.ex-p-prep .ex-n,.brand-login.ex-p-prep .ex-l,.brand-login.ex-p-prep .ex-o,
.brand-login.ex-p-prep .ex-chip{opacity:0}
.brand-login.ex-p-va .ex-p-stage{display:block;position:absolute;inset:0;overflow:visible}
.brand-login.ex-p-va .ex-p-x{animation:ex-p-x 4.4s 1 both}
.brand-login.ex-p-va .ex-exy{animation:ex-p-exy 4.4s ease-out 1 both}
.brand-login.ex-p-va .ex-o{animation:ex-p-o 4.4s 1 both;transform-origin:50% 53%}
.brand-login.ex-p-va .ex-n{animation:ex-p-n 4.4s ease-out 1 both}
.brand-login.ex-p-va .ex-l{animation:ex-p-l 4.4s ease-out 1 both}
.brand-login.ex-p-va .ex-o-sat{animation:ex-p-sat 4.4s linear 1 both}
.brand-login.ex-p-va .ex-o-sat.del{animation:ex-p-sat 4.4s linear 1 both,ex-p-del 4.4s linear 1 both}
.brand-login.ex-p-va .ex-o-sat.det{animation:ex-p-sat 4.4s linear 1 both,ex-p-det 4.4s linear 1 both}
.brand-login.ex-p-va .ex-chip{animation:ex-p-chip 4.4s ease-out 1 both}
/* Los corchetes: nacen en el icono (--dx/--dy, en px, los pone presentacion.js), vuelan a su sitio en la X y se retiran
   cuando «Exy» ya está entera (misma tinta, mismos píxeles: el relevo no se nota). */
@keyframes ex-p-x{
  0%{opacity:0;transform:translate(var(--dx),var(--dy)) scale(.86);animation-timing-function:cubic-bezier(.2,.8,.3,1)}
  11%{opacity:1;transform:translate(var(--dx),var(--dy)) scale(1)}
  25%{opacity:1;transform:translate(var(--dx),var(--dy));animation-timing-function:cubic-bezier(.65,0,.3,1)}
  52%,56%{opacity:1;transform:none}
  64%,100%{opacity:0;transform:none}}
/* El símbolo: nace (escala) donde el icono y se desliza a su sitio de «o» (--p-o-dx, en px). */
@keyframes ex-p-o{
  0%{opacity:0;transform:translateX(var(--p-o-dx)) scale(.6);animation-timing-function:cubic-bezier(.2,.8,.3,1)}
  11%{opacity:1;transform:translateX(var(--p-o-dx)) scale(1)}
  25%{opacity:1;transform:translateX(var(--p-o-dx));animation-timing-function:cubic-bezier(.65,0,.3,1)}
  52%,100%{opacity:1;transform:none}}
@keyframes ex-p-exy{0%,52%{opacity:0}64%,100%{opacity:1}}
@keyframes ex-p-n{0%,55%{transform:translateY(.28em);opacity:0}66%,100%{transform:none;opacity:1}}
@keyframes ex-p-l{0%,58%{transform:translateY(.28em);opacity:0}69%,100%{transform:none;opacity:1}}
/* Tres vueltas seguidas (el camino es cerrado: 100 % ≡ 0 %) y se posa arriba a la derecha. */
@keyframes ex-p-sat{0%{offset-distance:0%}27.3%{offset-distance:100%}27.31%{offset-distance:0%}54.6%{offset-distance:100%}54.61%{offset-distance:0%}81.9%,100%{offset-distance:100%}}
/* Las dos capas del satélite se turnan a media vuelta: delante en la primera mitad de cada vuelta, detrás en la segunda. */
@keyframes ex-p-del{0%,13.65%{opacity:1}13.66%,27.3%{opacity:0}27.31%,40.95%{opacity:1}40.96%,54.6%{opacity:0}54.61%,68.25%{opacity:1}68.26%,81.9%{opacity:0}81.91%,100%{opacity:1}}
@keyframes ex-p-det{0%,13.65%{opacity:0}13.66%,27.3%{opacity:1}27.31%,40.95%{opacity:0}40.96%,54.6%{opacity:1}54.61%,68.25%{opacity:0}68.26%,81.9%{opacity:1}81.91%,100%{opacity:0}}
@keyframes ex-p-chip{0%,68%{opacity:0;transform:translateY(8px)}80%,100%{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){
  .brand-login .ex-p-stage,.brand-login.ex-p-va .ex-p-stage{display:none}
  .brand-login .ex-exy,.brand-login .ex-n,.brand-login .ex-l,.brand-login .ex-o,.brand-login .ex-o-sat,.brand-login .ex-chip{animation:none;opacity:1}
}
/* Tagline bajo el wordmark: "Impulsado por" en texto normal + SOLO "ULTRACODE" en pixel-art 3D
   (morado franco #c084fc/#9333ea). El alto del SVG manda; el ancho escala con el viewBox. */
.exbrand{display:inline-flex;flex-direction:column;align-items:center;gap:4px}
.ex-tag{display:inline-flex;align-items:center;gap:6px;line-height:0}
.ex-tag-txt{font-family:'Poppins',sans-serif;font-weight:600;font-size:9px;letter-spacing:.12em;text-transform:uppercase;color:#b9c6e4;line-height:1}
.ex-tag-pix{display:block;line-height:0}
.ex-tag svg{display:block;height:10px;width:auto}
/* PANTALLAS DE ACCESO = MEMBRETE (29-sep-2026, pedido de Alejandro: «el membrete del login igual al del módulo general, con la
   división: el Exynol por un lado y el Impulsado por ULTRACODE del otro»). La misma fila de la barra superior —lockup a la
   izquierda, filete y el tagline en dos líneas a la derecha— a la escala de la pantalla de acceso (la tarjeta mide 360 px:
   medidas justas, probadas con captura). La copia del destello repite la fila sin filete. En pantallas angostas el tagline
   vuelve debajo, como antes. */
.brand-login .exbrand,.brand-login .brillo-base,.brand-login .brillo-top{flex-direction:row;align-items:center;gap:0}
.brand-login .ex-tag{flex-direction:column;align-items:flex-start;gap:5px;margin-left:14px;padding-left:14px;border-left:1px solid rgba(22,38,79,.16)}
[data-theme="oscuro"] .brand-login .ex-tag,.saludo .brand-login .ex-tag{border-left-color:rgba(255,255,255,.16)}
.brand-login .brillo-top .ex-tag{border-color:transparent}
.brand-login .ex-tag-txt{font-size:10px}
.brand-login .ex-tag svg{height:12px}
@media (max-width:400px){
  .brand-login .exbrand,.brand-login .brillo-base,.brand-login .brillo-top{flex-direction:column;gap:8px}
  .brand-login .ex-tag{flex-direction:row;align-items:center;gap:8px;margin-left:0;padding-left:0;border-left:0}
}
/* DESTELLO del wordmark (03-sep-2026, "Opción A · destello lineal" elegida por Alejandro en el lienzo
   Brillo Exynol): .brillo-base es el wordmark real; .brillo-top una copia en blanco apilada encima y
   recortada por una máscara en banda (105°) que barre de derecha a izquierda en el 11 % de un ciclo
   de 10 s (≈1,1 s de destello, 8,9 s de reposo). El brillo sigue las letras y los badges, no el fondo.
   Ambas capas repiten la columna flex del .exbrand para medir exactamente lo mismo. */
.exbrand{position:relative}
.brillo-base,.brillo-top{display:inline-flex;flex-direction:column;align-items:center;gap:4px}
.brillo-top{position:absolute;inset:0;pointer-events:none;
  -webkit-mask:linear-gradient(105deg,transparent 42%,#000 50%,transparent 58%) 160% 0/260% 100% no-repeat;
  mask:linear-gradient(105deg,transparent 42%,#000 50%,transparent 58%) 160% 0/260% 100% no-repeat;
  animation:ex-destello 10s linear infinite}
.brillo-top .ex-exy{filter:brightness(1.9) saturate(.4)}
.brillo-top .ex-nol{color:#fff3de}
.brillo-top .ex-soft{color:#fff}
.brillo-top .ex-erp{background:#ffd6a8}
.brillo-top .ex-lite{background:#e9d5ff}
.brillo-top .ex-tag-txt{color:#fff}
.brillo-top .ex-tag svg g{fill:#e9d5ff}
@keyframes ex-destello{0%{-webkit-mask-position:160% 0;mask-position:160% 0}11%{-webkit-mask-position:-60% 0;mask-position:-60% 0}100%{-webkit-mask-position:-60% 0;mask-position:-60% 0}}
/* En las pantallas de acceso con TEMA CLARO la tarjeta es blanca y el "Exy" es oscuro: una copia
   blanqueada no brilla, se lava. Ahí el destello se apaga; en la barra (siempre navy) y en el tema
   oscuro sí corre. */
[data-theme="claro"] .brand-login .brillo-top{display:none}
@media (prefers-reduced-motion:reduce){.brillo-top,.med-glint{display:none}}
/* En tema claro las tarjetas de acceso (login/setup/licencia) son blancas: el texto baja a slate */
[data-theme="claro"] .login-wrap .ex-tag-txt{color:#5b6c94}

/* Launcher: banda horizontal de iconos grandes = navegación principal (reemplaza al sidebar) */
.launcher{display:flex;gap:4px;flex-wrap:wrap;align-items:flex-start;padding:6px 16px;background:var(--surface);border-bottom:1px solid var(--border)}
.tile{display:flex;flex-direction:column;align-items:center;gap:4px;min-width:76px;padding:8px 10px;border-radius:var(--r-card);color:var(--text);text-decoration:none;border:1px solid transparent;transition:background .12s,border-color .12s}
.tile .ic{width:29px;height:29px;display:inline-flex;align-items:center;justify-content:center;flex:none}
.tile .ic svg{width:29px;height:29px;display:block;overflow:visible}
.tile .lbl{font-size:12px;font-weight:600;text-align:center;line-height:1.15;white-space:nowrap}
/* Iconos del launcher: monocromos en reposo (duotono de VALOR, no de matiz); el color aparece al hover/activo */
.ic-b{fill:var(--icon-1)}
.ic-a{fill:var(--icon-2)}
.tile:hover{background:var(--surface-2);border-color:var(--border)}
.tile:hover .ic-b{fill:var(--violet)}
.tile:hover .ic-a{fill:var(--accent)}
.tile.active{background:var(--accent);color:var(--accent-ink);border-color:var(--accent)}
.tile.active .ic-b{fill:var(--accent-ink)}
/* Activo (29-ago-2026): acentos en BLANCO. Con tinta al 72 % sobre el cuerpo en tinta, las piezas interiores (flecha de
   Devoluciones, moneda de Cuentas, renglones de Ventas y Auditoría) desaparecían — lo notó Alejandro en la demo de gestos. */
.tile.active .ic-a{fill:rgba(255,255,255,.92)}

/* ── Gestos del launcher (29-ago-2026, pedido Alejandro: "una animación en cada icono de cada módulo al ser
   seleccionado"; eligió "gesto propio" sobre la variante uniforme — demo en exynol-negocio/launcher-gestos).
   Cada icono mueve las piezas que su propio dibujo sugiere; los ganchos son las clases por pieza de NAV_ICONS
   (layout.js). DISPARO: al ATERRIZAR — el tile llega con .active en la página nueva y la animación corre UNA vez.
   El launcher es su propio grupo de view-transition (más abajo), así que se queda quieto mientras el contenido se
   desliza; --g0 es el respiro para que el gesto arranque cuando esa transición (.27s) ya asentó. En el clic mismo,
   solo un acuse (:active encoge el icono). Solo transform/opacity: compositado, como el patrón Exy.
   transform-box:fill-box mide el origen sobre la propia pieza (bisagra de la tapa, base de la barra, centro de la
   órbita); el overflow:visible del svg (arriba) es porque la tapa gira y la moneda cae fuera del viewBox. */
.tile .gest{transform-box:fill-box;transform-origin:center}
.tile:active .ic{transform:scale(.9);transition:transform .08s}
@media (prefers-reduced-motion: no-preference){
  .tile.active{--g0:.2s;animation:g-pop .3s var(--g0) cubic-bezier(.2,.8,.3,1.15) both}
  .tile.active .vt-tk{animation:g-up .42s var(--g0) cubic-bezier(.2,.8,.3,1.15) both}
  .tile.active .vt-l1{transform-origin:0 50%;animation:g-print .26s calc(var(--g0) + .14s) ease-out both}
  .tile.active .vt-l2{transform-origin:0 50%;animation:g-print .26s calc(var(--g0) + .24s) ease-out both}
  .tile.active .inv-lid{transform-origin:0 100%;animation:g-lid .52s var(--g0) cubic-bezier(.2,.8,.3,1.1) both}
  .tile.active .cp-svg{animation:g-cart .48s var(--g0) cubic-bezier(.2,.8,.3,1.15) both}
  .tile.active .cp-w1{animation:g-wheel .48s calc(var(--g0) + .04s) ease-out both}
  .tile.active .cp-w2{animation:g-wheel .48s calc(var(--g0) + .12s) ease-out both}
  .tile.active .dv-arrow{animation:g-back .42s var(--g0) cubic-bezier(.2,.8,.3,1.2) both}
  .tile.active .cu-coin{animation:g-coin .62s var(--g0) ease-out both}
  .tile.active .cl-2{animation:g-arrive .38s var(--g0) cubic-bezier(.2,.8,.3,1.15) both}
  .tile.active .pv-svg{animation:g-truck .52s var(--g0) cubic-bezier(.2,.8,.3,1.15) both}
  .tile.active .pv-w1{animation:g-wheel .52s calc(var(--g0) + .06s) ease-out both}
  .tile.active .pv-w2{animation:g-wheel .52s calc(var(--g0) + .14s) ease-out both}
  .tile.active .rp-1{transform-origin:50% 100%;animation:g-grow .42s var(--g0) cubic-bezier(.2,.8,.3,1.2) both}
  .tile.active .rp-2{transform-origin:50% 100%;animation:g-grow .42s calc(var(--g0) + .09s) cubic-bezier(.2,.8,.3,1.2) both}
  .tile.active .rp-3{transform-origin:50% 100%;animation:g-grow .42s calc(var(--g0) + .18s) cubic-bezier(.2,.8,.3,1.2) both}
  .tile.active .mk-1{animation:g-tick-down .56s var(--g0) ease-in-out both}
  .tile.active .mk-2{animation:g-tick-up .56s calc(var(--g0) + .07s) ease-in-out both}
  .tile.active .mk-3{animation:g-tick-down .56s calc(var(--g0) + .14s) ease-in-out both}
  .tile.active .au-clip{animation:g-press .32s var(--g0) ease-out both}
  .tile.active .au-l1{transform-origin:0 50%;animation:g-print .24s calc(var(--g0) + .08s) ease-out both}
  .tile.active .au-l2{transform-origin:0 50%;animation:g-print .24s calc(var(--g0) + .2s) ease-out both}
  .tile.active .ai-orbit{animation:g-orbit .7s var(--g0) cubic-bezier(.3,.7,.3,1) both}
  .tile.active .ai-core{animation:g-pulse .7s var(--g0) ease-in-out both}
  .tile.active .cf-k1{animation:g-knob-r .48s var(--g0) ease-in-out both}
  .tile.active .cf-k2{animation:g-knob-l .48s calc(var(--g0) + .07s) ease-in-out both}
  .tile.active .cf-k3{animation:g-knob-r .48s calc(var(--g0) + .14s) ease-in-out both}
}
@keyframes g-pop{0%{transform:scale(1)}40%{transform:scale(1.05)}100%{transform:scale(1)}}
@keyframes g-up{0%{transform:translateY(5px);opacity:.55}100%{transform:none;opacity:1}}
@keyframes g-print{0%{transform:scaleX(0)}100%{transform:scaleX(1)}}
@keyframes g-lid{0%{transform:rotate(0)}42%{transform:rotate(-22deg)}100%{transform:rotate(0)}}
@keyframes g-cart{0%{transform:translateX(-3px) rotate(-4deg)}60%{transform:translateX(.8px) rotate(1deg)}100%{transform:none}}
@keyframes g-wheel{0%{transform:none}45%{transform:translateY(-1.6px)}100%{transform:none}}
@keyframes g-back{0%{transform:none}45%{transform:translateX(-4.5px)}80%{transform:translateX(.9px)}100%{transform:none}}
@keyframes g-coin{0%{transform:translateY(-8px) scaleX(.15);opacity:0}30%{transform:translateY(-4px) scaleX(1);opacity:1}48%{transform:translateY(-1px) scaleX(.2)}66%{transform:translateY(1.3px) scaleX(1)}82%{transform:translateY(-.6px) scaleX(.85)}100%{transform:none}}
@keyframes g-arrive{0%{transform:translateX(5px);opacity:0}100%{transform:none;opacity:1}}
@keyframes g-truck{0%{transform:translateX(-4px)}65%{transform:translateX(1px)}100%{transform:none}}
@keyframes g-grow{0%{transform:scaleY(0)}100%{transform:scaleY(1)}}
@keyframes g-tick-up{0%{transform:none}50%{transform:translateY(-2.4px)}100%{transform:none}}
@keyframes g-tick-down{0%{transform:none}50%{transform:translateY(2px)}100%{transform:none}}
@keyframes g-press{0%{transform:translateY(-1.6px)}60%{transform:translateY(.4px)}100%{transform:none}}
@keyframes g-orbit{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}
@keyframes g-pulse{0%{transform:scale(1)}45%{transform:scale(1.32)}100%{transform:scale(1)}}
@keyframes g-knob-r{0%{transform:none}50%{transform:translateX(4px)}100%{transform:none}}
@keyframes g-knob-l{0%{transform:none}50%{transform:translateX(-4px)}100%{transform:none}}

.spacer{flex:1}
.btn-tema{background:var(--btn-sec);color:var(--btn-sec-ink);border:none;border-radius:var(--r-ctl);padding:8px 16px;cursor:pointer;font-size:14px;font-weight:600}
.btn-tema:hover{background:var(--btn-sec-hover)}
/* Selector de tema (Claro/Oscuro) en Configuración */
.tema-opts{display:inline-flex;gap:8px}
.btn-tema.active{background:var(--accent);color:var(--accent-ink);border-color:var(--accent);font-weight:600}

.content{flex:1;padding:16px 22px;overflow:auto;overscroll-behavior-y:contain}
.toolbar{display:flex;align-items:center;gap:10px;margin-bottom:14px;flex-wrap:wrap;min-height:38px}
/* Los buscadores de toolbar no se estiran a pantalla completa */
.toolbar .inp.grow,.toolbar input[type="search"]{flex:0 1 380px;min-width:220px}
/* Celdas de rango de fechas del toolbar (Ventas): etiqueta al lado, compactas */
.tb-fecha{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--muted);white-space:nowrap}
.tb-fecha .inp{width:140px}
.chips{display:flex;gap:6px;flex-wrap:wrap}
.chip{background:var(--surface);border:1px solid var(--border);border-radius:20px;padding:6px 14px;cursor:pointer;color:var(--text);font-size:13.5px;font-weight:600;box-shadow:inset 0 1px 0 var(--sutil-brillo)}
.chip:hover{border-color:var(--border-hover)}
.chip.active{background:var(--violet);color:#fff;border-color:var(--violet);box-shadow:inset 0 1px 0 rgba(255,255,255,.18)}
[data-theme="oscuro"] .chip.active{color:#14161b}
/* ─── BOTONES (05-sep-2026, decisión de Alejandro en el lienzo "Botones y Monedas"): PRIMARIO de la propuesta G
   "Aurora" — degradado naranja→coral de la marca (el del header de la Caja y Exynol AI) que se desplaza al pasar,
   con halo del mismo color — y el RESTO de la propuesta A "Sólido con relieve": secundario slate con brillo
   arriba y sombra corta, nivel SUTIL para lo neutro (Cancelar/Limpiar/Cerrar/Quitar/No), peligro con relieve.
   Todos llevan borde de 1px (transparente en el primario) y padding 7/15 en vez de 8/16: mismo tamaño exterior
   que antes, así ninguna fila ni pie se mueve. */
.btn-primary{background-color:var(--accent);background-image:linear-gradient(115deg,#ffa53f 0%,#ee8c2a 45%,#f2705a 100%);background-size:200% 100%;background-position:0 0;color:var(--accent-ink);border:1px solid transparent;border-radius:var(--r-ctl);padding:7px 15px;font-weight:600;cursor:pointer;box-shadow:0 4px 12px rgba(238,140,42,.32);transition:background-position .25s ease,box-shadow .14s ease,transform .08s ease,color .12s ease}
.btn-primary:hover{background-position:100% 0;box-shadow:0 6px 16px rgba(242,112,90,.38)}
.btn-primary:active{transform:translateY(1px);box-shadow:0 2px 6px rgba(238,140,42,.35)}
.btn-primary:disabled{background-image:none;background-color:var(--border);color:var(--muted);opacity:1;cursor:not-allowed;box-shadow:none;transform:none}

.grid{width:100%;border-collapse:collapse;background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);overflow:hidden;box-shadow:var(--shadow)}
/* Reportes → Existencias por umbral: checkboxes Ver mínimos / promedios / máximos (pedido 03-ago) */
.rep-chks{display:inline-flex;gap:8px;align-items:center;padding:5px 8px;border:1px solid var(--border);border-radius:var(--r-ctl);background:var(--surface)}
.rep-chk{display:inline-flex;gap:6px;align-items:center;font-size:14px;font-weight:600;cursor:pointer;white-space:nowrap;padding:3px 10px;border-radius:999px}
.rep-chk input{margin:0;cursor:pointer}
/* Cada casilla pintada con su banda (pedido 06-ago): Mín roja · Prom amarilla · Máx verde —
   la misma paleta del editor de producto; pastel pleno en claro, translúcido en oscuro. */
.rep-chk-minimo{background:#ffc7ce;color:#9c0006}
.rep-chk-promedio{background:#fff9b2;color:#9c8000}
.rep-chk-maximo{background:#c6efce;color:#006100}
.rep-chk-minimo input{accent-color:#9c0006}
.rep-chk-promedio input{accent-color:#9c8000}
.rep-chk-maximo input{accent-color:#006100}
[data-theme="oscuro"] .rep-chk-minimo{background:rgba(156,0,6,.38);color:#ffb3ba}
[data-theme="oscuro"] .rep-chk-promedio{background:rgba(156,128,0,.33);color:#ffe98a}
[data-theme="oscuro"] .rep-chk-maximo{background:rgba(0,97,0,.38);color:#8ee6a8}
[data-theme="oscuro"] .rep-chk-minimo input{accent-color:#ffb3ba}
[data-theme="oscuro"] .rep-chk-promedio input{accent-color:#ffe98a}
[data-theme="oscuro"] .rep-chk-maximo input{accent-color:#8ee6a8}

/* Inventario (pedido 03-ago): el nombre del producto NUNCA se parte en varias líneas — cada fila
   es una sola línea y la tabla scrollea horizontal dentro de su contenedor si no cabe.
   El wrapper vive FUERA de #tabla (los swaps htmx reemplazan la tabla, el scroll sobrevive). */
/* 04-sep (rendimiento): el "chrome" de tarjeta (borde, radio, sombra) va en el WRAPPER y no en la
   tabla: border-radius + overflow:hidden sobre una tabla de 873 filas obligaba a recortar toda la tabla
   en cada paso de scroll (200 ms por frame medidos; 50 ms sin ese recorte). El wrapper ya recorta por
   ser el scroller, así que el resultado visual es el mismo. */
.tabla-scroll{overflow-x:auto;border:1px solid var(--border);border-radius:var(--r-card);box-shadow:var(--shadow);background:var(--surface)}
.tabla-scroll .grid{width:max-content;min-width:100%;border:0;border-radius:0;box-shadow:none;overflow:visible}
/* Centinela de la carga por tandas del inventario (#inv-mas): una fila discreta con un giro. */
.inv-mas td{text-align:center;color:var(--muted);font-size:13px;padding:10px 12px}
.inv-giro{display:inline-block;width:12px;height:12px;margin-right:8px;vertical-align:-2px;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:inv-giro .8s linear infinite}
@keyframes inv-giro{to{transform:rotate(360deg)}}
.tabla-scroll .grid th,.tabla-scroll .grid td{white-space:nowrap}
/* Barra horizontal FLOTANTE de la tabla del inventario (03-sep, pedido Alejandro; corregido el mismo día:
   la quería en la ZONA INFERIOR, no arriba): con 870 fichas la barra nativa queda al final de la tabla;
   esta réplica va justo DEBAJO de la tabla con position:sticky; bottom:0 → mientras la tabla siga más
   abajo se queda pegada al borde inferior de la pantalla y, al llegar al final, se acomoda en su sitio.
   Sincronizada con la real en los dos sentidos (app.js invScrollBarra). Solo aparece si la tabla
   desborda a lo ancho; entonces la barra nativa del wrapper se oculta (no dos barras al final). */
/* 04-sep (pedido Alejandro: "que se integre un frame en esa parte inferior únicamente para la barra"):
   la barra ya no flota como una línea encima de las filas; va dentro de un MARCO propio, un dock opaco
   pegado al borde inferior del área de contenido, a todo el ancho (los márgenes negativos son el
   padding de .content), con sombra hacia arriba, rótulo a la izquierda y la pista de scroll a la
   derecha (pulgar grueso y redondeado; naranja al pasar el mouse). Al final de la tabla se acomoda en
   su sitio (sticky). El JS lo muestra con display:flex solo si la tabla desborda. bottom:-16px porque el
   sticky se mide contra la caja de contenido de .content y su padding inferior es 16px: con 0 quedaba
   una franja de 16px por debajo del marco con filas asomando. */
.tabla-scroll-fija{position:sticky;bottom:-16px;z-index:3;display:none;align-items:center;gap:14px;margin:10px -22px -16px;padding:8px 22px;background:var(--surface);border-top:1px solid var(--border);box-shadow:0 -8px 18px rgba(0,0,0,.10)}
[data-theme="oscuro"] .tabla-scroll-fija{box-shadow:0 -8px 18px rgba(0,0,0,.4)}
.tsf-rotulo{display:inline-flex;align-items:center;gap:7px;font-size:12.5px;font-weight:600;letter-spacing:.2px;color:var(--muted);white-space:nowrap;user-select:none}
.tsf-track{flex:1;min-width:0;overflow-x:auto;overflow-y:hidden;height:14px;border-radius:7px}
.tsf-track>div{height:1px}
.tsf-track::-webkit-scrollbar{height:14px}
.tsf-track::-webkit-scrollbar-track{background:var(--surface-2);border:1px solid var(--border);border-radius:7px}
.tsf-track::-webkit-scrollbar-thumb{background:var(--muted);border:3px solid var(--surface-2);border-radius:7px}
.tsf-track::-webkit-scrollbar-thumb:hover{background:var(--accent)}
/* EL MARCO NO SE SALE DE LA PANTALLA (13-oct-2026, 0.163, Alejandro: «Sí» a que el Inventario no desborde a lo ancho en el
   teléfono). Sus márgenes negativos son el padding de .content en la PC (22 px a los lados, 16 abajo); en el teléfono .content
   lleva 10 px a los lados y 24 abajo (≤ 720 px sin sesión de teléfono: 10 en todo), así que el marco sobraba 12 px por la derecha
   (377 px de ancho en 353 útiles a 360) y TODA la página se deslizaba de lado con su barra naranja abajo. Aquí los márgenes y el
   bottom del sticky siguen al padding de cada caso (a ras de los bordes, sin filas asomando por debajo). */
@media (max-width:720px){
  .tabla-scroll-fija{margin:10px -10px -10px;padding:8px 10px;bottom:-10px}
}
body[data-movil] .tabla-scroll-fija{margin:10px -10px -24px;padding:8px 10px;bottom:-24px}
.tabla-scroll.sin-barra{scrollbar-width:none}
.tabla-scroll.sin-barra::-webkit-scrollbar{display:none}

/* Header de tabla: tinte suave + texto de marca (adiós bloque morado sólido) */
.grid th{background:var(--th-bg);color:var(--th-text);text-align:left;padding:8px 12px;font-size:12.5px;text-transform:uppercase;letter-spacing:.04em;font-weight:700;border-bottom:2px solid var(--violet);font-family:var(--font-head)}
/* Lupa de autofiltro por columna (Inventario, pedido 06-ago — como el autofiltro del Excel).
   El input hereda el uppercase del th → se resetea; con filtro activo la lupa tinta de violeta. */
.th-flt{display:inline-flex;align-items:center;gap:5px}
.th-lupa{background:none;border:none;padding:0;cursor:pointer;color:inherit;opacity:.45;line-height:0}
.th-lupa:hover{opacity:1}
.th-flt.on .th-lupa{opacity:1;color:var(--violet)}
.th-lupa .ic16{width:13px;height:13px}
.th-filtro{font-size:12px;padding:3px 7px;min-width:110px;max-width:160px;font-weight:400;text-transform:none;letter-spacing:normal}
/* Menú de autofiltro tipo Excel en los encabezados del inventario (04-sep, pedido Alejandro). Vive fuera
   de la tabla (#th-menu, position:fixed) y se arma por JS para la columna pulsada. */
.th-lbl{white-space:nowrap}
.th-ord{font-size:9px;color:var(--accent-text);margin-left:1px}
.th-flt.sorted .th-lbl{color:var(--accent-text)}
.th-menu{position:fixed;z-index:60;width:290px;display:flex;flex-direction:column;background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);box-shadow:var(--shadow-md);padding:6px;font-size:13px;font-weight:400;text-transform:none;letter-spacing:normal;color:var(--text);font-family:var(--font-ui)}
.thm-tit{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);padding:4px 8px 6px}
.thm-it{display:flex;align-items:center;gap:8px;width:100%;text-align:left;background:none;border:0;padding:6px 8px;border-radius:6px;cursor:pointer;color:var(--text);font:inherit}
.thm-it:hover{background:var(--surface-2)}
.thm-it.on{color:var(--accent-text);font-weight:600}
.thm-ic{width:14px;text-align:center;font-size:11px;color:var(--muted);flex:none}
.thm-it.on .thm-ic{color:var(--accent-text)}
.thm-sep{border-top:1px solid var(--border);margin:6px 0}
.thm-buscar{width:100%;font-size:13px;padding:5px 8px;margin-bottom:6px;font-weight:400;text-transform:none;letter-spacing:normal}
.th-menu[hidden]{display:none}
.thm-lista{flex:0 1 auto;min-height:60px;max-height:260px;overflow:auto;padding:2px 4px}
.thm-lista label{display:flex;gap:8px;align-items:center;padding:3px 4px;border-radius:5px;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.thm-lista label:hover{background:var(--surface-2)}
.thm-lista label.todo{font-weight:600;border-bottom:1px dashed var(--border);border-radius:0;margin-bottom:3px;padding-bottom:5px}
.thm-lista input[type="checkbox"]{flex:none;margin:0}
.thm-mas{color:var(--muted);font-size:12px;padding:6px 8px}
.grid td{padding:6px 12px;border-top:1px solid var(--border)}
/* ENCABEZADOS PEGADOS de las tablas de registro (24-sep-2026, pedido de Alejandro): al bajar por Ventas, Compras, Devoluciones,
   Cuentas (cobrar y pagar), Gastos, Clientes, Proveedores y Auditoría la fila de encabezado se queda pegada arriba. Estas tablas
   van directas en .content (sin marco de scroll), así que basta position:sticky en las celdas; pero .grid recorta con
   overflow:hidden para redondear las esquinas —y un recorte anula el sticky—, por eso estas nueve pasan a bordes separados y
   redondean sus esquinas celda a celda. top:-16px = el padding de .content: la fila queda a ras del borde, como la cabecera del
   Inventario. El Inventario en modo Tabla vive dentro de un marco con scroll horizontal y lo resuelve app.js (invTheadPegado)
   desplazando cada celda del encabezado con --thead-dy. La flecha «volver arriba» de estos módulos también la pone app.js.
   0.158 (pedido de Alejandro): entra la tabla de Reportes → «Existencias por umbral» (#rep-exist-tabla), que también va directa en
   .content; como lleva pie («Valor total…»), las esquinas de abajo las redondea su tfoot y no la última fila. En el teléfono
   (≤ 720 px o sesión de teléfono) .content tiene 10 px de padding: el encabezado se pega en -10px para quedar a ras. */
:is(#ventas-list,#compras-list,#devols-list,#cxc-list,#cxp-list,#gastos-list,#tabla-cli,#tabla-prov,#audit-list,#rep-exist-tabla){overflow:visible;border-collapse:separate;border-spacing:0}
:is(#ventas-list,#compras-list,#devols-list,#cxc-list,#cxp-list,#gastos-list,#tabla-cli,#tabla-prov,#audit-list,#rep-exist-tabla) th{position:sticky;top:-16px;z-index:3}
:is(#ventas-list,#compras-list,#devols-list,#cxc-list,#cxp-list,#gastos-list,#tabla-cli,#tabla-prov,#audit-list,#rep-exist-tabla) th:first-child{border-top-left-radius:calc(var(--r-card) - 1px)}
:is(#ventas-list,#compras-list,#devols-list,#cxc-list,#cxp-list,#gastos-list,#tabla-cli,#tabla-prov,#audit-list,#rep-exist-tabla) th:last-child{border-top-right-radius:calc(var(--r-card) - 1px)}
:is(#ventas-list,#compras-list,#devols-list,#cxc-list,#cxp-list,#gastos-list,#tabla-cli,#tabla-prov,#audit-list,#rep-exist-tabla) tbody tr:first-child td{border-top:0}
:is(#ventas-list,#compras-list,#devols-list,#cxc-list,#cxp-list,#gastos-list,#tabla-cli,#tabla-prov,#audit-list,#rep-exist-tabla) tbody tr:last-child td:first-child{border-bottom-left-radius:calc(var(--r-card) - 1px)}
:is(#ventas-list,#compras-list,#devols-list,#cxc-list,#cxp-list,#gastos-list,#tabla-cli,#tabla-prov,#audit-list,#rep-exist-tabla) tbody tr:last-child td:last-child{border-bottom-right-radius:calc(var(--r-card) - 1px)}
#rep-exist-tabla:has(tfoot) tbody tr:last-child td:is(:first-child,:last-child){border-radius:0}
#rep-exist-tabla tfoot tr:last-child td:first-child{border-bottom-left-radius:calc(var(--r-card) - 1px)}
#rep-exist-tabla tfoot tr:last-child td:last-child{border-bottom-right-radius:calc(var(--r-card) - 1px)}
body[data-movil] :is(#ventas-list,#compras-list,#devols-list,#cxc-list,#cxp-list,#gastos-list,#tabla-cli,#tabla-prov,#audit-list,#rep-exist-tabla) th{top:-10px}
@media (max-width:720px){:is(#ventas-list,#compras-list,#devols-list,#cxc-list,#cxp-list,#gastos-list,#tabla-cli,#tabla-prov,#audit-list,#rep-exist-tabla) th{top:-10px}}
#tabla thead th{position:relative;z-index:5;transform:translateY(var(--thead-dy,0px))}
/* Anchos gobernados: columnas de contenido acotado fijas, la columna elástica (Nombre) absorbe el resto */
.grid th.c-xs{width:80px}
.grid th.c-sm{width:110px}
.grid th.c-md{width:150px}
/* Icono de la categoría en la tabla del Inventario (0.157): a la izquierda del nombre, en el segundo color de la marca. Mide
   15 px —menos que el renglón—, así la fila no crece; con «Tamaño» 85 · 75 · Ajustar encoge con la tabla (zoom). */
.grid td.td-cat svg.ico-cat{width:15px;height:15px;margin-right:6px;vertical-align:-3px;fill:none;stroke:currentColor;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round;color:var(--violet);overflow:visible}
.grid th.c-lg{width:200px}
.grid td.num,.grid th.num{white-space:nowrap}
/* Números de tabla con peso medio (pedido tipografía 2026-08-11): las cifras son lo que el ojo
   busca en un ERP — medio paso más de grosor las levanta sin gritar como una negrita plena. */
.grid td.num{font-weight:500;font-variant-numeric:tabular-nums}
.grid tbody tr:hover{background:var(--row-hover)}
.grid .num{text-align:right;font-variant-numeric:tabular-nums}
.grid .mono{font-family:var(--font-mono);color:var(--muted);font-size:13px}
.grid .nombre{font-weight:600}
.grid tfoot td{border-top:1px solid var(--border);background:var(--surface)}

.muted{color:var(--muted)}
.ok{color:var(--ok);font-weight:600}
.no{color:var(--no);font-weight:600}
/* Estado vacío rico: icono del módulo atenuado + título + pista */
.vacio{text-align:center;color:var(--muted);padding:44px 20px}
.vacio .vz-ic{display:block;margin:0 auto 10px}
.vacio .vz-ic svg{width:40px;height:40px;opacity:.45}
.vacio .vz-ic .ic-b{fill:var(--icon-1)}
.vacio .vz-ic .ic-a{fill:var(--icon-2)}
.vacio b{display:block;color:var(--text);font-size:14px;margin-bottom:2px}
.badge{display:inline-block;padding:2px 9px;border-radius:20px;font-size:11px;font-weight:700;border:1px solid transparent}
.b-prod{background:color-mix(in srgb,var(--violet) 14%,transparent);color:var(--violet);border-color:color-mix(in srgb,var(--violet) 28%,transparent)}
.b-serv{background:rgba(37,150,190,.16);color:var(--info);border-color:rgba(37,150,190,.3)}
.b-com{background:rgba(238,140,42,.12);color:var(--accent-text);border-color:rgba(238,140,42,.3)}
.tabla-foot{color:var(--muted);font-size:11px}
.tabla-foot .hint{float:right;opacity:.7}
code{background:var(--surface-2);padding:2px 6px;border-radius:5px}

/* --- Edición inline estilo Excel --- */
.cell{cursor:pointer;position:relative}
.cell:hover{box-shadow:inset 0 0 0 2px var(--accent)}
td.editing{padding:2px 6px}
.cell-input{width:100%;box-sizing:border-box;border:1px solid var(--accent);border-radius:5px;padding:4px 8px;background:var(--surface);color:var(--text);font:inherit}
.cell-input:focus{outline:none;box-shadow:var(--ring)}
.num .cell-input{text-align:right}
/* Lector vivo bajo el input de un precio gravable: "= 116,00 con I.V.A." mientras se teclea la base.
   position:absolute para NO alterar la altura de la fila (el ancho uniforme de fila es un requisito). */
td.editing{position:relative}
.cell-hint{position:absolute;right:6px;top:100%;z-index:5;margin-top:2px;padding:2px 7px;border-radius:5px;
  background:var(--surface-2);border:1px solid var(--border);color:var(--muted);font-size:11px;white-space:nowrap;
  box-shadow:0 4px 12px rgba(0,0,0,.16)}

/* Precio de MOSTRADOR (I.V.A. incluido) en el inventario: marca discreta que distingue un gravable de
   un exento sin robar ancho ni partir la fila en dos líneas. El número sigue siendo el único grande.
   line-height:0 + top en vez de vertical-align:super — "super" AGRANDA la caja de línea y dejaba las
   filas gravables 1px más altas que las exentas (la altura uniforme de fila es un requisito). */
/* ⚠️ El content va en DOS strings: "\00a0c/IVA" en uno solo hacía que la 'c' se consumiera como
   sexto dígito HEX del escape (\00a0c = U+0A0C, un carácter gurmukhi) → se renderizaba "□/IVA"
   con tofu. Bug cosmético preexistente cazado en la tanda tipográfica 2026-08-11. */
.pv-iva::after{content:"\00a0" "c/IVA";font-size:10px;font-weight:600;letter-spacing:.02em;color:var(--muted);
  position:relative;top:-.45em;line-height:0;opacity:.85}

/* Toggle de estado (Disponible/Agotado, Activo/Inactivo): punto-semáforo + anillo al hover = "soy un botón" */
.toggle{position:relative;border:none;border-radius:20px;padding:3px 11px 3px 21px;font-weight:600;font-size:12px;cursor:pointer}
.toggle::before{content:"";position:absolute;left:9px;top:50%;margin-top:-3px;width:6px;height:6px;border-radius:50%;background:currentColor}
.toggle:hover{box-shadow:inset 0 0 0 1px currentColor;filter:none}
.toggle.on{background:rgba(25,135,84,.16);color:var(--ok)}
.toggle.off{background:rgba(192,57,43,.16);color:var(--no)}
/* En oscuro las alfas calculadas para fondo blanco sobresaturan: bajarlas */
[data-theme="oscuro"] .toggle.on{background:rgba(52,211,153,.13)}
[data-theme="oscuro"] .toggle.off{background:rgba(240,101,90,.13)}
.toggle:hover{filter:brightness(1.08)}

/* Acciones por fila: lado a lado (apiladas duplicaban la altura de la fila) */
.acciones{text-align:center;width:72px;white-space:nowrap}
.btn-del{background:transparent;border:none;cursor:pointer;font-size:13px;opacity:.55;border-radius:6px;padding:3px 6px;line-height:1}
.btn-del:hover{opacity:1;background:rgba(192,57,43,.14)}
.btn-edit{background:transparent;border:none;cursor:pointer;font-size:13px;opacity:.55;border-radius:6px;padding:3px 6px;line-height:1}
.btn-edit:hover{opacity:1;background:rgba(238,140,42,.16)}

/* Editor de producto (UserForm) */
.prod-modal{width:min(860px,96vw)}
/* El editor DEBE poder scrollear (el .vm-body genérico esconde el overflow — en pantallas bajas las
   casillas de abajo quedaban inalcanzables, pedido Alejandro 2026-08-01) + botón ⛶ para maximizar. */
.prod-modal .vm-body{overflow-y:auto}
/* Umbrales de existencia SIEMPRE coloreados con su banda (Mínima roja · Promedio/Reorden amarillas ·
   Máxima verde), número incluido. Variantes translúcidas en tema oscuro (el pastel pleno ciega). */
.prod-modal .inp.exist-min{background:#ffc7ce;color:#9c0006;font-weight:600}
.prod-modal .inp.exist-prom{background:#fff9b2;color:#9c8000;font-weight:600}
.prod-modal .inp.exist-max{background:#c6efce;color:#006100;font-weight:600}
[data-theme="oscuro"] .prod-modal .inp.exist-min{background:rgba(156,0,6,.38);color:#ffb3ba}
[data-theme="oscuro"] .prod-modal .inp.exist-prom{background:rgba(156,128,0,.33);color:#ffe98a}
[data-theme="oscuro"] .prod-modal .inp.exist-max{background:rgba(0,97,0,.38);color:#8ee6a8}
/* Imagen referencial: preview en el editor + espacio en la Caja */
.prod-img-prev{width:126px;height:126px;object-fit:contain;border:1px solid var(--border);border-radius:var(--r-card);background:#fff;flex:none}
.prod-img-prev.sin-img{background:var(--surface-2);border-style:dashed}
/* Captura arriba (no centrada): las celdas quedan a la par del tope de la imagen (ajuste 03-jul) */
.v-add-wrap{display:flex;gap:14px;align-items:flex-start}
.v-add-rows{flex:1;min-width:0}
/* Imagen referencial en la Caja: CUADRADA y protagonista. El margen negativo la sube a la franja
   del título "Agregar producto" para que las líneas del segmento queden casi a ras del cuadro. */
.v-img-box{width:160px;height:160px;flex:none;display:flex;align-items:center;justify-content:center;border:1px dashed var(--border);border-radius:var(--r-card);background:var(--surface-2);overflow:hidden;margin-top:-27px}
/* Fila ÚNICA de captura: Artículo elástico (la celda más larga), el resto compacto para que
   TODO quepa en una línea incluso con el selector "Vender" visible (fraccionables). */
.v-add-linea{gap:6px}
.v-add-linea .btn-primary,.v-add-linea .btn-ghost{padding-left:11px;padding-right:11px}
.v-fld-art{flex:999 1 220px;min-width:220px}   /* 02-oct: base 240 → 220 (su mínimo): la Tarifa segmentada es 7 px más ancha que la lista que reemplaza */
/* El <select> de Artículo llena TODA la celda (por defecto se dimensiona por su contenido) →
   la celda se ve realmente larga y el borde del botón "+ Nuevo Producto" calza con el del select */
.v-fld-art #producto-wrap,.v-fld-art #cproducto-wrap{display:block;width:100%}
.v-fld-art #producto-wrap select,.v-fld-art #cproducto-wrap select{width:100%}
/* ─── COMBO DE ARTÍCULO PROPIO de la Caja (05-sep): el <select> #v-producto queda como estado oculto (1×1, sin
   opacidad: Playwright y el escáner lo siguen viendo) y el combo .v-combo pinta nombre + cápsula «N Exist.» ─── */
.v-fld-art #producto-wrap,.v-fld-art #cproducto-wrap{position:absolute;width:1px;height:1px;overflow:hidden;opacity:0;pointer-events:none;left:0;bottom:0}
.v-fld-art{position:relative}
.v-combo{position:relative;width:100%}
.v-combo-btn{width:100%;display:flex;align-items:center;gap:8px;text-align:left;cursor:pointer;padding-right:28px;position:relative;font-weight:600;font:inherit;font-weight:600}
.v-combo-btn .v-combo-txt{flex:1;min-width:0;display:flex;align-items:center;gap:8px;overflow:hidden}
.v-combo-nom{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.v-combo-chev{position:absolute;right:10px;top:50%;transform:translateY(-50%);color:#999;font-size:12px;line-height:1}
.v-combo-list{position:absolute;left:0;right:0;top:calc(100% + 4px);z-index:30;background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);box-shadow:var(--shadow-md);padding:6px;display:flex;flex-direction:column;gap:6px;min-width:min(640px,calc(100vw - 48px))}
.v-combo-list[hidden]{display:none}
.v-combo-q{width:100%;height:32px;font-weight:400}
/* Búsqueda Alfabética | Coincidente (05-sep): alternador junto a cada buscador y sombreado de lo buscado */
.v-combo-top{display:flex;align-items:center;gap:6px}
.v-combo-top .v-combo-q{flex:1;width:auto;min-width:0}
.seg-bus{flex:none}
.seg-bus button{padding:3px 8px;font-size:11.5px}
/* CABECERA PEGADA del Inventario (23-sep-2026, pedido de Alejandro): al bajar, el LANZADOR de módulos se va con el scroll
   (en este módulo vive dentro de .content) y la cabecera del módulo —barra de filtros/botones + marco de inventarios— queda
   pegada bajo el membrete, sobre la tabla. top:-16px = el padding superior de .content; la propia cabecera repone esos 16 px
   con fondo para tapar las filas que pasan por debajo. .pegada (app.js, por un centinela) enciende la sombra. */
.content-lanzador>.launcher{margin:-16px -22px 16px}
/* 24-sep-2026 — REBOTE reportado por Alejandro con la 0.141 («al bajar solo un poco la barra se retrae y se extiende de manera frenética»):
   al plegarse la barra el contenido de abajo sube ~200 px y el ANCLAJE DE SCROLL de Chromium compensaba moviendo el scroll hacia arriba,
   lo que volvía a mostrar el centinela → se extendía → el scroll bajaba → se plegaba… (17 cambios de clase por segundo en la prueba 2d).
   Sin anclaje el scroll se queda donde el usuario lo dejó y el plegado es un solo movimiento. */
.content-lanzador{overflow-anchor:none}
.inv-centinela{height:1px;margin-top:-1px}
.inv-cabecera{position:sticky;top:-16px;z-index:6;background:var(--bg);padding-top:16px;margin-top:-16px;transition:box-shadow .15s}
.inv-cabecera.pegada{box-shadow:0 12px 16px -10px rgba(0,0,0,.28)}
.inv-bus{margin-left:-4px}
/* En el CATÁLOGO la fila de chips de categoría / orden / tamaño también se pega, justo debajo de la cabecera (decisión de
   Alejandro, 23-sep). Vive dentro de la tabla: el marco .tabla-scroll deja de ser contenedor de scroll en esa vista
   (overflow visible) para que el sticky se mida contra .content; el alto de la cabecera llega en --inv-cab-h (app.js). */
.tabla-scroll:has(>.inv-cat-tabla){overflow:visible}
.inv-cat-pleg{position:sticky;top:calc(var(--inv-cab-h,120px) - 16px);z-index:4;background:var(--surface);padding-top:12px;margin-top:-12px}
/* RETRAER / EXTENDER la cabecera pegada (24-sep-2026, pedido de Alejandro): un botón en la esquina derecha del marco de
   inventarios, que nace junto con la flecha «volver arriba» (solo con la cabecera pegada), pliega la barra de filtros/botones
   —y en Catálogo la fila de categorías/orden/tamaño— dejando el marco (pestañas + valor) como asa para volver a extender.
   .plegada la pone app.js (invPlegar) y se recuerda en la PC; arriba del todo (cabecera sin pegar) todo se muestra aunque la
   preferencia siga marcada. El plegado es un grid 1fr→0fr (alto REAL animado, sin max-height a ciegas); el hijo recorta con
   6 px de aire a los lados para no cortar las sombras de los botones, y la visibilidad se apaga al terminar de plegar. */
.inv-plegable,.inv-cat-pleg{display:grid;grid-template-rows:1fr;transition:grid-template-rows .22s ease}
.inv-plegable>div,.inv-cat-pleg>div{min-height:0;overflow:hidden;margin:0 -6px;padding:0 6px}
.inv-cabecera.pegada.plegada .inv-plegable,.inv-cabecera.pegada.plegada~.tabla-scroll .inv-cat-pleg{grid-template-rows:0fr}
.inv-cabecera.pegada.plegada .inv-plegable>div,.inv-cabecera.pegada.plegada~.tabla-scroll .inv-cat-pleg>div{visibility:hidden;transition:visibility 0s .22s}
.btn-plegar{flex:none;width:30px;height:30px;margin-right:-6px;padding:0;border-radius:9px;border:1px solid var(--border);background:var(--surface-2);color:var(--muted);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;opacity:0;visibility:hidden;transition:opacity .15s,visibility 0s .15s,color .12s,border-color .12s,background .12s}
.inv-cabecera.pegada .btn-plegar{opacity:1;visibility:visible;transition:opacity .15s,color .12s,border-color .12s,background .12s}
.btn-plegar svg{width:18px;height:18px;display:block;transition:transform .22s ease}
.btn-plegar:hover{color:var(--text);border-color:var(--border-hover);background:var(--surface)}
.inv-cabecera.plegada .btn-plegar svg{transform:rotate(180deg)}
@media (prefers-reduced-motion:reduce){.inv-plegable,.inv-cat-pleg,.btn-plegar,.btn-plegar svg{transition:none}}
/* Botón «volver arriba»: nace cuando el lanzador de módulos se oculta (cabecera pegada) y sube al principio de un golpe. */
.btn-arriba{position:fixed;right:26px;bottom:72px;z-index:7;width:46px;height:46px;border-radius:50%;border:0;background:var(--accent);color:var(--accent-ink);box-shadow:0 8px 22px rgba(0,0,0,.28);cursor:pointer;display:flex;align-items:center;justify-content:center;animation:arriba-entra .18s ease-out}
.btn-arriba[hidden]{display:none}
.btn-arriba svg{width:22px;height:22px;display:block}
.btn-arriba:hover{filter:brightness(1.08)}
@keyframes arriba-entra{from{opacity:0;transform:translateY(8px) scale(.9)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){.btn-arriba{animation:none}}
/* FLECHA «VOLVER ARRIBA» EN EL TELÉFONO (12-oct-2026, 0.163, Alejandro: «el botón para volver a la parte superior de los módulos no
   está integrado a la aplicación móvil»): la misma flecha (public/app.js → arribaGenerico; en el Inventario #inv-arriba), del tamaño
   del pulgar (52 px), pegada a la esquina inferior derecha (respetando el borde redondeado / la barra de gestos) y, si asoma el total
   fijo de la lista del día de Reportes, justo encima de él (--arriba-pie, lo mide app.js → arribaSobrePie). */
body[data-movil] .btn-arriba{right:calc(12px + env(safe-area-inset-right, 0px));bottom:calc(16px + env(safe-area-inset-bottom, 0px) + var(--arriba-pie, 0px));width:52px;height:52px;z-index:8;transition:bottom .15s ease}
body[data-movil] .btn-arriba svg{width:24px;height:24px}
@media (prefers-reduced-motion:reduce){body[data-movil] .btn-arriba{transition:none}}
body[data-movil] .content-lanzador>.launcher{margin:-10px -10px 10px}
mark.hl{background:color-mix(in srgb,var(--accent) 40%,transparent);color:inherit;border-radius:3px;padding:0 1px}
.v-combo-items{max-height:min(46vh,380px);overflow:auto}
.v-combo-it{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:6px 8px;border-radius:7px;cursor:pointer;font-size:14px}
.v-combo-it:hover,.v-combo-it.sel{background:var(--row-hover)}
.v-combo-it.off{opacity:.5;cursor:not-allowed}
.v-combo-it .cat-stock{flex:none}
.v-combo-vacio{padding:10px;color:var(--muted);font-size:13px}
.v-combo-tipo{flex:none;font-size:11px;font-weight:600;color:var(--muted);border:1px solid var(--border);border-radius:999px;padding:1px 8px;text-transform:capitalize}
.fn-row .v-combo-btn{height:34px}
.v-lbl-row{display:flex;align-items:flex-end;gap:10px;width:100%}
.v-lbl-row .btn-primary{margin-left:auto;height:22px;padding:1px 10px;font-size:11px;display:inline-flex;align-items:center;white-space:nowrap;box-shadow:0 2px 5px rgba(238,140,42,.3)}
.v-add-linea .inp.num{width:82px}
/* 0.158 (ajuste 5): en monto el «Descuento» de la fila de captura cortaba desde «99999,99» al escribirlo (con las flechas del
   campo numérico le quedan ~45 px al texto de 12,5 px): crece con lo que lleva (field-sizing:content) desde sus 82 px hasta lo
   que pide «999999,99» (100 px medidos; el tope va en em para que alcance también con la letra de 16 px del teléfono). A 1366 y
   a 1280 la fila sigue en un renglón: cede el ancho el buscador de producto. align-self: el .fld es una columna flex y estiraría
   la casilla a todo su ancho (en el teléfono, al tope aun vacía). */
.v-add-linea #v-desc{width:auto;field-sizing:content;min-width:82px;max-width:8.3em;align-self:flex-start}
.v-add-linea .inp-cod{width:114px}
.v-add-linea #v-inv-filtro{width:112px;padding-left:8px;padding-right:22px;background-position:right 7px center}
/* Tarifa: el segmentado P.V.P | P.V.M del Catálogo, a la altura de las celdas de la fila de captura */
#v-tar-wrap .seg-tar{gap:2px}
#v-tar-wrap .seg-tar button{padding:6px 5px;font-size:12px}   /* compacto: con el tipo dentro de Cantidad (.v-cant.con-tv) la fila debe seguir cabiendo en UN renglón a 1366 px */
/* Compras: Costo actual / Costo promedio (solo lectura). Sin existencia previa el promedio "arranca" en
   esta compra: borde punteado como seña (el title lo explica). */
.v-add-linea #l-costo-act,.v-add-linea #l-costo-prom{width:96px}
#l-costo-prom.c-prom-arranca{border-style:dashed}
.v-add-linea .disp-hint{min-width:0;font-size:11px;align-self:auto}
.v-img-box img{display:none;max-width:100%;max-height:100%;object-fit:contain;background:#fff}
.v-img-box.con-img{border-style:solid;background:#fff}
.v-img-box.con-img img{display:block}
.v-img-box.con-img #v-img-ph{display:none}
#v-img-ph{font-size:11px;text-align:center;padding:4px}
.prod-modal .vm-body #prod-msg:not(:empty){margin:0 20px 8px}
.prod-modal .fld .inp.num{width:120px}

/* Formulario de alta (+ Nuevo) */
.form-nuevo{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);padding:14px;margin-bottom:14px;box-shadow:var(--shadow-md)}
/* flex-end: inputs con label y botones sin label comparten la LÍNEA BASE (nada flota a media altura) */
.fn-row{display:flex;gap:8px;align-items:flex-end;flex-wrap:wrap;row-gap:10px}
.fn-row .inp,.fn-row select.inp{height:34px}
.fn-row .btn-primary,.fn-row .btn-ghost{height:34px;display:inline-flex;align-items:center}
.vm-sec .fn-row + .fn-row{margin-top:10px}
.inp{background:var(--surface-2);border:1px solid var(--border);border-radius:var(--r-ctl);padding:8px 10px;color:var(--text);font:inherit;box-shadow:inset 0 1px 2px var(--inp-sombra)}
.inp:focus{outline:none;border-color:var(--accent);box-shadow:var(--ring)}
.inp::placeholder{color:var(--muted);opacity:1}
.inp.grow{flex:1;min-width:180px}
.inp.num{width:110px;text-align:right}
/* Selects (comboboxes) con aspecto de botón: borde definido, chevron propio, hover de marca */
select.inp{appearance:none;-webkit-appearance:none;cursor:pointer;font-weight:600;padding-right:30px;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23999' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
  background-repeat:no-repeat;background-position:right 10px center}
select.inp:hover{border-color:var(--border-hover)}
select.inp:focus{border-color:var(--accent);box-shadow:var(--ring)}
.inp:disabled{opacity:.45;cursor:not-allowed}

/* Pantalla de Configuración: grid de cards en 2 columnas (adiós media pantalla vacía) */
/* Ajustes en ACORDEÓN (11-sep-2026, opción C del lienzo «Ajustes reagrupado» de Alejandro): UNA columna a todo lo ancho,
   cinco grupos con índice pegado arriba, cada frame es un <details> con cabecera (título · resumen · chevron) y mide lo
   que su contenido; los campos del form en rejilla de tres con la ayuda debajo; Guardar pegado abajo. */
.cfg-page{display:flex;flex-direction:column;gap:12px;max-width:none}
.cfg-form{display:contents}
.cfg-nav{display:flex;gap:8px;flex-wrap:wrap;position:sticky;top:-16px;z-index:5;background:var(--bg);padding:16px 0 10px;margin:-16px 0 -2px}
.cfg-nav .chip{padding:5px 13px;font-size:13px;text-decoration:none}
.cfg-grupo{font-family:var(--font-head);font-weight:700;font-size:12.5px;letter-spacing:.09em;text-transform:uppercase;color:var(--muted);margin:10px 0 -2px 2px;display:flex;align-items:center;gap:10px;scroll-margin-top:64px}
.cfg-grupo::after{content:"";flex:1;height:1px;background:var(--border)}
.cfg-acc-item{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);box-shadow:var(--shadow-md);min-width:0}
.cfg-acc-item>summary{list-style:none;display:flex;align-items:center;gap:14px;padding:12px 18px;cursor:pointer;user-select:none;border-radius:var(--r-card)}
.cfg-acc-item>summary::-webkit-details-marker{display:none}
.cfg-acc-item>summary:hover{background:var(--row-hover)}
.cfg-acc-item>summary .vm-sec-t{margin:0;flex:none;min-width:250px}
.cfg-acc-item .acc-res{flex:1;min-width:0;font-size:13px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cfg-acc-item .acc-chev{flex:none;color:var(--muted);display:flex;transition:transform .15s}
.cfg-acc-item[open]>summary .acc-chev{transform:rotate(180deg)}
.cfg-acc-item[open]>summary{border-bottom:1px solid var(--border);border-radius:var(--r-card) var(--r-card) 0 0}
.cfg-acc-item.acc-animando{will-change:height}
.cfg-acc-item .acc-chev{transition:transform .24s cubic-bezier(.2,.7,.2,1)}
.cfg-acc-item .cfg-card,.cfg-acc-item>div>.cfg-card{border:0;box-shadow:none;border-radius:0;background:transparent;padding:12px 18px 16px}
.cfg-acc-item .cfg-card>.vm-sec-t{display:none}
.cfg-filas{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px 24px;align-items:start}
.cfg-filas.dos{grid-template-columns:repeat(2,minmax(0,1fr))}
.cfg-filas .fld-row{display:flex;flex-direction:column;align-items:stretch;gap:5px;margin:0}
.cfg-filas .fld-row>span:first-child{font-size:12.5px;font-weight:600}
.cfg-filas .fld-row.ancho{grid-column:1/-1}
.fld-help{font-size:12px;color:var(--muted);line-height:1.35}
.cfg-nota{margin:10px 0 0;font-size:13px;color:var(--muted);display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.cfg-acc .cfg-card .fld-row>.btn-primary,.cfg-acc .cfg-card .fld-row>.btn-ghost,.cfg-acc .cfg-card .fld-row>.btn-danger{justify-self:start}
.cfg-acc .cfg-card .fld-row>.inp{max-width:560px}
.cfg-dos-col{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px 28px;align-items:start}
@media (max-width:900px){.cfg-filas,.cfg-filas.dos,.cfg-dos-col{grid-template-columns:1fr}.cfg-acc-item .acc-res{display:none}.cfg-acc-item>summary .vm-sec-t{min-width:0}}
.cfg-grid{display:flex;flex-direction:column;gap:16px;min-width:0}
.cfg-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);padding:16px 18px;box-shadow:var(--shadow-md);margin:0;min-width:0}
.cfg-card .vm-sec-t{margin-bottom:10px}
.cfg-actions{position:sticky;bottom:-16px;z-index:5;display:flex;justify-content:flex-end;gap:8px;padding:10px 0 16px;background:linear-gradient(to top,var(--bg) 75%,transparent);margin:0 0 -16px}
.cfg-msg{margin:0}
.fld-row{display:grid;grid-template-columns:180px 1fr;align-items:center;gap:12px;margin-bottom:10px;font-size:14px;color:var(--muted)}
.fld-row .inp{width:100%}
.cfg-ok{background:rgba(25,135,84,.14);color:var(--ok);border-radius:7px;padding:9px 12px;margin:0 0 14px;font-weight:600}
.cfg-err{background:rgba(192,57,43,.14);color:var(--no)}

/* Respaldos (card en Configuración) */
.resp-estado{margin:0 0 12px;font-size:13px}
.resp-ok{color:var(--ok);font-weight:600}
.resp-warn{color:var(--accent-text);font-weight:600}
.resp-err{color:var(--no);font-weight:600}
.resp-tabla{width:100%;border-collapse:collapse;margin-top:12px;font-size:13px}
.resp-tabla th{text-align:left;color:var(--muted);font-weight:600;padding:4px 8px;border-bottom:1px solid var(--border)}
.resp-tabla td{padding:5px 8px;border-bottom:1px solid var(--border)}
.resp-tabla .num{text-align:right}
.resp-pie{margin:12px 0 0;font-size:12px;word-break:break-all}

/* Caja (Ventas): correlativo, disponible en vivo, color de tarifa PVP/PVM */
.prox-venta{align-self:center;color:var(--muted);font-size:13px}
.disp-hint{align-self:center;font-size:13px;color:var(--muted);min-width:120px}
.disp-hint.no{color:var(--no);font-weight:600}
.tar-pvp{background:rgba(25,135,84,.16) !important}
.tar-pvm{background:rgba(13,110,253,.18) !important}
[data-theme="oscuro"] .tar-pvm{background:rgba(110,168,254,.26) !important}

/* Formulario MODAL de venta (estilo UserForm de Excel) */
.venta-modal{border:none;border-radius:var(--r-modal);padding:0;width:min(1280px,96vw);max-height:92vh;background:var(--surface);color:var(--text);box-shadow:0 24px 70px rgba(0,0,0,.4)}
.venta-modal.maximized{width:100vw;height:100vh;max-width:100vw;max-height:100vh;border-radius:0}
.venta-modal.maximized .vm-head{border-radius:0}
.venta-modal.maximized form{height:100vh;max-height:100vh}
.inp-cod{width:150px;font-family:var(--font-mono)}
/* El valor escaneado va en mono, pero la invitación al usuario vuelve a la voz de la app */
.inp-cod::placeholder{font-family:var(--font-ui);letter-spacing:0}
.venta-modal::backdrop{background:rgba(0,0,0,.5)}
.venta-modal form{display:flex;flex-direction:column;max-height:92vh}
/* Header del modal en el navy del logo = misma marca del chip .brand (blanco 13.7:1 AA) */
.vm-head{display:flex;align-items:center;gap:12px;padding:14px 20px;background:linear-gradient(135deg,#1d2c50,#0c1730);color:#fff;border-radius:var(--r-modal) var(--r-modal) 0 0;position:relative;overflow:hidden}
/* Membrete «Exy flotante» (06-sep): el patrón del appbar inyectado por app.js en cada .vm-head; los
   hijos reales quedan por encima (el patrón es absolute z-index 0, pointer-events none). */
.vm-head > *:not(.exy-pat){position:relative;z-index:1}
.exy-pat-head .malla{stroke-opacity:.12}
.exy-pat-head .brillo{stroke-width:1.8}.exy-pat-head .halo{stroke-width:4.5}
.vm-head h2{margin:0;font-size:18px;font-family:var(--font-head);font-weight:600}
.vm-head .prox-venta{color:#fff;opacity:.9;font-size:13px}
.vm-head .vm-spacer{flex:1}
.vm-x{background:rgba(255,255,255,.18);color:#fff;border:none;border-radius:8px;width:30px;height:30px;cursor:pointer;font-size:14px}
.vm-x:hover{background:rgba(255,255,255,.3)}
.vm-body{display:flex;flex-direction:column;flex:1;min-height:0;overflow:hidden;padding:0}
.vm-sec{padding:14px 20px;border-bottom:1px solid var(--border)}
.vm-sec-t{font-size:12px;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);margin-bottom:8px;font-weight:700;font-family:var(--font-head)}
/* Layout del modal: fila superior (datos | tasas) + agregar + fila inferior (detalle | resumen fijado) */
.vm-row-top{display:flex;align-items:stretch}
.vm-row-top>.vm-sec{flex:1;border-bottom:1px solid var(--border)}
.vm-row-top>.vm-sec:first-child{flex:1.5;border-right:1px solid var(--border)}
.vm-row-bottom{display:flex;flex:1;min-height:0;align-items:stretch}
.vm-detalle{flex:1;min-width:0;overflow:auto;border-right:1px solid var(--border)}
/* Resumen + pago + acciones: PANEL FIJO en la esquina inferior derecha (ajuste 3).
   El "pegado abajo" va con margin-top:auto en .vm-tot y NO con justify-content:flex-end (19-ago):
   con flex-end, el contenido que no cabía desbordaba por ARRIBA hacia territorio NO scrolleable
   (scrollHeight ni lo cuenta) y "Subtotal:" se pintaba sobre la sección vecina sin que el
   overflow pudiera atraparlo. Con margin-auto el look es idéntico cuando sobra espacio, y cuando
   falta el desborde cae hacia ABAJO, donde el overflow-y:auto sí da scroll y todo queda accesible. */
.vm-foot{width:400px;flex:none;display:flex;flex-direction:column;align-items:flex-end;gap:10px;padding:14px 20px;background:var(--surface-2);border-radius:0 0 var(--r-modal) 0;overflow:hidden}
/* Totales SIEMPRE arriba y quietos (pedido Alejandro: "el Subtotal, IVA y TOTAL se quedan en su
   lugar"). El scroll de los extremos vive DENTRO del cajón (.pago-body), no en el .vm-foot, así el
   pie nunca desborda y los totales no se van de vista al abrir el cajón. */
.vm-foot>.vm-tot{flex:none}
.vm-tot{display:flex;flex-direction:column;align-items:flex-end;gap:3px;text-align:right}
.vm-tot span{color:var(--muted);font-size:14px}
.vm-tot b{color:var(--text);font-variant-numeric:tabular-nums}
/* EL número de la app: etiqueta pequeña + cifra 28px */
.vm-tot .big{display:flex;align-items:baseline;gap:8px}
.vm-tot .tot-lbl{font-size:12px;font-weight:700;letter-spacing:.06em;color:var(--muted)}
.vm-tot .big b{font-size:28px;letter-spacing:-.02em;color:var(--accent-text);font-family:var(--font-display)}
.vm-btns{display:flex;gap:8px;justify-content:flex-end}
/* En los editores (producto/cliente) el pie NO es la columna de 400px de la Caja: es una BARRA
   a lo ancho — nota a la izquierda, botones abajo a la DERECHA (pedido de Alejandro 03-jul). */
.prod-modal .vm-foot,.cli-modal .vm-foot{width:auto;flex-direction:row;align-items:center;border-radius:0 0 var(--r-modal) var(--r-modal)}
/* En los diálogos tipo kardex (Comandas, Mesas, cuenta de mesa — Fase C) el pie tampoco es la
   columna de 400px de la Caja: es una BARRA a lo ancho, si no los botones se salen del dialog. */
.kardex-modal .vm-foot{width:auto;flex-direction:row;align-items:center;border-radius:0 0 var(--r-modal) var(--r-modal)}
/* Checkbox "Imprimir recibo al guardar" (encima de los botones de la Caja) */
.vm-print-auto{display:flex;align-items:center;gap:6px;font-size:13px;color:var(--muted);cursor:pointer;user-select:none}
/* Marcador "Factura con I.V.A." del modal de compras (mismo trato visual) */
.vm-iva{display:flex;align-items:center;gap:6px;font-size:13px;color:var(--muted);cursor:pointer;user-select:none}
/* MsgBox Sí/No (estilo UserForm) */
.msgbox{border:none;border-radius:var(--r-modal);padding:22px 24px;min-width:340px;max-width:min(92vw,560px);background:var(--surface);color:var(--text);box-shadow:0 24px 70px rgba(0,0,0,.4)}
/* Diálogo de elección (0.163): un botón por salida; en el teléfono, apilados y a todo lo ancho */
.msgbox-elegir .vm-btns{flex-wrap:wrap;gap:8px}
@media (max-width:720px){.msgbox-elegir .vm-btns{flex-direction:column;align-items:stretch}.msgbox-elegir .vm-btns button{width:100%}}
.msgbox::backdrop{background:rgba(0,0,0,.5)}
.msgbox-txt{margin:0 0 16px;font-size:15px;white-space:pre-line;line-height:1.45}
/* Aviso de un solo botón (23-sep-2026): reemplaza al alert() nativo con el mismo molde del msgbox. */
.msgbox-aviso .vm-btns{justify-content:flex-end}

/* ── EN VIVO (23-sep-2026, pedido de Alejandro; variantes que ÉL eligió sobre el lienzo de animaciones) ──
   B1 · Deslizar y resaltar: la fila nueva baja desde la cabecera pintada de verde y el color se apaga solo (~2 s).
   A3 · Destello + conteo: la TARJETA del KPI parpadea en verde (sube) o rojo (baja) con un aro, la cifra cuenta hasta la
   nueva (public/vivo.js) y una etiqueta dice cuánto cambió. C2 · Pulso en la barra afectada: lo dibuja Chart.js (views/reportes.js). */
@keyframes vivoFila{0%{transform:translateY(-38%);opacity:.55;background:rgba(21,115,71,.28)}18%{transform:none;opacity:1;background:rgba(21,115,71,.28)}100%{background:transparent}}
tr.vivo-fila>td{animation:vivoFila 2.3s cubic-bezier(.2,.7,.2,1) both}
@keyframes vivoSube{0%{color:var(--ok)}72%{color:var(--ok)}100%{color:inherit}}
@keyframes vivoBaja{0%{color:var(--no)}72%{color:var(--no)}100%{color:inherit}}
.kpi-val.vivo-sube{animation:vivoSube 1.9s ease}
.kpi-val.vivo-baja{animation:vivoBaja 1.9s ease}
/* 24-sep-2026 (pedido de Alejandro): la TARJETA ya no parpadea en verde/rojo; con cada movimiento en vivo TODAS las tarjetas
   laten una vez con el BORDE en el mismo naranja del pulso de la gráfica (.vivo-late, la pone exyReportesVivo). La cifra sí
   conserva su tinte verde/rojo mientras cuenta (.kpi-val.vivo-sube/.vivo-baja) y la etiqueta «▲ +130,00» va A LA DERECHA DE
   LA CIFRA, en la misma fila (.kpi-fila, views/reportes.js), no en la esquina de la tarjeta a la altura del rótulo. */
@keyframes vivoKpi{0%{border-color:var(--border);box-shadow:var(--shadow-md)}18%{border-color:#ee8c2a;background-color:rgba(238,140,42,.10);box-shadow:var(--shadow-md),0 0 0 3px rgba(238,140,42,.35)}100%{border-color:var(--border);box-shadow:var(--shadow-md)}}
.kpi{position:relative}
.kpi.vivo-late{animation:vivoKpi 1.5s ease}
.kpi-fila{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:6px}
.kpi-fila>.kpi-val{margin-top:0}
@keyframes vivoChip{from{opacity:0;transform:translateX(-4px)}to{opacity:1;transform:none}}
.vivo-delta{position:absolute;top:8px;right:10px;font-family:var(--font-head);font-size:11px;font-weight:700;padding:2px 8px;border-radius:999px;font-variant-numeric:tabular-nums;animation:vivoChip .35s ease both;line-height:1.3}
/* 25-sep-2026 (Alejandro: «al aparecer amplían un poco cada tarjeta, y al desaparecer lo hacen de golpe»): la etiqueta ya NO
   entra en el flujo de .kpi-fila (saltaba a una 2.ª línea y las siete tarjetas crecían 23 px; al irse, volvían de golpe). Es una
   CAPA sobre la tarjeta (vivo.js → colocarChip): a la derecha de la cifra, centrada en su fila (top inline) si cabe; si no,
   abajo a la derecha (.abajo). La tarjeta no cambia de alto nunca, ni al entrar ni al salir. */
.vivo-delta.en-fila{position:absolute;right:18px;top:auto;font-size:10.5px;padding:1px 7px;margin:0}
.vivo-delta.en-fila.abajo{bottom:2px}
.vivo-delta.up{background:rgba(21,115,71,.14);color:var(--ok)}
.vivo-delta.down{background:rgba(176,42,30,.12);color:var(--no)}
/* Salida con fundido propio (una animación, no una transición: el fill de vivoChip tapaba cualquier transición). */
@keyframes vivoChipSale{to{opacity:0;transform:translateX(8px)}}
.vivo-delta.se-va{animation:vivoChipSale .45s ease both}
@media (prefers-reduced-motion:reduce){tr.vivo-fila>td,.kpi-val.vivo-sube,.kpi-val.vivo-baja,.kpi.vivo-late,.vivo-delta{animation:none}}

/* ── Modo móvil del administrador (23-sep-2026): página de elección y pie con el modo ── */
.mov-card{max-width:440px}
.mov-opciones{display:flex;flex-direction:column;gap:12px;margin:18px 0 6px}
.mov-op{display:flex;flex-direction:column;align-items:flex-start;gap:4px;text-align:left;padding:14px 16px;border:1.5px solid var(--border);border-radius:var(--r-card);background:var(--surface);color:var(--text);cursor:pointer;font:inherit;position:relative}
.mov-op:hover,.mov-op:focus-visible{border-color:var(--accent-text,#ea580c);outline:none;box-shadow:var(--shadow-md)}
.mov-op.activa{border-color:var(--accent-text,#ea580c)}
.mov-op-t{font-family:var(--font-head);font-weight:700;font-size:17px}
.mov-op-d{color:var(--muted);font-size:13px;line-height:1.4}
.mov-op-actual{position:absolute;top:10px;right:12px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--accent-text,#ea580c)}
.pie-movil{display:inline-block;padding:1px 8px;border-radius:999px;background:rgba(255,255,255,.14);color:#fff;font-weight:600;font-size:11px;text-decoration:none;letter-spacing:.04em}
.pie-movil.ver{background:rgba(234,88,12,.85)}
.chip.chip-solo-ver{background:rgba(234,88,12,.14);color:var(--accent-text,#ea580c);font-weight:700;text-decoration:none}
/* En el teléfono: tablas anchas deslizables, KPIs a dos columnas, barras de filtros que se envuelven */
.tbl-scroll{overflow-x:auto;-webkit-overflow-scrolling:touch;max-width:100%}
.tbl-scroll>table.grid{min-width:640px}
.tbl-scroll>#audit-list{min-width:1180px}   /* Auditoría lleva columnas fijas: sin ancho, el Detalle se aplasta y estira la fila */
.tbl-scroll>#ventas-list{min-width:760px}
body[data-movil] .content{padding:10px 10px 24px}
body[data-movil] .kpis{gap:8px}
body[data-movil] .kpi{flex:1 1 calc(50% - 8px);padding:10px 12px}
body[data-movil] .toolbar{flex-wrap:wrap;gap:6px}
body[data-movil] .rep-scroll{overflow-x:auto}
/* PESTAÑAS DE REPORTES EN UNA SOLA LÍNEA (12-oct-2026, 0.163, Alejandro: «no es necesario encimar las pestañas una sobre otra,
   ajústalas para que estén todas en una sola línea»): en el teléfono la fila no se envuelve —se desliza de lado— y la pestaña activa
   se trae a la vista al cargar y al tocarla (public/app.js → pestanaActivaAVista). La PC sigue con su fila que envuelve. */
body[data-movil] .rep-tabs{flex-wrap:nowrap;overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;-webkit-overflow-scrolling:touch}
body[data-movil] .rep-tabs .tab-inv{flex:none;white-space:nowrap}
/* BARRAS DE SCROLL NARANJAS EN EL TELÉFONO (12-oct-2026, 0.163, Alejandro: «pinta todas las barras de scroll del naranja
   característico del sistema para que sea más fácil de ver»). TODAS las del teléfono —la página, tablas, pestañas, catálogo, chips,
   modales—: el pulgar en --accent (el naranja de marca, igual en claro y en oscuro), 7 px para verse y arrastrarse con el dedo, y
   la pista apenas marcada (gris al 14 %: se lee sobre el fondo claro y sobre el oscuro). Solo bajo body[data-movil]: la PC no cambia.
   En Chromium (Android, la PWA) mandan los ::-webkit-scrollbar, que dan una barra SIEMPRE visible; desde Chrome 121 un
   scrollbar-color o scrollbar-width distinto de «auto» los apagaría, así que aquí se devuelven a auto (las barras escondidas a
   propósito —el lanzador, la réplica de la tabla del Inventario— siguen ocultas por su ::-webkit-scrollbar{display:none}). Donde no
   hay ::-webkit-scrollbar (Firefox), scrollbar-color pinta lo mismo. */
@supports selector(::-webkit-scrollbar){
  body[data-movil] *{scrollbar-width:auto;scrollbar-color:auto}
  body[data-movil] *::-webkit-scrollbar{width:7px;height:7px}
  body[data-movil] *::-webkit-scrollbar-thumb{background:var(--accent);border-radius:7px}
  body[data-movil] *::-webkit-scrollbar-thumb:active{background:var(--accent-text)}
  body[data-movil] *::-webkit-scrollbar-track{background:rgba(127,127,127,.14);border-radius:7px}
  body[data-movil] *::-webkit-scrollbar-corner{background:transparent}
}
@supports not selector(::-webkit-scrollbar){
  body[data-movil] *{scrollbar-color:var(--accent) rgba(127,127,127,.14)}
}
/* LA BARRA DE LA PÁGINA, MÁS FINA (13-oct-2026, 0.163, Alejandro: «Sí» a una barra del documento más fina). La vertical de la
   página está SIEMPRE a la vista y, con 7 px, le quitaba ese ancho a una pantalla de 360-390 px. En el teléfono la página se desliza
   en .content (html y body no se mueven: layout.js), así que esa es «la barra de la página»: 3 px, el mismo naranja. Por si algún
   día desliza el documento, html y body llevan la misma. Las barras de DENTRO (tablas, listas, chips, pestañas, el detalle de la
   venta) siguen en 7 px. html no cae bajo «body[data-movil] *»: se alcanza con :has(). Sin ::-webkit-scrollbar (Firefox) lo más
   fino que existe es scrollbar-width:thin, con los mismos colores. */
@supports selector(::-webkit-scrollbar){
  html:has(> body[data-movil])::-webkit-scrollbar,body[data-movil]::-webkit-scrollbar,body[data-movil] .content::-webkit-scrollbar{width:3px}
  html:has(> body[data-movil])::-webkit-scrollbar-thumb{background:var(--accent);border-radius:3px}
  html:has(> body[data-movil])::-webkit-scrollbar-track{background:rgba(127,127,127,.14)}
  body[data-movil] .content::-webkit-scrollbar-thumb{border-radius:3px}
}
@supports not selector(::-webkit-scrollbar){
  html:has(> body[data-movil]){scrollbar-color:var(--accent) rgba(127,127,127,.14)}
  html:has(> body[data-movil]),body[data-movil],body[data-movil] .content{scrollbar-width:thin}
}
/* La gráfica de Generales en el teléfono (pieza 9 de la 0.162, «la gráfica más baja», views/reportes.js) contaba con una barra
   flotante que no ocupaba sitio; la naranja mide 7 px DENTRO de su contenedor. Para que el área de las barras quede como estaba
   (tarjeta de 260 px, ≥ 120 px útiles) la barra se acomoda en el relleno de abajo de la tarjeta (10 → 3 px) cuando hay scroll. */
@media (max-width:720px){
  @supports selector(::-webkit-scrollbar){
    body[data-movil] .rep-card-gen:has(> .rep-scroll.con-scroll){padding-bottom:3px}
  }
}

/* ── Fotos por código de barras (23-sep-2026): rejilla de aprobación ── */
.fe-intro{max-width:900px;line-height:1.5}
.fe-nav{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:10px 0}
.fe-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:12px;margin:10px 0 18px}
.fe-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);padding:10px;display:flex;flex-direction:column;gap:8px;box-shadow:var(--shadow-md);min-width:0}
.fe-img{position:relative;aspect-ratio:1/1;background:#fff;border-radius:8px;overflow:hidden;display:flex;align-items:center;justify-content:center;border:1px solid var(--border)}
.fe-img img{width:100%;height:100%;object-fit:contain;display:block;opacity:0;transition:opacity .35s ease}
.fe-img.fe-lista img{opacity:1}
.fe-img.fe-sin img{display:none}
.fe-espera,.fe-sinfoto{position:absolute;inset:0;display:none;align-items:center;justify-content:center;text-align:center;padding:12px;font-size:12px;color:var(--muted)}
.fe-img.fe-cargando .fe-espera{display:flex}
.fe-img.fe-cargando::after{content:'';position:absolute;bottom:10px;width:22px;height:22px;border-radius:50%;border:3px solid rgba(234,88,12,.25);border-top-color:#ea580c;animation:feGiro .9s linear infinite}
@keyframes feGiro{to{transform:rotate(360deg)}}
.fe-img.fe-sin .fe-sinfoto{display:flex;background:var(--bg)}
.fe-img.fe-sin~.fe-aviso,.fe-img.fe-sin~.fe-acciones .fe-usar{display:none}
.fe-info{display:flex;flex-direction:column;gap:2px;min-width:0}
.fe-info b{font-size:13px;line-height:1.3;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
.fe-info .mono{font-size:12px}
.fe-aviso{margin:0;font-size:11px;color:var(--accent-text,#c2410c);background:rgba(234,88,12,.1);border-radius:6px;padding:4px 8px;line-height:1.35}
.fe-acciones{display:flex;gap:6px;margin-top:auto}
.fe-acciones .btn-primary,.fe-acciones .btn-ghost{flex:1;justify-content:center}
.fe-estado{margin:0;font-size:12px;color:var(--ok)}
.fe-descartada .fe-estado{color:var(--muted)}
.fe-card.fe-usada,.fe-card.fe-descartada{opacity:.85}
@media (max-width:720px){.fe-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}.fe-card{padding:8px}}
.fe-aportes{background:var(--surface);border:1px solid var(--border);border-left:3px solid var(--accent);border-radius:var(--r-card);padding:12px 16px;margin:10px 0 14px;display:flex;flex-direction:column;gap:8px}
.fe-aportes-t{display:flex;flex-wrap:wrap;gap:4px 12px;align-items:baseline}
.fe-aportes p{margin:0}
.fe-consent{display:flex;flex-direction:column;gap:10px;align-items:flex-start}
.fe-consent label{display:flex;gap:8px;align-items:flex-start;font-size:13px;line-height:1.45;max-width:780px}
.fe-consent input{margin-top:3px}
/* QR de las direcciones para el teléfono (23-sep): siempre sobre blanco, con aire, nítido (SVG) */
.qr{display:block;background:#fff;padding:6px;border-radius:10px;border:1px solid var(--border);box-shadow:var(--shadow-md)}
.qr-fila{display:flex;gap:14px;align-items:center;flex-wrap:wrap}
.qr-fila .muted{max-width:320px;font-size:13px;line-height:1.45}
.msgbox .vm-btns .btn-primary,.msgbox .vm-btns .btn-ghost{min-width:72px;justify-content:center}
.vm-pago{display:flex;align-items:center;justify-content:flex-start;gap:10px;flex-wrap:wrap;width:100%;padding-top:10px;border-top:1px dashed var(--border)}
.vm-pago .inp{width:auto}
/* Monedas como mini-tabla: una fila por moneda, cifras alineadas a la derecha para comparar de un vistazo */
.vm-monedas{display:flex;flex-direction:column;gap:6px;width:100%}
/* Combo de moneda: el icono va FUERA del <select> (un select no admite marcado dentro). */
.sel-mon{display:inline-flex;align-items:center}
.vm-mon{display:flex;align-items:center;gap:8px;cursor:pointer}
.vm-mon b{margin-left:auto;font-variant-numeric:tabular-nums;font-size:17px;color:var(--accent-text);font-weight:700}
/* Etiquetas de formulario más presentes (12→12.5px y seminegrita). El peso NO debe contagiar a los
   inputs (font:inherit los heredaría): se les repone 400 — salvo los select, que ya eran 600. */
.fld{display:flex;flex-direction:column;font-size:12.5px;font-weight:600;color:var(--muted);gap:3px}
.fld .inp:not(select){font-weight:400}
/* Celda de TASA POR INVENTARIO (multi-inventario S2): va junto a las tasas del día, separada por un
   filete para que se lea "estas son las globales · esta es la de A2". El nombre del inventario en
   tinta plena (es lo que la identifica); la etiqueta del par, apagada como las demás. */
.fld-inv{padding-left:10px;border-left:2px solid var(--border);white-space:nowrap}
.fld-inv b{color:var(--text);font-weight:600}
/* PAR de un inventario con tasas propias (30-sep-2026): el nombre UNA vez, a la altura de los números, y detrás sus dos
   celdas; el filete separa el grupo de las tasas del principal. Dentro del grupo las celdas no repiten filete ni sangría. */
.fld-grp{display:flex;align-items:flex-end;gap:8px;padding-left:10px;border-left:2px solid var(--border);white-space:nowrap}
.fld-grp-n{align-self:flex-end;font-size:12.5px;font-weight:700;color:var(--text);line-height:1;padding-bottom:12px;cursor:help}
.fld-grp .fld-inv{padding-left:0;border-left:0}
/* Compras enseña solo el par del inventario al que entra la compra (verTasasCompra): lo demás queda oculto en el formulario.
   Sin tasas del principal delante, el grupo no necesita filete. */
.fld[hidden],.fld-grp[hidden]{display:none}
/* …y sin tasas del principal delante el grupo no necesita filete ni repetir el nombre: el selector «Inventario» de la
   izquierda ya dice de cuál es. */
#compra-dialog .fld-grp{padding-left:0;border-left:0}
#compra-dialog .fld-grp-n{display:none}
/* El rótulo que lleva chip NO ensancha su celda: mide lo que el número de abajo y el chip puede asomar unos píxeles sobre
   el hueco entre celdas. Así encender el recomendador no cambia el ancho de la barra (ni la hace saltar de línea). */
.fld>span:has(>.tx-chip){display:block;width:0;min-width:100%;white-space:nowrap}
/* «Datos de la compra» en UNA línea también con el selector de Inventario (2+ inventarios): Factura y Proveedor ceden
   ancho y el Comentario se queda con lo que reste, como la Nota de la Caja. Con el bloque superior en dos líneas, el cajón
   «Pagar» no cabía a 1366×768. */
#c-row-top .fld.grow{flex:1 1 120px;min-width:120px}
#c-row-top .fld.grow .inp.grow{flex:none;width:100%;min-width:0}
#c-row-top .fn-row:has(#c-inventario) input[name=factura]{width:128px}
#c-row-top .fn-row:has(#c-inventario) select[name=proveedor_id]{width:204px}
/* Input HEREDANDO: vacío, con la tasa prestada de placeholder. El borde punteado dice "esto no lo
   declaraste tú" sin tener que leer — al teclear una tasa propia el borde se vuelve sólido solo.
   El placeholder va en tinta normal (no itálica: ensanchaba el número y lo cortaba a 110px). */
.inp-hereda{border-style:dashed}
/* TASAS DE CAMBIO POR INVENTARIO (29-sep-2026, lienzo «Tasas por inventario»): en Ajustes, un bloque por inventario con los
   dos números que unen su moneda con las otras dos; en la Caja, las mismas celdas bloqueadas y el enlace para editarlas. */
.tx-card{display:flex;flex-direction:column;gap:12px}
.tx-inv-fila{display:flex;flex-direction:column;gap:8px;padding:10px 12px;border:1px solid var(--border);border-radius:10px;background:var(--surface-2)}
.tx-inv-cab{display:flex;align-items:center;gap:8px;font-size:14px;flex-wrap:wrap}
.tx-inv-cab .muted{font-size:13px}
.tx-inv-celdas{display:grid;grid-template-columns:repeat(2,minmax(0,240px));gap:10px 18px}
.tx-cel{font-size:12.5px;font-weight:600;color:var(--muted)}
.tx-cel .tx-inp{text-align:right;font-weight:700;font-size:15px}
.tx-editar{align-self:center;font-size:12.5px;font-weight:600;color:var(--accent-text);white-space:nowrap}
@media (max-width:560px){.tx-inv-celdas{grid-template-columns:1fr}}
/* RECOMENDADOR DE TASAS (29-sep-2026, solo Exynol ERP): la barra del interruptor con el sello «Potenciado por Exynol IA», la tabla
   con el diferencial (▲ verde a favor · ▼ rojo en contra), la tasa óptima y el cambio actual, y la caja de Exynol IA. Medidas del
   lienzo «Tasas por inventario» (Main v7). */
.tx-cuerpo{display:flex;flex-direction:column;gap:12px}
.tx-bar{display:flex;align-items:center;gap:10px 22px;flex-wrap:wrap;padding:10px 14px;border:1px solid var(--border);border-radius:10px;background:var(--surface-2)}
.tx-bar .sw{margin-left:0;font-size:14px;color:var(--text)}
.tx-bar .sw .sw-lbl{color:var(--text)}
.tx-bar .fld{display:flex;flex-direction:row;align-items:center;gap:7px;font-size:12.5px;color:var(--muted);font-weight:600}
.tx-bar select.inp{height:32px;padding:4px 28px 4px 10px;font-size:13px;font-weight:600}
.tx-sello{margin-left:auto;font-size:12px;color:var(--muted);white-space:nowrap}
.tx-ia-sello{margin-left:auto;display:inline-flex;align-items:center;gap:8px;height:32px;box-sizing:border-box;padding:0 14px 0 8px;border-radius:16px;border:1px solid rgba(126,34,206,.45);background:radial-gradient(90px 40px at 0% 0%,rgba(192,132,252,.34),transparent 70%),radial-gradient(90px 40px at 100% 100%,rgba(238,140,42,.16),transparent 70%),#faf6ff;box-shadow:inset 0 0 0 2px rgba(255,255,255,.75),0 1px 3px rgba(88,28,135,.14);white-space:nowrap}
.tx-ia-sello-ic{width:24px;height:24px;flex:none}
.tx-ia-sello-txt{display:flex;flex-direction:column;justify-content:center;line-height:1.1}
.tx-ia-sello-txt small{font:700 8.5px var(--font-ui);letter-spacing:.1em;text-transform:uppercase;color:#7e22ce}
.tx-ia-sello-txt b{font:700 13px var(--font-head);color:#3b0764;letter-spacing:.01em}
[data-theme="oscuro"] .tx-ia-sello{background:radial-gradient(90px 40px at 0% 0%,rgba(147,51,234,.32),transparent 70%),radial-gradient(90px 40px at 100% 100%,rgba(238,140,42,.14),transparent 70%),#221a33;border-color:rgba(192,132,252,.45);box-shadow:inset 0 0 0 2px rgba(255,255,255,.06)}
[data-theme="oscuro"] .tx-ia-sello-txt small{color:#d8b4fe}
[data-theme="oscuro"] .tx-ia-sello-txt b{color:#f3e8ff}
.tx-tabla{width:100%;border-collapse:separate;border-spacing:0;font-size:14px}
.tx-tabla th{font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--th-text);text-align:left;padding:6px 12px;border-bottom:1px solid var(--border);white-space:nowrap}
.tx-tabla td{padding:8px 12px;border-bottom:1px solid color-mix(in srgb,var(--border) 55%,transparent);vertical-align:middle;font-variant-numeric:tabular-nums}
.tx-tabla tr.tx-inv td{background:var(--surface-2);font-weight:700;padding-top:9px;padding-bottom:9px;border-bottom:1px solid var(--border)}
.tx-tabla tr.tx-inv td .muted{font-weight:400;font-size:13px}
.tx-par{font-weight:600;white-space:nowrap}
.tx-tabla .tx-inp{width:118px;text-align:right;font-weight:700;font-size:15px}
.tx-dif{display:inline-flex;align-items:baseline;gap:5px;font-weight:700;font-size:13.5px;padding:2px 10px;border-radius:12px;white-space:nowrap;font-variant-numeric:tabular-nums}
.tx-dif.up{color:var(--ok);background:rgba(38,166,91,.15)}
.tx-dif.down{color:var(--no);background:rgba(220,53,69,.13)}
.tx-dif small{font-weight:600;font-size:11.5px}
/* Chip del recomendador junto a una tasa de solo lectura (Caja y Compras, 30-sep-2026): el diferencial de esa tasa contra el mercado. */
.tx-chip{display:inline-block;margin-left:3px;font-style:normal;font-weight:700;font-size:10px;line-height:15px;padding:0 3px;border-radius:8px;letter-spacing:-.01em;white-space:nowrap;font-variant-numeric:tabular-nums;vertical-align:1px;cursor:help}
.tx-chip[hidden]{display:none}
.tx-chip.up{color:var(--ok);background:rgba(38,166,91,.15)}
.tx-chip.down{color:var(--no);background:rgba(220,53,69,.13)}
.tx-opt{display:inline-flex;align-items:baseline;gap:7px;white-space:nowrap}
.tx-opt b{font-size:15px;color:var(--ok)}
.tx-opt i,.tx-merc i{font-style:normal;font-size:11.5px;color:var(--muted)}
.tx-merc{white-space:nowrap}
.tx-merc b{font-size:14px}
.tx-merc i{margin-left:7px}
.btn-ghost.tx-usar{font-size:12.5px;padding:4px 12px;min-height:0;height:30px;white-space:nowrap}
.tx-ia{display:flex;gap:12px;align-items:flex-start;padding:11px 14px;border-radius:10px;border:1px solid color-mix(in srgb,var(--violet) 32%,transparent);background:color-mix(in srgb,var(--violet) 7%,var(--surface))}
.tx-ia-ic{flex:none;width:32px;height:32px;border-radius:50%;background:#1b2447;display:flex;align-items:center;justify-content:center;margin-top:1px}
.tx-ia p{margin:0;font-size:13.5px;line-height:1.5}
.tx-ia .t{display:block;font-size:11.5px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--violet);margin-bottom:3px}
.tx-ia a{font-weight:600;color:var(--violet)}
.tx-pie{font-size:12.5px;color:var(--muted);margin:0;line-height:1.5}
.tx-pie b{color:var(--text)}
.tx-cargando{display:flex;align-items:center;gap:10px;padding:14px;color:var(--muted);font-size:13.5px}
@media (max-width:900px){.tx-tabla{display:block;overflow-x:auto}}
/* Etiqueta-botón que alterna el modo del descuento (% / monto fiat) */
.lbl-tog{align-self:flex-start;background:none;border:none;padding:0;margin:0;font:inherit;font-size:12px;color:var(--text);cursor:pointer;text-decoration:underline dotted;text-underline-offset:2px}
.lbl-tog:hover{color:var(--accent)}
/* FRAME ÚNICO de botón (14-ago): mismo relleno sólido, alto, radio y peso que el "+Nuevo" —
   se acabaron los botones "fantasma". Solo cambia el color por intención (slate = normal). */
.btn-ghost{background:linear-gradient(180deg,var(--btn-sec-hover) 0%,var(--btn-sec) 100%);border:1px solid var(--btn-sec-borde);border-radius:var(--r-ctl);padding:7px 15px;cursor:pointer;color:var(--btn-sec-ink);font-weight:600;text-decoration:none;display:inline-flex;align-items:center;gap:4px;box-shadow:inset 0 1px 0 rgba(255,255,255,.16),0 1px 2px rgba(16,24,40,.18);transition:background-color .12s ease,box-shadow .12s ease,transform .08s ease,border-color .12s ease}
.btn-ghost:hover{background:var(--btn-sec-hover);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 2px 6px rgba(16,24,40,.22)}
.btn-ghost:active{box-shadow:inset 0 2px 4px rgba(0,0,0,.3);transform:translateY(1px)}
.btn-ghost .ic16{fill:currentColor}
/* Nivel SUTIL (propuesta A): Cancelar / Limpiar / Cerrar / Quitar / No — lo que no hace nada por sí mismo deja
   de competir en slate con "Guardar cotización" o "Nuevo Cliente": fondo de control, borde, brillo arriba. */
.btn-ghost.btn-sutil{background:var(--surface-2);color:var(--text);border-color:var(--border);box-shadow:inset 0 1px 0 var(--sutil-brillo),0 1px 1px rgba(16,24,40,.08)}
.btn-ghost.btn-sutil:hover{background:var(--surface);border-color:var(--border-hover);box-shadow:inset 0 1px 0 var(--sutil-brillo),0 1px 2px rgba(16,24,40,.1)}
.btn-ghost.btn-sutil:active{box-shadow:inset 0 2px 4px rgba(16,24,40,.12)}
.btn-ghost.btn-sutil .ic16{fill:var(--icon-1)}
/* Acciones destructivas (Anular, Condonar): rojo contenido, se enciende al hover */
.btn-danger{background:linear-gradient(180deg,var(--no-claro) 0%,var(--no) 100%);border:1px solid var(--no-borde);border-radius:var(--r-ctl);padding:7px 15px;cursor:pointer;color:var(--btn-no-ink);font-weight:600;box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(16,24,40,.18);transition:box-shadow .12s ease,transform .08s ease}
.btn-danger:hover{background:var(--no);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 2px 6px rgba(16,24,40,.22)}
.btn-danger:active{box-shadow:inset 0 2px 4px rgba(0,0,0,.3);transform:translateY(1px)}
.fn-row .btn-danger{height:34px;display:inline-flex;align-items:center}

/* Etiquetas del modal de venta (ajuste 2a): los labels de CAMPO en texto pleno,
   pero los rótulos de sección (overlines) retroceden para no competir con los datos */
.venta-modal .vm-sec-t{color:var(--muted);font-weight:600;letter-spacing:.08em}
.venta-modal .fld{color:var(--text)}
.venta-modal .vm-tot span{color:var(--text)}
/* Pie de la Caja/Compras en pantallas BAJAS (feedback 19-ago): a ~768px de alto el panel de 400px
   no cabía con todo (totales + pago + monedas + botones) y el desborde salía por arriba — el
   "Subtotal:" quedaba fuera del frame en la caja real (1366×768). Se compacta lo justo para que
   quepa holgado; el overflow-y del .vm-foot queda de red para cualquier extremo que aún no quepa.
   Solo los modales maximizados (venta/compras): los de pie-barra no usan la columna de 400px. */
@media (max-height:860px){
  .venta-modal.maximized .vm-foot{padding:10px 16px;gap:5px}
  .venta-modal.maximized .vm-tot{gap:2px}
  .venta-modal.maximized .vm-tot .big b{font-size:22px}
  .venta-modal.maximized .vm-pago{padding-top:6px;gap:6px 8px}
  .venta-modal.maximized #v-credito-info{font-size:12px}
  .venta-modal.maximized .vm-monedas{gap:4px}
  /* (19-ago, 2ª vuelta) Con VARIOS inventarios las secciones de arriba crecen (celda de tasa por
     inventario que envuelve la fila + selector del filtro) y le roban alto al pie: se recupera
     en los paddings de sección. (28-ago) La imagen vuelve a crecer SIN devolver ese alto: los
     márgenes negativos hacen que aporte al flujo lo mismo que la caja de 104px que reemplaza
     (138−27−7=104) — ocupa la franja del título y el padding inferior, que a su derecha están
     vacíos, sin empujar el Detalle ni el pie. */
  .venta-modal.maximized .vm-sec{padding:7px 20px}   /* 08-sep: −6 px de alto que se van al cajón (él: «extiende un poco más el cajón») */
  .venta-modal.maximized .vm-sec-t{margin-bottom:5px}
  .venta-modal.maximized .v-img-box{width:138px;height:138px;margin-bottom:-7px}
  .venta-modal.maximized .vm-head{padding:8px 20px}
  /* El modo MIXTO suma Contado + info de crédito (+87px reales medidos): los campos del pago
     pasan a label-al-lado y la mini-tabla de monedas se aprieta un poco — pero SIGUE vertical:
     en fila, tres montos reales (12.000,00…) no caben en 400px y la cifra se corta. */
  .venta-modal.maximized .vm-monedas{gap:3px}
  .venta-modal.maximized .vm-mon b{font-size:15px}
  .venta-modal.maximized .vm-pago .fld{flex-direction:row;align-items:center;gap:6px}
  .venta-modal.maximized .vm-pago{font-size:13px}
  /* Un panel un poco más ANCHO envuelve el pago en menos líneas (medido: mixto pasaba 14px). */
  .venta-modal.maximized .vm-foot{width:520px}   /* 07-sep: 520 en todas las alturas (regla de abajo); aquí solo por claridad */
  .venta-modal.maximized .vm-btns .btn-primary,.venta-modal.maximized .vm-btns .btn-ghost{padding:6px 13px}
}

/* Modal "Exportar Inventario" (15-ago): filtros en rejilla compacta + pie con formato de salida */
.exp-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px 14px}
.exp-grid .inp{width:100%}
.exp-pie{display:flex;gap:10px;align-items:center;margin-top:18px}

/* Multi-inventario + visor "Ver en" (15-ago) */
/* Frame de inventarios JUSTO ENCIMA de la tabla (feedback Alejandro, ajustado 15-ago: debajo se
   perdía con inventarios largos): pestañas estilo Excel — radio hacia ARRIBA, apoyadas al borde
   inferior, "mirando" a la tabla que viene debajo; la activa en violeta como los chips — + el
   Valor de inventario en la misma franja. El "+" del final = la hoja nueva del Excel (gestión). */
.vi-frame{display:flex;align-items:center;gap:14px;row-gap:8px;flex-wrap:wrap;background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);padding:8px 16px;margin:2px 0 12px;box-shadow:var(--shadow-md)}
.vi-frame .vi-spacer{flex:1}
.inv-tabs{display:flex;align-items:stretch;gap:3px;flex-wrap:wrap}
.tab-inv{padding:6px 16px;border:1px solid var(--border);border-bottom:none;border-radius:10px 10px 0 0;background:transparent;color:var(--muted);font-size:13px;font-weight:600;cursor:pointer;display:inline-flex;align-items:center;gap:6px}
.tab-inv:hover{color:var(--text);border-color:var(--border-hover)}
.tab-inv.active{background:var(--violet);color:#fff;border-color:var(--violet)}
[data-theme="oscuro"] .tab-inv.active{color:#14161b}
.tab-inv.active .cur{color:inherit}
/* Iconos en las pestañas (29-ago-2026, pedido Alejandro): mismo set duotono del launcher (tabIcon en layout.js) a
   16 px. Reposo: tonos de icono; hover: violeta+naranja como los tiles; activa: cuerpo en el color del texto de la
   pestaña (blanco en claro, tinta en oscuro → currentColor) y acento naranja — en oscuro la pestaña activa es azul
   claro y el naranja de marca se funde con ella, así que ahí el acento va en naranja quemado. */
.tab-inv .tic{width:16px;height:16px;display:inline-flex;flex:none}
.tab-inv .tic svg{width:16px;height:16px;display:block}
/* Pestañas grandes. 06-sep: Reportes +25 % (8/20 px, 16 px, iconos 20). 07-sep (pedido Alejandro): Reportes baja un 10 %
   (7/18 px, 14.4 px, iconos 18) y las pestañas de Ventas y de Cuentas adoptan ese MISMO tamaño (clase tabs-md). */
.rep-tabs .tab-inv,.tabs-md .tab-inv{padding:7px 18px;font-size:14.4px;gap:7px}
.rep-tabs .tab-inv .tic,.rep-tabs .tab-inv .tic svg,.tabs-md .tab-inv .tic,.tabs-md .tab-inv .tic svg{width:18px;height:18px}
.tab-inv:hover .ic-b{fill:var(--violet)}
.tab-inv:hover .ic-a{fill:var(--accent)}
.tab-inv.active .ic-b{fill:currentColor}
.tab-inv.active .ic-a{fill:var(--accent)}
[data-theme="oscuro"] .tab-inv.active .ic-a{fill:#a33a0c}
.tab-inv-plus{font-size:15px;padding:6px 12px;border-style:dashed}
.inv-nombre{width:200px;font-weight:600}
.chips-ver{align-items:center}
.chips-ver .ver-lbl{font-size:12.5px;font-weight:600;color:var(--muted);margin-right:2px}
.chips-ver .ver-nota{font-size:11.5px;color:var(--muted);font-style:italic;margin-left:4px}
/* Celdas en modo visor: fondo apenas tintado = "esto es referencial, no editable" */
.pv-ref{background:color-mix(in srgb,var(--accent) 5%,transparent)}
.vi-item b + b{margin-left:8px}
.btn-inv-del{padding:2px 10px;font-size:12px;height:auto}
/* Gestor de CATEGORÍAS (03-sep): filas nombre editable · conteo · acción; el botón etiqueta del header y del editor */
.cat-row{display:grid;grid-template-columns:1fr 110px auto;align-items:center;gap:12px;margin-bottom:8px;font-size:14px}
.cat-row .inp{width:100%}
.cat-nombre{display:flex;align-items:center;gap:8px}
.cat-n{text-align:right;white-space:nowrap}
.cat-mover{display:inline-flex;gap:6px;align-items:center;margin:0}
.cat-mover .inp{width:auto;max-width:200px;padding:3px 8px;font-size:12.5px}
.cat-resumen{margin:0 0 12px}
.cat-lista{max-height:46vh;overflow:auto;padding-right:4px}
.th-cats{margin-left:2px}
.btn-cats{align-self:flex-end;white-space:nowrap}

/* Reportes: KPIs + tarjeta de gráfico */
.kpis{display:flex;gap:12px;flex-wrap:wrap;margin:16px 0}
.kpi{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);padding:14px 18px;box-shadow:var(--shadow-md);min-width:0;flex:1 1 160px;container-type:inline-size;display:flex;flex-direction:column;justify-content:space-between}
.kpi.pct{flex:.62 1 110px}
.kpi.pct .kpi-val{font-size:clamp(16px,18cqw,26px)}
.kpi-lbl{color:var(--muted);font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.08em;font-family:var(--font-head)}
.kpi-val{font-size:clamp(16px,11cqw,26px);font-weight:700;margin-top:6px;letter-spacing:-.02em;font-variant-numeric:tabular-nums;font-family:var(--font-display)}
/* Código de moneda atenuado: la cifra manda, el "COP" acompaña */
.cur{font-size:12px;font-weight:600;color:var(--muted);letter-spacing:.03em;margin-right:2px}
/* CHIP de moneda EXTRANJERA (14-ago): la base del negocio se queda con el .cur neutro de arriba y
   solo las otras monedas se pintan — el color señala "esto no está en tu moneda", no adorna.
   La cifra NUNCA se tiñe (decisión de Alejandro): el chip lleva el color, el número queda en tinta
   normal y los tabular-nums siguen alineados columna a columna.
   ⚠ ALTURA: line-height 1 + padding chico para que el chip quepa DENTRO de la caja de línea de
   15px/1.45 y las filas no crezcan ni 1px (misma trampa que costó el marcador c/IVA el 11-ago:
   vertical-align:super agrandaba la línea). Medir alturas de fila al tocar esto. */
.cur-usd,.cur-ves,.cur-cop{display:inline-block;padding:2px 6px;border-radius:20px;font-size:11px;font-weight:700;line-height:1;letter-spacing:.02em;border:1px solid transparent;margin-right:4px;vertical-align:1px}
.cur-usd{background:color-mix(in srgb,var(--cur-usd) 14%,transparent);color:var(--cur-usd);border-color:color-mix(in srgb,var(--cur-usd) 32%,transparent)}
.cur-ves{background:color-mix(in srgb,var(--cur-ves) 14%,transparent);color:var(--cur-ves);border-color:color-mix(in srgb,var(--cur-ves) 32%,transparent)}
.cur-cop{background:color-mix(in srgb,var(--cur-cop) 14%,transparent);color:var(--cur-cop);border-color:color-mix(in srgb,var(--cur-cop) 32%,transparent)}
/* ─── MONEDA DIBUJADA (05-sep, lienzo "Botones y Monedas": ficha A junto a los combos, en "Cobrar en" y en el setup).
   El color sale de los tokens --cur-* (claro y oscuro); la neutra "Todas" va en gris de icono. ─── */
.coin{flex:none;vertical-align:middle;display:inline-block}
.coin[hidden]{display:none}
.coin .coin-d{fill:color-mix(in srgb,var(--cur-usd) 16%,transparent);stroke:color-mix(in srgb,var(--cur-usd) 45%,transparent);stroke-width:1.4}
.coin .coin-t{fill:var(--cur-usd);font-family:var(--font-ui);font-weight:700}
.coin .coin-p{fill:var(--cur-usd)}
.coin-ves .coin-d{fill:color-mix(in srgb,var(--cur-ves) 16%,transparent);stroke:color-mix(in srgb,var(--cur-ves) 45%,transparent)}
.coin-ves .coin-t{fill:var(--cur-ves)}
.coin-cop .coin-d{fill:color-mix(in srgb,var(--cur-cop) 16%,transparent);stroke:color-mix(in srgb,var(--cur-cop) 45%,transparent)}
.coin-cop .coin-t{fill:var(--cur-cop)}
.coin-all .coin-d{fill:color-mix(in srgb,var(--icon-1) 16%,transparent);stroke:color-mix(in srgb,var(--icon-1) 45%,transparent)}
.coin-all .coin-p{fill:var(--icon-1)}
/* Ranura del combo (integración 1): las 4 fichas, se ve la del valor elegido */
.coin-slot{display:inline-flex;align-items:center;margin-right:6px}
/* Inventario: chips "Ver en:" con ficha + código; pestañas, barra de valor y kardex con la ficha delante */
.chips-ver .chip{display:inline-flex;align-items:center;gap:5px}
.tab-inv .coin{vertical-align:-3px;margin-left:2px}
.vi-item .coin{vertical-align:-3px;margin-right:2px}
.vm-mon .coin{margin-right:0}
.setup-mon .mon-sim{display:inline-flex;align-items:center;justify-content:center;height:30px}
.kpi-val .cur{font-size:13px}
.kpi-val .cur-usd,.kpi-val .cur-ves,.kpi-val .cur-cop{font-size:12px;vertical-align:4px}
.kpi-val .kpi-n{font-size:12px;font-weight:400}
.rep-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);padding:16px 18px;box-shadow:var(--shadow-md);margin-bottom:18px;height:420px}
.rep-h{margin:24px 0 10px;font-size:17px;font-weight:600;font-family:var(--font-head)}
/* Reportes en PESTAÑAS (27-ago, pedido Alejandro): las mismas pestañas estilo Excel del Inventario y
   Cuentas, apoyadas en una línea; cada panel trae su gráfica (salvo Top productos, decisión suya) en
   moneda BASE. Los paneles inactivos siguen en el DOM (hidden): el export y las suites leen la página
   entera. El panel de existencias (.rep-exist) vive bajo la barra Exportar, solo con ese aspecto. */
.rep-tabs{margin:18px 0 0;padding:0 6px;border-bottom:1px solid var(--border)}
.rep-panel{padding-top:14px}
.rep-panel[hidden]{display:none}
.rep-panel > .rep-h:first-child{margin-top:4px}
.rep-card-sm{height:320px}
/* Gráfica de Generales con VENTANA de 15 movimientos (03-sep, pedido Alejandro): #rep-scroll ocupa el
   área de la tarjeta y desplaza en horizontal; #rep-scroll-inner se ensancha (n/15) desde reportes.js
   y el canvas responsive lo sigue. Con scroll activo, el interior cede 18 px a la barra. */
.rep-scroll{position:absolute;inset:16px 18px;overflow-x:auto;overflow-y:hidden}
.rep-scroll-inner{height:100%;min-width:100%;position:relative}
.rep-scroll.con-scroll .rep-scroll-inner{height:calc(100% - 18px)}
/* EJE DE CANTIDADES SUPERPUESTO (24-sep-2026, pedido de Alejandro): #rep-eje va pegado (sticky) al borde izquierdo de la
   ventana visible de #rep-scroll —no ocupa sitio (0×0)— y el plugin repEje (reportes.js) le cuelga los rótulos del eje Y a
   la altura exacta de cada línea de la rejilla, sobre un fondo que se desvanece hacia las barras. Al cambiar la escala
   (cuando el scroll se detiene) los rótulos nuevos entran con un fundido corto mientras las barras se acomodan. */
.rep-eje{position:sticky;left:0;top:0;width:0;height:0;z-index:2;display:block;pointer-events:none;overflow:visible}
.rep-eje-fondo{position:absolute;left:0;width:58px;background:linear-gradient(90deg,var(--surface) 0,var(--surface) 42px,transparent)}
.rep-eje-t{position:absolute;left:0;width:50px;text-align:right;transform:translateY(-50%);font-size:11px;color:var(--text);font-variant-numeric:tabular-nums;white-space:nowrap;line-height:1}
.rep-eje.cambia .rep-eje-t{animation:repEjeEntra .35s ease both}
@keyframes repEjeEntra{from{opacity:.2}to{opacity:1}}
@media (prefers-reduced-motion:reduce){.rep-eje.cambia .rep-eje-t{animation:none}}
.rep-vacio{height:100%;display:flex;align-items:center;justify-content:center;color:var(--muted);font-size:14px}
/* FACTURA DEL MOVIMIENTO (24-sep-2026, opción A del lienzo de Alejandro): ventana sobre Reportes con el molde de los modales. Las
   barritas y los puntos llevan el color EXACTO de las velas (#7cb5ec · rgba(220,53,69,.88) · rgba(38,166,91,.92)); las cifras van un
   tono más oscuro para leerse sobre el gris (azul propio; costo y ganancia con --no / --ok). En oscuro la venta vuelve al azul de la vela. */
.rep-fac{width:min(1040px,96vw)}
.rep-fac-body{padding:14px 18px 16px;gap:12px;overflow:auto}
.rep-fac-chip{display:inline-flex;align-items:center;font-size:12px;font-weight:600;color:#fff;background:rgba(255,255,255,.14);border:1px solid rgba(255,255,255,.22);border-radius:999px;padding:3px 10px;white-space:nowrap}
.rep-fac-chip.b-ok{background:rgba(38,166,91,.28);border-color:rgba(38,166,91,.5)}
.rep-fac-chip.b-no{background:rgba(220,53,69,.3);border-color:rgba(220,53,69,.55)}
.rep-fac-kpis{display:flex;gap:10px}
.rep-fac-kpis .kpi{padding:10px 14px}
.rep-fac-venta{color:#2a6fb5}
.rep-fac-costo{color:var(--no)}
.rep-fac-gan{color:var(--ok)}
[data-theme="oscuro"] .rep-fac-venta{color:#7cb5ec}
.rep-fac-tabla th.rep-fac-venta,.rep-fac-tabla th.rep-fac-costo,.rep-fac-tabla th.rep-fac-gan{color:inherit}
.rep-fac-tabla th.rep-fac-venta{color:#2a6fb5}.rep-fac-tabla th.rep-fac-costo{color:var(--no)}.rep-fac-tabla th.rep-fac-gan{color:var(--ok)}
[data-theme="oscuro"] .rep-fac-tabla th.rep-fac-venta{color:#7cb5ec}
.rep-fac-tabla td.rep-fac-venta,.rep-fac-tabla td.rep-fac-costo,.rep-fac-tabla td.rep-fac-gan{font-weight:600}
.rep-fac-tabla tfoot td{border-top:2px solid var(--violet);background:var(--surface-2)}
.rep-fac-leyenda{display:flex;align-items:center;gap:18px;font-size:12.5px;color:var(--muted)}
.rep-fac-leyenda span{display:inline-flex;align-items:center;gap:6px}
.rep-fac-leyenda-nota{margin-left:auto;font-style:italic}
.rep-fac-punto{display:inline-block;width:10px;height:10px;border-radius:3px}
.rep-fac-punto-v{background:#7cb5ec}.rep-fac-punto-c{background:rgba(220,53,69,.88)}.rep-fac-punto-g{background:rgba(38,166,91,.92)}
.rep-fac-barra-td{padding-left:6px}
.rep-fac-barra{display:inline-flex;width:120px;height:8px;border-radius:4px;overflow:hidden;background:rgba(51,69,110,.12);vertical-align:middle}
.rep-fac-barra-c{height:8px;background:rgba(220,53,69,.88)}
.rep-fac-barra-g{height:8px;background:rgba(38,166,91,.92)}
.rep-fac-desc{font-size:12px}
.rep-fac-nota{margin:0;font-size:12.5px}
.rep-fac-pie{display:flex;align-items:center;gap:10px;font-size:12px}
/* 25-sep-2026 (Alejandro, cierre del 24: «la ventana surge de manera tosca, y no se logra ver el latido naranja»): el doble clic
   en una barra late 1 s (views/reportes.js → LATIDO_MS; 1,5 s en la 0.145) y SOLO ENTONCES la factura SURGE DESDE LA BARRA: transform-origin en
   el punto de la barra (aunque quede fuera de la ventana; surgirDesde) y esta animación en vez del «pop» genérico de app.js. */
@media (prefers-reduced-motion: no-preference){
  dialog.rep-fac-surge{animation:rep-fac-surge .7s cubic-bezier(.3,.7,.3,1)}
  dialog.rep-fac-surge::backdrop{animation:dlg-velo .7s ease}
}
@keyframes rep-fac-surge{0%{opacity:0;transform:scale(.04)}30%{opacity:1}100%{opacity:1;transform:none}}
/* La tabla «Movimientos del día» ya no atrae el scroll al formarse: late ENTERA con el aro naranja de las tarjetas (vivoKpi). */
@keyframes vivoTabla{0%{border-color:var(--border);box-shadow:var(--shadow)}18%{border-color:#ee8c2a;box-shadow:var(--shadow),0 0 0 3px rgba(238,140,42,.35)}100%{border-color:var(--border);box-shadow:var(--shadow)}}
@keyframes vivoTablaCelda{0%{background-color:transparent}18%{background-color:rgba(238,140,42,.10)}100%{background-color:transparent}}
/* 0.158: con el filtro en un solo día, el Modo venta hace latir la tabla del filtro (#rep-mov-dia) en vez de formar otra igual. */
:is(#rep-dia-panel,#rep-mov-dia) table.rep-tabla-late{animation:vivoTabla 1.5s ease}
:is(#rep-dia-panel,#rep-mov-dia) table.rep-tabla-late td{animation:vivoTablaCelda 1.5s ease}
@media (prefers-reduced-motion:reduce){:is(#rep-dia-panel,#rep-mov-dia) table.rep-tabla-late,:is(#rep-dia-panel,#rep-mov-dia) table.rep-tabla-late td{animation:none}}
.rep-exist{margin-top:6px}
.rep-exist .rep-h{margin-top:12px}
.rep-nota{margin:0 0 10px;font-size:13px}
/* Chips ▲/▼ % de variación por período (12-sep, «Alternativa B» de Alejandro): mismo molde que .merc-delta del Mercado
   Cambiario. En la gráfica se dibujan en el canvas bajo el eje (plugin repDeltas de views/reportes.js) y la tarjeta crece
   32 px para darles sitio sin robárselo a las barras; en la tabla van en la columna «vs. anterior». */
.rep-card.con-deltas{height:452px}
.rep-nota-deltas{margin-top:-8px}
.rep-delta{display:inline-block;font-size:12.5px;font-weight:700;padding:1px 8px;border-radius:10px;white-space:nowrap;line-height:1.45}
.rep-delta.up{color:var(--ok);background:rgba(38,166,91,.14)}
.rep-delta.down{color:var(--no);background:rgba(220,53,69,.12)}
.rep-delta.eq{color:var(--info);background:rgba(124,181,236,.16)}
.rep-delta.curso{background:transparent;border:1px dashed currentColor;padding:0 7px}
.rep-curso{font-size:11px;font-weight:400;margin-left:6px}
.rep-n{display:inline-flex;align-items:center;gap:6px;margin-left:10px;vertical-align:middle}
.rep-n .ver-lbl{font-size:12.5px;font-weight:600;color:var(--muted)}
.rep-n .chip{padding:3px 11px;font-size:12.5px;line-height:1.3}
/* Top productos · «Ordenar por» como alternador de 3 partes (29-sep-2026, pedido de Alejandro): el segmentado claro del
   S/M/L del Catálogo, a la altura y con la letra de los chips «Ver N» de al lado. */
.rep-top-seg{margin-left:0}
.rep-top-seg button{padding:3px 11px;font-size:12.5px;line-height:1.3}

/* ── FONDO HEXAGONAL (13-oct-2026, 0.163; antes, desde el 14-ago, ~180 logos «Exy» a la deriva): el panal + la onda naranja en cuatro
   direcciones, un SVG por marco (views/layout.js → patronExy). Vive sobre los frames NAVY (login/licencia/setup + membrete + cabeceras
   de los diálogos), oscuros en ambos temas → la malla va en blanco tenue y la onda en el naranja del sistema, sin variantes de tema.
   La onda la mueve SMIL dentro del SVG; aquí solo se pinta y se apaga: «reducir movimiento» del sistema o el interruptor de animaciones
   de Ajustes (html.exy-sin-anim, que pone app.js desde la preferencia exyAnimChat de esta PC) esconden la capa .onda y queda el panal
   quieto. El patrón es absolute con z-index 0 y pointer-events none: decoración; los hijos reales del marco van encima. */
.exy-pat{position:absolute;inset:0;overflow:hidden;pointer-events:none;z-index:0}
.exy-pat .panal{display:block;width:100%;height:100%}
.exy-pat .malla{fill:none;stroke:#fff;stroke-opacity:.14;stroke-width:1;stroke-linejoin:round}
.exy-pat .brillo{fill:none;stroke:#ee8c2a;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round}
.exy-pat .halo{fill:none;stroke:#ee8c2a;stroke-opacity:.22;stroke-width:7;stroke-linecap:round;stroke-linejoin:round}
.exy-pat-appbar .brillo{stroke-width:2}.exy-pat-appbar .halo{stroke-width:5.5}
@media (prefers-reduced-motion: reduce){.exy-pat .onda{display:none}}
html.exy-sin-anim .exy-pat .onda{display:none}
/* Los hijos reales del frame quedan SOBRE el patrón (que es absolute con z-index 0). */
.appbar{position:relative}
/* .toast-host queda FUERA (19-sep): vive dentro del membrete y esta regla le pisaba el position:fixed → con 2+ avisos
   el membrete crecía (70 → 162 px) y empujaba toda la pantalla hacia abajo en cada venta. */
.appbar > *:not(.exy-pat):not(.toast-host){position:relative;z-index:1}
.login-wrap > *:not(.exy-pat){position:relative;z-index:1}

/* Login: portada de marca (fondo navy del logo + tarjeta elevada) */
.login-wrap{min-height:100vh;position:relative;display:flex;align-items:center;justify-content:center;background:radial-gradient(1100px 700px at 75% -10%,#22335c 0%,#0c1730 60%,#0a1226 100%)}
.login-card{background:var(--surface);border:none;border-top:3px solid var(--accent);border-radius:16px;box-shadow:0 20px 50px rgba(2,8,20,.45),0 2px 8px rgba(2,8,20,.3);padding:32px;width:360px;display:flex;flex-direction:column;gap:12px}
.login-ver{font-size:11px;color:var(--muted);text-align:center;margin:4px 0 0}
.login-act{font-size:12px;font-weight:600;color:var(--accent-text);text-align:center;margin:10px 0 0;padding:6px 10px;border-radius:var(--r-ctl);background:rgba(238,140,42,.10);border:1px solid rgba(238,140,42,.35)}
.login-card .inp{width:100%}
.login-card .btn-primary{width:100%;padding:10px}
.login-error{background:rgba(192,57,43,.14);color:var(--no);border-radius:7px;padding:8px 10px;margin:0;font-size:13px;text-align:center}
/* Teléfonos emparejados (11-sep): la página de espera del teléfono (views/emparejar.js) */
/* ¿Olvidó su clave?, datos fiscales de la licencia y clave temporal (12-sep-2026) */
.login-link{font-size:12.5px;text-align:center;color:var(--muted);text-decoration:underline;text-underline-offset:2px}
.login-link:hover{color:var(--accent-text)}
.rec-card{width:min(420px,calc(100vw - 32px))}
.rec-tit{margin:0;font-size:18px;text-align:center;font-family:var(--font-head)}
.rec-pasos{margin:0;padding-left:20px;font-size:13px;line-height:1.45;display:flex;flex-direction:column;gap:6px}
.rec-equipo{font-family:var(--font-mono);font-weight:700;letter-spacing:.06em;white-space:nowrap}
.rec-codigo{text-transform:uppercase;letter-spacing:.08em}
.rec-ok{margin:0;font-size:14px;text-align:center;color:var(--ok)}
.rec-ir{text-align:center;text-decoration:none}
.inp.inp-bloq{background:rgba(127,127,127,.12);cursor:not-allowed}
.setup-fiscal{margin:0;font-size:12.5px;line-height:1.4;padding:8px 10px;border-radius:8px;background:rgba(238,140,42,.10);border-left:3px solid var(--accent)}
.fiscal-nota{margin:0}
.mi-clave-forzado{margin-bottom:8px}
.mi-clave-salir{margin-top:8px}
.login-card.emp-card{width:min(400px,calc(100vw - 32px));gap:14px}
.emp-t{font-family:'Poppins',sans-serif;font-weight:700;font-size:19px;line-height:1.2;margin:0;text-align:center}
.emp-p{margin:0;font-size:13.5px;line-height:1.45;color:var(--muted);text-align:center}
.emp-codigo{font-family:'IBM Plex Mono',ui-monospace,Consolas,monospace;font-size:40px;font-weight:700;letter-spacing:.14em;text-align:center;color:var(--accent);margin:2px 0;user-select:all}
.emp-nombre{display:flex;gap:6px;margin:0}
.emp-nombre .inp{flex:1;min-width:0}
.emp-nombre .btn-ghost{white-space:nowrap}
.emp-estado{display:flex;align-items:center;justify-content:center;gap:8px;margin:0;font-size:12.5px;color:var(--muted);text-align:center}
.emp-spin{flex:none;width:14px;height:14px;border:2px solid var(--border);border-top-color:var(--accent);border-radius:50%;animation:emp-spin 1s linear infinite}
@keyframes emp-spin{to{transform:rotate(360deg)}}
.emp-volver{text-align:center;text-decoration:none}

/* ─── Asistente de configuración inicial (/setup, wizard de onboarding del primer arranque) ───
   Misma escena del login (login-wrap + login-card) con la tarjeta más ancha y 3 pasos. */
.setup-card{width:560px;max-width:calc(100vw - 32px);gap:13px}
.setup-sub{margin:-4px 0 0;text-align:center;color:var(--muted);font-size:13px}
.setup-pasos{display:flex;gap:6px;justify-content:center;list-style:none;margin:2px 0 2px;padding:0}
.setup-pasos li{display:flex;align-items:center;gap:6px;font-size:12px;font-weight:600;color:var(--muted);padding:4px 12px 4px 5px;border-radius:999px;border:1px solid var(--border);transition:color .15s,border-color .15s}
.setup-pasos li .n{display:inline-flex;align-items:center;justify-content:center;width:19px;height:19px;border-radius:50%;background:var(--surface-2);font-size:11px;transition:background-color .15s,color .15s}
.setup-pasos li.activo{color:var(--accent-text);border-color:var(--accent)}
.setup-pasos li.activo .n{background:var(--accent);color:var(--accent-ink)}
.setup-pasos li.hecho{color:var(--ok);border-color:var(--ok)}
.setup-pasos li.hecho .n{background:var(--ok);color:#fff}
.setup-paso{border:none;margin:0;padding:0;display:flex;flex-direction:column;gap:10px;min-width:0}
.setup-paso[hidden]{display:none}
.setup-fld{display:flex;flex-direction:column;gap:4px}
.setup-fld>span,.setup-fld-tit{font-size:12px;font-weight:600;color:var(--muted)}
.setup-fld b{color:var(--accent-text)}
.setup-fld i{font-weight:400;font-style:normal;color:var(--muted);opacity:.8}
.setup-grid2{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.setup-hint{font-size:12px;color:var(--muted);margin:-4px 0 0;line-height:1.35}
.setup-monedas{display:flex;gap:8px}
/* Rubros del negocio: mismas tarjetas de las monedas, en rejilla 2×2 (4 opciones con texto largo) */
.setup-rubros{flex-wrap:wrap;margin-bottom:10px}
.setup-rubros .setup-mon{flex:1 1 42%}
.setup-rubros .mon-nom{font-size:13px}
.setup-rubros .mon-cod{text-align:center}
.setup-mon{position:relative;flex:1;display:flex;flex-direction:column;align-items:center;gap:1px;padding:10px 8px 8px;border:1px solid var(--border);border-radius:var(--r-card);cursor:pointer;transition:border-color .12s,background-color .12s,box-shadow .12s}
.setup-mon:hover{border-color:var(--border-hover)}
.setup-mon input{position:absolute;opacity:0;pointer-events:none}
.setup-mon:has(input:checked){border-color:var(--accent);background:var(--surface-2);box-shadow:var(--ring)}
.setup-mon .mon-sim{font-family:var(--font-display);font-weight:700;font-size:20px;color:var(--violet)}
.setup-mon .mon-nom{font-size:12px;font-weight:600}
.setup-mon .mon-cod{font-size:11px;color:var(--muted)}
.setup-iva{max-width:140px}
.setup-nav{display:flex;gap:8px;margin-top:2px}
.setup-nav .btn-primary{flex:1;padding:10px}
.setup-nav .btn-ghost{flex:0 0 auto}

/* Costo derivado (solo lectura) */
.costo-cell{color:var(--muted)}

/* ─── Heatmap de UTILIDAD (bandas exactas del Excel, Sub AplicarColorUtilidad) ───
   ≤0 sin color · 0–20 rojo · 20–35 naranja · 35–50 amarillo · 50–100 verde · >100 azul */
.util-cell{font-variant-numeric:tabular-nums}
/* 0.163 (12-oct-2026): cada banda lee sus colores de las variables --util-<banda>-bg/-tx (la hoja por negocio que layout() mete en
   la cabecera cuando Ajustes → Colores del margen tiene colores propios); sin ellas, los colores de siempre. */
.util-r{color:var(--util-r-tx,#9c0006);background:var(--util-r-bg,#ffc7ce)}
.util-o{color:var(--util-o-tx,#9c5700);background:var(--util-o-bg,#ffe0b2)}
.util-y{color:var(--util-y-tx,#9c8000);background:var(--util-y-bg,#fff9b2)}
.util-g{color:var(--util-g-tx,#006100);background:var(--util-g-bg,#c6efce)}
.util-b{color:var(--util-b-tx,#002060);background:var(--util-b-bg,#bdd7ee)}
[data-theme="oscuro"] .util-r{color:var(--util-r-tx,#ffb3ba);background:var(--util-r-bg,rgba(156,0,6,.30))}
[data-theme="oscuro"] .util-o{color:var(--util-o-tx,#ffd28a);background:var(--util-o-bg,rgba(156,87,0,.28))}
[data-theme="oscuro"] .util-y{color:var(--util-y-tx,#ffe98a);background:var(--util-y-bg,rgba(156,128,0,.24))}
[data-theme="oscuro"] .util-g{color:var(--util-g-tx,#8ee6a8);background:var(--util-g-bg,rgba(0,97,0,.30))}
[data-theme="oscuro"] .util-b{color:var(--util-b-tx,#a9c9f5);background:var(--util-b-bg,rgba(0,32,96,.45))}
/* Ajustes → Colores del margen (0.163): un selector de color por rango con su muestra; con color propio la muestra lo tiñe en vivo */
.util-colores{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin:4px 0 10px}
.util-color-fld{display:inline-flex;align-items:center;gap:8px;padding:5px 8px 5px 6px;border:1px solid var(--border);border-radius:var(--r-ctl);background:var(--surface-2);cursor:pointer}
.util-color-fld input[type=color]{width:34px;height:26px;border:1px solid var(--border);border-radius:6px;background:none;padding:0;cursor:pointer}
.util-muestra{padding:3px 9px;border-radius:6px;font-variant-numeric:tabular-nums;font-weight:600;font-size:13px;white-space:nowrap}
.util-muestra[style*="--muestra"]{background:color-mix(in srgb,var(--muestra) 28%,#fff);color:color-mix(in srgb,var(--muestra) 55%,#000)}
[data-theme="oscuro"] .util-muestra[style*="--muestra"]{background:color-mix(in srgb,var(--muestra) 30%,transparent);color:color-mix(in srgb,var(--muestra) 55%,#fff)}
/* Tile MARGEN de Reportes teñido con las mismas bandas (03-sep, pedido Alejandro): el rótulo hereda la tinta */
.kpi.util-r .kpi-lbl,.kpi.util-o .kpi-lbl,.kpi.util-y .kpi-lbl,.kpi.util-g .kpi-lbl,.kpi.util-b .kpi-lbl{color:inherit;opacity:.8}
/* MEDALLAS del Top productos (03-sep-2026, pedido Alejandro): oro / plata / bronce para los 3 primeros,
   disco metálico con aro claro y numeral, y el MISMO destello lineal del wordmark (banda que barre cada
   10 s, escalonado 0,22 s entre medallas). Autocontenidas: se ven igual en tema claro y oscuro. */
.medalla{position:relative;display:inline-flex;width:22px;height:22px;border-radius:50%;align-items:center;justify-content:center;font:700 11px/1 var(--font-head);overflow:hidden;vertical-align:middle;box-shadow:inset 0 0 0 1.5px rgba(255,255,255,.55),0 1px 2px rgba(0,0,0,.28)}
.medalla .n{position:relative;z-index:1}
.medalla.oro{background:radial-gradient(circle at 35% 28%,#fff3c4 0%,#f4c453 38%,#cf9a1e 76%,#a8760f 100%);color:#4a3200}
.medalla.plata{background:radial-gradient(circle at 35% 28%,#ffffff 0%,#e4e8ee 38%,#aeb6c4 76%,#8a93a3 100%);color:#2e3440}
.medalla.bronce{background:radial-gradient(circle at 35% 28%,#ffddbd 0%,#dfa475 38%,#b8733c 76%,#8f5527 100%);color:#3d2208}
.med-glint{position:absolute;inset:0;background:linear-gradient(115deg,transparent 38%,rgba(255,255,255,.9) 50%,transparent 62%) 160% 0/260% 100% no-repeat;animation:med-destello 10s linear infinite}
.medalla.plata .med-glint{animation-delay:.22s}
.medalla.bronce .med-glint{animation-delay:.44s}
@keyframes med-destello{0%{background-position:160% 0}9%{background-position:-60% 0}100%{background-position:-60% 0}}

/* ─── Semáforo de EXISTENCIA (formato condicional del Kardex del Excel) ───
   rojo ≤ mín · amarillo dentro de la banda promedio [prom, reorden] · verde ≥ máx */
.ex-r{color:#c62324;background:#f4d3d3}
.ex-y{color:#8a6800;background:#ffffcc}
.ex-g{color:#354f10;background:#c6efce}
[data-theme="oscuro"] .ex-r{color:#ff9d9d;background:rgba(198,35,36,.28)}
[data-theme="oscuro"] .ex-y{color:#ffe98a;background:rgba(204,153,0,.22)}
[data-theme="oscuro"] .ex-g{color:#8ee6a8;background:rgba(53,79,16,.45)}

/* Header-toggle de las columnas de utilidad (alternar % ↔ moneda) */
.th-utog{background:none;border:none;padding:0;margin:0;font:inherit;color:inherit;text-transform:inherit;letter-spacing:inherit;cursor:pointer;text-decoration:underline dotted;text-underline-offset:3px;white-space:nowrap}
.th-utog:hover{color:var(--accent-text)}
/* El mismo alternador, pero en la barra del catálogo del inventario (08-sep): ahí no es un encabezado de
   tabla, así que se le pone cuerpo de control (borde, fondo) y se le quita el subrayado de puntos. */
.inv-cat-utog{flex:none;white-space:nowrap;font:600 12.5px/1 var(--font-ui);color:var(--text);padding:6px 11px;border:1px solid var(--border);border-radius:20px;background:var(--surface-2);text-decoration:none;box-shadow:inset 0 1px 0 var(--sutil-brillo)}
.inv-cat-utog:hover{border-color:var(--border-hover);color:var(--text)}
.inv-cat-utog:focus-visible{outline:2px solid var(--focus);outline-offset:2px}

/* Filas de compras clicables (abren el detalle) */
.compra-row{cursor:pointer}

/* Valor de inventario (colores pedidos: título naranja · costo rojo · P.V.P verde · P.V.M azul oscuro) */
.vi-bar{display:flex;align-items:baseline;gap:20px;flex-wrap:wrap;background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);padding:10px 16px;margin-bottom:14px;box-shadow:var(--shadow)}
.vi-t{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--accent-text)}
.vi-item{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.05em}
.vi-item b{font-size:16px;margin-left:6px;font-variant-numeric:tabular-nums;color:inherit;font-weight:700}
.vi-costo{color:var(--no)}
.vi-pvp{color:var(--ok)}
.vi-pvm{color:#002060}
[data-theme="oscuro"] .vi-pvm{color:#a9c9f5}
/* Dentro del modal del kardex la barra va "plana" (sin card dentro de card) */
.vi-plana{border:none;box-shadow:none;padding:0;margin:0;background:transparent;width:100%}
/* BARRA DE ACCIONES DE UN DETALLE (04-oct-2026, 0.157; pedido de Alejandro: «al mostrar las especificaciones de una venta,
   algunos botones son más grandes que otros, y tampoco están todos alineados»). .btn-ghost y .btn-danger no fijan su letra: un
   <a> hereda la de la página (15 px / 1,45 → 37,8 px de alto) y un <button> trae la del navegador (13,33 px → 32 px); el rojo ni
   siquiera era flex (33 px) y la barra alineaba por línea base, así que cada uno arrancaba a su altura. En la barra de un
   detalle (venta, cotización, devolución, gasto) conviven los tres: aquí se les da UNA caja —alto, letra, relleno e icono
   centrado— sea cual sea la etiqueta. La línea de totales baja a su propio renglón, desde el mismo borde izquierdo. */
.det-acciones{display:flex;flex-wrap:wrap;align-items:center;gap:8px 10px}
.det-acciones .btn-ghost,.det-acciones .btn-danger,.det-acciones .btn-primary{display:inline-flex;align-items:center;justify-content:center;gap:4px;height:32px;padding:0 14px;margin:0;font:inherit;font-size:13.5px;font-weight:600;line-height:1;white-space:nowrap;text-decoration:none}
.det-acciones>.muted:empty{display:none}   /* el hueco del aviso de impresión no ocupa sitio hasta que dice algo */
.det-acciones>.kardex-saldo{flex:1 0 100%;margin-top:2px}
.det-acciones+.kardex-saldo{display:block;margin-top:10px}
/* Los botones sueltos dentro del texto del detalle (la cuenta de la venta, la nota de cada devolución): la misma caja, menor. */
.dev-hist .btn-ghost{display:inline-flex;align-items:center;gap:4px;height:26px;padding:0 10px;font:inherit;font-size:12.5px;font-weight:600;line-height:1;white-space:nowrap;vertical-align:middle;text-decoration:none}

/* Kardex como modal: por defecto ocupa TODO el ancho de la pantalla; ⛶ lo extiende a toda la altura */
.kardex-modal{width:100vw;max-width:100vw;border-radius:0}
.kardex-modal .vm-head{border-radius:0}
.kardex-modal .vm-body{display:block;overflow:auto;padding:0 0 8px;max-height:calc(92vh - 60px)}
.kardex-modal.maximized{width:100vw;height:100vh;max-height:100vh;margin:0}
.kardex-modal.maximized .vm-body{max-height:calc(100vh - 60px)}
/* Las reglas genéricas ".venta-modal form" y ".venta-modal.maximized form" (pensadas para el ÚNICO form
   de la Caja) no aplican a los forms de fila del kardex: ni columna, ni height:100vh al maximizar. */
.kardex-modal form.fn-row{flex-direction:row;align-items:flex-end;max-height:none;height:auto}

/* Kardex: existencia clicable + panel de movimientos */
.kardex-link{cursor:pointer;text-decoration:underline dotted;text-underline-offset:3px}
.kardex-link:hover{box-shadow:inset 0 0 0 2px var(--accent)}
.kardex{margin-top:18px}
.kardex-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:10px}
.kardex-head .spacer{flex:1}
.kardex-saldo{color:var(--muted);font-size:13px}
.kardex-saldo b{color:var(--text)}
.kardex-tbl th{font-size:11px}
.mov-ent{color:var(--ok);font-weight:600}
.mov-sal{color:var(--no);font-weight:600}
.kardex-ajuste{margin-top:12px;border-top:1px dashed var(--border);padding-top:12px;align-items:center}

/* Venta anulada */
.badge.b-no{background:rgba(192,57,43,.16);color:var(--no)}
.fila-anulada{opacity:.55}
.fila-anulada td.num,.fila-anulada td.mono{text-decoration:line-through}

/* Devoluciones (parciales) */
.badge.b-warn{background:rgba(238,140,42,.18);color:var(--accent-text)}
.badge.b-ok{background:rgba(39,174,96,.16);color:var(--ok)}
.dev-hist{margin-top:12px;border-top:1px dashed var(--border);padding-top:10px}
.dev-hist>b{font-size:13px}
.dev-list{margin:6px 0 0;padding-left:18px;font-size:13px;color:var(--muted)}
.dev-list li{margin:2px 0}
.dev-list b{color:var(--no)}

/* ═══ MOVIMIENTO (pedido 05-jul): ruleta entre módulos · modales que emergen · bienvenida ═══
   Todo respeta prefers-reduced-motion: con "reducir movimiento" activo en Windows no hay animación. */

/* ─── Ruleta entre módulos: View Transitions ENTRE PÁGINAS (Chromium del Electron) ───
   El cromo (appbar + launcher) lleva nombre propio → queda quieto; solo el CONTENIDO rota como
   tarjeta de carrusel. La dirección (izq/der) sigue el ORDEN de los tiles: app.js la guarda al
   clicar y el <head> de la página siguiente la repone como html[data-nav-dir] (ver layout.js).
   Sin dirección (F5, atrás, redirects) el cambio es un fundido sobrio. */
@media (prefers-reduced-motion: no-preference){
  @view-transition{navigation:auto}
  .appbar{view-transition-name:appbar}
  .launcher{view-transition-name:launcher}
  .content{view-transition-name:contenido}
  ::view-transition-old(contenido){animation:vt-fundido-sale .18s ease both}
  ::view-transition-new(contenido){animation:vt-fundido-entra .18s ease both}
  html[data-nav-dir="der"]::view-transition-old(contenido){animation:vt-sale-izq .27s cubic-bezier(.3,.7,.35,1) both}
  html[data-nav-dir="der"]::view-transition-new(contenido){animation:vt-entra-der .27s cubic-bezier(.3,.7,.35,1) both}
  html[data-nav-dir="izq"]::view-transition-old(contenido){animation:vt-sale-der .27s cubic-bezier(.3,.7,.35,1) both}
  html[data-nav-dir="izq"]::view-transition-new(contenido){animation:vt-entra-izq .27s cubic-bezier(.3,.7,.35,1) both}
}
@keyframes vt-fundido-sale{to{opacity:0}}
@keyframes vt-fundido-entra{from{opacity:0}}
@keyframes vt-sale-izq{to{opacity:0;transform:perspective(1400px) translateX(-72px) rotateY(10deg)}}
@keyframes vt-entra-der{from{opacity:0;transform:perspective(1400px) translateX(72px) rotateY(-10deg)}}
@keyframes vt-sale-der{to{opacity:0;transform:perspective(1400px) translateX(72px) rotateY(-10deg)}}
@keyframes vt-entra-izq{from{opacity:0;transform:perspective(1400px) translateX(-72px) rotateY(10deg)}}
/* Motor sin View Transitions: al menos el contenido ENTRA con vida (no aparece de golpe) */
@supports not (view-transition-name: none){
  @media (prefers-reduced-motion: no-preference){
    .content{animation:vt-fundido-entra .2s ease}
  }
}

/* ─── Modales que EMERGEN del control que los abre ───
   app.js parchea showModal(): fija transform-origin en el punto del último click y aplica
   .dlg-anim → el dialog crece desde el botón/fila que lo generó (y el velo se funde).
   Los formularios inline de alta (+ Nuevo) entran con el mismo gesto. */
@media (prefers-reduced-motion: no-preference){
  dialog.dlg-anim{animation:dlg-pop .2s cubic-bezier(.2,.85,.3,1.02)}
  dialog.dlg-anim::backdrop{animation:dlg-velo .2s ease}
  .form-nuevo{animation:sube-suave .18s ease}
}
@keyframes dlg-pop{from{opacity:0;transform:scale(.55)}}
@keyframes dlg-velo{from{opacity:0}}
@keyframes sube-suave{from{opacity:0;transform:translateY(6px)}}

/* ─── Bienvenida post-login (views/login.js → paginaBienvenida) ───
   Mismo telón navy de la portada; saludo escalonado que se recoge solo antes de entrar al ERP. */
.saludo-wrap{min-height:100vh;display:flex;align-items:center;justify-content:center;background:radial-gradient(1100px 700px at 75% -10%,#22335c 0%,#0c1730 60%,#0a1226 100%)}
.saludo{display:flex;flex-direction:column;align-items:center;gap:16px;text-align:center;padding:24px}
.saludo-hola{color:#b9c6e4;font-size:14px;letter-spacing:.16em;text-transform:uppercase;font-weight:600;margin:0}
.saludo-nombre{color:#fff;font-family:'Poppins',sans-serif;font-weight:700;font-size:clamp(28px,5vw,44px);line-height:1.12;letter-spacing:-.01em;margin:0;max-width:80vw;overflow-wrap:break-word;word-break:break-word}
.saludo-linea{width:64px;height:4px;border-radius:2px;background:var(--accent)}
@media (prefers-reduced-motion: no-preference){
  .saludo>*{animation:saludo-sube .5s cubic-bezier(.2,.7,.3,1) both}
  .saludo>:nth-child(2){animation-delay:.12s}
  .saludo>:nth-child(3){animation-delay:.24s}
  .saludo>:nth-child(4){animation-delay:.36s}
  .saludo{animation:saludo-out .3s ease 1.4s both}
}
@keyframes saludo-sube{from{opacity:0;transform:translateY(14px)}}
@keyframes saludo-out{to{opacity:0;transform:translateY(-8px)}}

/* ═══ Reportes: barra de exportación · drill-down · badges de banda de existencia ═══ */
.rep-export{display:flex;align-items:center;gap:8px;margin:2px 0 14px;flex-wrap:wrap}
.rep-export .inp{padding:6px 10px}
.rep-export .chip{display:inline-flex;align-items:center;font-weight:600;padding:6px 12px}
.rep-export .chip .ic16{margin-right:6px}
.rep-export .chip.active .ic16 path{fill:currentColor}
/* MODO de la gráfica de Generales (22-sep, pedido de Alejandro: «dar a notar con letras del naranja principal del
   sistema si se está en modo mes, semana o día»): barra encima de la tarjeta con el modo en naranja, qué es cada
   barra y «Volver» a la derecha — antes el botón flotaba sobre la gráfica. */
.rep-modo{display:flex;align-items:center;gap:8px;flex-wrap:wrap;min-height:30px;margin:4px 2px 8px}
.rep-modo-t{color:var(--accent-text);font:800 15px var(--font-head);letter-spacing:.05em;text-transform:uppercase;white-space:nowrap}
.rep-modo-c{color:var(--muted);font-size:13px}
.rep-volver{font-size:12px;padding:4px 10px}
.rep-card{position:relative}
.ex-badge{display:inline-block;padding:2px 9px;border-radius:999px;font-size:11px;font-weight:700;line-height:1.5;white-space:nowrap}
.ex-min{background:var(--ex-min-bg);color:var(--ex-min-tx)}
.ex-prom{background:var(--ex-prom-bg);color:var(--ex-prom-tx)}
.ex-max{background:var(--ex-max-bg);color:var(--ex-max-tx)}
.ex-int{background:var(--surface-2);color:var(--muted)}
.row-anulada{opacity:.55}
/* 0.158 (pedido de Alejandro): en «Existencias por umbral» los números de cada umbral llevan el tono de su etiqueta de Estado
   —Mín el de «Mínimo», Prom. ini y Prom. fin el de «Promedio», Máx el de «Máximo»—, y su rótulo en el encabezado también. */
.grid .ex-col-min{color:var(--ex-min-tx)}
.grid .ex-col-prom{color:var(--ex-prom-tx)}
.grid .ex-col-max{color:var(--ex-max-tx)}
.grid td.ex-col-min,.grid td.ex-col-prom,.grid td.ex-col-max{font-weight:600}

/* ─── Licencia: activación, bloqueo y banner de gracia ─── */
.lic-card{width:420px;max-width:calc(100vw - 32px);gap:13px}
.lic-cod{text-align:center;letter-spacing:2px;font-weight:600;text-transform:uppercase;font-size:16px}
.lic-candado{display:flex;justify-content:center;color:var(--accent-text);margin:2px 0 -2px}
.lic-titulo{margin:0;text-align:center;font-size:19px}
.lic-msg{margin:0;text-align:center;color:var(--muted);font-size:13px;line-height:1.45}
.lic-contacto{margin:2px 0 0;text-align:center;color:var(--muted);font-size:12px}
.lic-banner{background:rgba(242,155,63,.16);color:var(--accent-text);border-bottom:1px solid rgba(242,155,63,.35);padding:7px 16px;text-align:center;font-size:13px;font-weight:600}
/* VENTA SIN SERVIDOR (28-sep-2026, core/isla.js): el aviso bajo el lanzador del motor de la caja. Azul (informa); rojo si se cumplió el tope. */
.isla-aviso{background:rgba(47,125,209,.14);color:var(--text);border-bottom:1px solid rgba(47,125,209,.4);padding:7px 16px;text-align:center;font-size:13px}
.isla-aviso.isla-vencida{background:rgba(217,70,62,.16);border-bottom-color:rgba(217,70,62,.45)}
.isla-btn{margin-left:6px;padding:3px 12px;border-radius:7px;border:none;background:var(--accent,#EE8C2A);color:#fff;font-weight:600;font-size:12.5px;cursor:pointer}
.isla-btn:disabled{opacity:.6;cursor:default}

/* ── Repetido en el carrito (pedido de Alejandro, 17-ago) ─────────────────────────────────────────
   Al agregar un artículo que YA está en la lista se suma en su renglón; el renglón destella un
   momento y debajo de la tabla queda dicho qué pasó. El color va en rgba del acento y no en un
   token sólido: así el destello funciona igual en claro y en oscuro sin definirlo dos veces. */
.fila-fusion>td{animation:fusion-flash 1.8s ease-out 1}
@keyframes fusion-flash{0%{background:rgba(238,140,42,.30)}70%{background:rgba(238,140,42,.14)}100%{background:transparent}}
.vm-fusion{margin:6px 2px 0;font-size:12.5px;color:var(--accent-text)}

/* ── Re-precio sugerido en Compras (pedido de Alejandro, 21-ago) ─────────────────────────────────
   El aviso vivo bajo la fila de captura toma las bandas del heatmap de utilidad (util-r/o/y/g/b,
   con su variante oscura ya definida); vacío conserva el renglón (min-height) para que aparecer y
   desaparecer no haga saltar el modal. La fila .c-reprecio cuelga de su línea del detalle. */
.c-util-aviso{min-height:20px;margin-top:6px;font-size:12.5px;padding:2px 8px;border-radius:6px;display:inline-block}
.c-util-aviso:empty{padding:2px 0}
.c-reprecio td{border-top:0;padding-top:0;padding-bottom:8px;font-size:12.5px}
.c-rep-btn{padding:2px 10px;font-size:12px}
.c-rep-chip{padding:2px 8px;border-radius:6px;font-size:12px}

/* ── Ajuste masivo de precios (modal, 24-ago) ────────────────────────────────────────────────────
   La tabla de preview y el historial comparten .ajp-t (compacta, números a la derecha); los chips
   de precio nuevo reusan las bandas util-* del heatmap. Los checkboxes P.V.P./P.V.M. se alinean a
   la base de los .fld vecinos (flex-end del .fn-row + padding que compensa el label de arriba). */
.ajp-form{align-items:flex-end;flex-wrap:wrap}
.ajp-chks{display:flex;gap:12px;padding-bottom:9px}
.ajp-chk{display:flex;align-items:center;gap:5px;font-size:13.5px;white-space:nowrap;cursor:pointer}
.ajp-sum{margin:14px 0 2px}
.ajp-t{width:100%;border-collapse:collapse;font-size:13px;margin:8px 0 2px}
.ajp-t th,.ajp-t td{padding:4px 8px;border-bottom:1px solid var(--border);text-align:left}
.ajp-t th{color:var(--th-text);background:var(--th-bg)}
.ajp-t .num{text-align:right;white-space:nowrap}
.ajp-t .viejo,.ajp-res .viejo{color:var(--muted)}
.ajp-res .btn-primary{margin-top:10px}
.ajp-h{margin:18px 0 4px;font-size:15px}

/* ─── MODO CATÁLOGO de la Caja (05-sep-2026; lienzo "Caja Modo Catálogo" aprobado por Alejandro) ───
   Conmutador Formulario | Catálogo en la cabecera del modal (.seg, sobre el navy del header) y, en
   modo catálogo, el cuerpo pasa a REJILLA de 2 columnas: catálogo (buscador + chips + tarjetas por
   tandas) a la izquierda y el detalle + pie de siempre apilados a la derecha. Los campos de la fila
   de captura siguen en el DOM (ocultos): las tarjetas los manejan y todo el JS de la caja sigue igual. */
.seg{display:inline-flex;background:rgba(255,255,255,.12);border-radius:8px;padding:3px;gap:3px;margin-left:8px}
.seg button{border:0;border-radius:6px;padding:5px 12px;font:inherit;font-size:13px;font-weight:600;color:#fff;background:transparent;cursor:pointer;display:inline-flex;align-items:center;gap:6px;white-space:nowrap}
.seg button:hover{background:rgba(255,255,255,.14)}
.seg button.on{background:var(--accent);color:var(--accent-ink)}
.vm-head .seg .ic16{fill:currentColor;width:14px;height:14px;margin:0}
.v-add-wrap[hidden]{display:none}
.v-cat{display:flex;flex-direction:column;flex:1;min-height:0}
.v-cat[hidden]{display:none}
/* 🪤 08-sep: la barra NO cabía en una línea. Medido a 1366×768 con la Caja maximizada: la columna del Catálogo
   da 805 px y las piezas suman 990 (buscador 240 mínimo + «Nuevo Producto» 156 + Alfabética|Coincidente 168 +
   contador 74 + S/M/L 89 + visor de monedas 190 + huecos). Ya ANTES del botón se salían 17 px y el visor (USD)
   (VES)(COP) del 07-sep quedaba cortado por el borde. Con flex-wrap nada se pierde: lo que no entra baja a una
   segunda línea (row-gap corto para que cueste lo menos posible de rejilla). */
.v-cat-top{display:flex;flex-wrap:wrap;align-items:center;gap:6px 12px;margin-bottom:8px}
.v-cat-buscar{flex:0 1 460px;min-width:240px}
/* «Nuevo Producto» del Catálogo (08-sep): pegado al buscador, sin estirarse ni partir el rótulo. */
.v-cat-top .v-cat-nuevo{flex:none;white-space:nowrap}
.v-cat-buscar .inp{height:34px}
.v-cat-conteo{font-size:12.5px;white-space:nowrap}
/* Barra de categorías: nombres ORIGINALES en una fila que se desplaza (rueda del mouse / arrastre de la barra) */
.v-cat-chips{margin-bottom:6px;flex:none;flex-wrap:nowrap;overflow-x:auto;overflow-y:hidden;padding-bottom:6px;scrollbar-width:thin;scrollbar-color:var(--border-hover) var(--surface-2)}
.v-cat-chips::-webkit-scrollbar{height:8px}
.v-cat-chips::-webkit-scrollbar-track{background:var(--surface-2);border-radius:6px}
.v-cat-chips::-webkit-scrollbar-thumb{background:var(--border-hover);border-radius:6px}
.v-cat-chips::-webkit-scrollbar-thumb:hover{background:var(--accent)}
.v-cat-chips .chip{padding:5px 12px;font-size:13px;white-space:nowrap}
.v-cat-chips .chip[hidden]{display:none}
.v-cat-scroll{flex:1;min-height:0;overflow:auto;padding:2px 2px 12px}
/* Columnas FIJAS por tamaño (decisión 05-sep): S 5 · M 4 · L 3 — a 1366 px la existencia y el contador caben */
.v-cat-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px}
.cat-card{display:flex;flex-direction:column;border:1px solid var(--border);border-radius:var(--r-card);background:var(--surface);overflow:hidden;box-shadow:var(--shadow-sm)}
.cat-card:hover{border-color:var(--border-hover);box-shadow:var(--shadow-md)}
/* La foto va sobre BLANCO aunque el tema sea oscuro: es contenido (fotos de producto), como en .v-img-box */
.cat-foto{height:118px;background:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;padding:6px;flex:none}
.cat-foto img{max-width:100%;max-height:100%;object-fit:contain;display:block}
.cat-sinfoto{display:flex;flex-direction:column;align-items:center;gap:4px;color:var(--icon-2);font-size:11px}
.cat-sinfoto i{font-style:normal}
.cat-info{padding:8px 8px 9px;display:flex;flex-direction:column;gap:4px;flex:1}
.cat-nombre{font-size:12.5px;font-weight:600;line-height:1.25;height:31px;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}   /* 2 líneas con puntos suspensivos: un nombre de 3 líneas ya no se corta a media letra */
.cat-marca{font-size:11px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cat-fila{display:flex;align-items:baseline;justify-content:space-between;gap:6px;margin-top:2px}
.cat-precio b{font-size:15px;font-variant-numeric:tabular-nums}
/* Rótulo de tarifa a la IZQUIERDA del precio (14-sep): "P.V.P COP 26.000". Verde P.V.P / azul P.V.M,
   los mismos colores que el select de tarifa del modo Formulario (.tar-pvp / .tar-pvm). */
.cat-tar-lbl{display:inline-block;font-size:10px;font-weight:600;letter-spacing:.02em;line-height:1.5;padding:0 4px;margin-right:4px;border-radius:3px;vertical-align:1px}
.cat-tar-lbl.pvp{background:rgba(25,135,84,.16);color:#198754}
.cat-tar-lbl.pvm{background:rgba(13,110,253,.18);color:#0d6efd}
.cat-tar-lbl.falta{background:rgba(238,140,42,.18);color:#c26a10}
[data-theme="oscuro"] .cat-tar-lbl.falta{color:#eb9d4a}
[data-theme="oscuro"] .cat-tar-lbl.pvp{color:#4ec98a}
[data-theme="oscuro"] .cat-tar-lbl.pvm{color:#6ea8fe}
/* El mismo rótulo en la columna Tarifa de una tabla de detalle (venta, cotización; 04-oct): un punto más grande y sin el
   margen que en la tarjeta lo separa del precio. */
.cat-tar-lbl.tar-celda{font-size:11px;padding:1px 6px;margin-right:0;vertical-align:baseline;white-space:nowrap}
/* La tarifa cambia lo que se COBRA: su segmentado se marca con el color de la tarifa activa, no con
   el gris de los mandos de vista (tamaño, moneda) que tiene al lado. */
.seg-tar button.on[data-tar="pvp"]{background:rgba(25,135,84,.16);color:#198754}
.seg-tar button.on[data-tar="pvm"]{background:rgba(13,110,253,.18);color:#0d6efd}
[data-theme="oscuro"] .seg-tar button.on[data-tar="pvp"]{color:#4ec98a}
[data-theme="oscuro"] .seg-tar button.on[data-tar="pvm"]{color:#6ea8fe}
.cat-stock{display:inline-block;padding:2px 7px;border-radius:20px;font-size:11px;font-weight:700;line-height:1.3;border:1px solid transparent;white-space:nowrap}
.cat-stock.ok{background:color-mix(in srgb,var(--ok) 14%,transparent);color:var(--ok);border-color:color-mix(in srgb,var(--ok) 30%,transparent)}
.cat-stock.bajo{background:color-mix(in srgb,var(--accent) 18%,transparent);color:var(--accent-text);border-color:color-mix(in srgb,var(--accent) 40%,transparent)}
.cat-stock.no{background:color-mix(in srgb,var(--no) 14%,transparent);color:var(--no);border-color:color-mix(in srgb,var(--no) 30%,transparent)}
.cat-accion{margin-top:auto;padding-top:4px}
.cat-add{height:34px;width:100%;font-size:13.5px;padding:0 10px;display:inline-flex;align-items:center;justify-content:center;gap:6px}
/* Contador −/+ del artículo que YA está en el carrito (mismo slate de los botones normales) */
.cat-stepper{display:flex;align-items:center;justify-content:space-between;border-radius:var(--r-ctl);background:linear-gradient(180deg,var(--btn-sec-hover) 0%,var(--btn-sec) 100%);border:1px solid var(--btn-sec-borde);color:var(--btn-sec-ink);height:34px;overflow:hidden;box-shadow:inset 0 1px 0 rgba(255,255,255,.16),0 1px 2px rgba(16,24,40,.18)}
.cat-stepper button{border:0;background:transparent;color:inherit;width:40px;height:34px;font-size:18px;cursor:pointer;font-family:inherit;line-height:1}
.cat-stepper button:hover{background:rgba(255,255,255,.14)}
.cat-stepper b{font-size:14px;font-variant-numeric:tabular-nums}
.cat-nodisp{display:block;text-align:center;font-size:12px;color:var(--muted);padding:8px 0}
.v-cat-mas{text-align:center;padding:10px;color:var(--muted);font-size:12px}
.v-cat-mas[hidden]{display:none}
/* Cuerpo del modal en modo catálogo: rejilla — fila superior (datos | tasas) a lo ancho; debajo,
   catálogo (izq) | detalle + pie apilados (der, 440px como el pie compacto). */
.venta-modal.modo-cat .vm-body{display:grid;grid-template-columns:minmax(0,1fr) 440px;grid-template-rows:auto minmax(0,1fr)}
.venta-modal.modo-cat #v-row-top{grid-column:1/-1}
.venta-modal.modo-cat #v-sec-agregar{grid-column:1;grid-row:2;display:flex;flex-direction:column;min-height:0;border-bottom:0;border-right:1px solid var(--border);padding-bottom:0}
.venta-modal.modo-cat #v-sec-agregar .v-add-wrap{display:none}
.venta-modal.modo-cat .vm-row-bottom{grid-column:2;grid-row:2;flex-direction:column;min-height:0}
.venta-modal.modo-cat .vm-detalle{flex:1;min-height:0;border-right:0;border-bottom:1px solid var(--border)}
/* Padding lateral más corto SOLO aquí: la tabla del detalle (Artículo + Cant/Precio/Subtotal sin partir +
   papelera) mide ~406px y con los 20px de siempre no cabía en la columna de 440 → barra horizontal. */
.venta-modal.modo-cat .vm-detalle{padding-left:12px;padding-right:12px;overflow-x:hidden}
/* 07-sep (v3): el pie mide su contenido — plegado = totales + ficha (el Detalle recupera el alto); la regla vive con el cajón v3 al final. */
.venta-modal.modo-cat .vm-foot{width:auto;flex:none;height:auto;max-height:none}
/* Detalle compacto en la columna angosta: sin Tarifa ni Desc. (siguen en el carrito, solo no se pintan) */
.venta-modal.modo-cat .vm-detalle .grid th:nth-child(2),.venta-modal.modo-cat .vm-detalle .grid td:nth-child(2),
.venta-modal.modo-cat .vm-detalle .grid th:nth-child(5),.venta-modal.modo-cat .vm-detalle .grid td:nth-child(5){display:none}
.venta-modal.modo-cat .vm-detalle .grid{font-size:13px}
.venta-modal.modo-cat .vm-detalle .grid td{padding:5px 8px}
.venta-modal.modo-cat .vm-detalle .grid th{padding:6px 8px}
@media (max-height:860px){
  .venta-modal.maximized.modo-cat .vm-foot{width:auto}
  .venta-modal.maximized.modo-cat #v-sec-agregar{padding-bottom:0}
}

/* ─── CANTIDAD Y DESCUENTO EDITABLES + ZOOM + TAMAÑO DE TARJETA (05-sep, pedido de Alejandro tras probar el catálogo) ─── */
/* Detalle (los dos modos): − N + y descuento % editables en la fila */
.det-step{display:inline-flex;align-items:center;border:1px solid var(--border);border-radius:7px;overflow:hidden;background:var(--surface-2);vertical-align:middle}
.det-step button{border:0;background:transparent;color:var(--text);width:22px;height:24px;font-size:15px;line-height:1;cursor:pointer;font-family:inherit;padding:0}
.det-step button:hover{background:var(--row-hover)}
.det-qty{width:46px;height:24px;border:0;background:transparent;text-align:center;font:inherit;font-size:13px;font-variant-numeric:tabular-nums;color:var(--text);padding:0 2px}
.det-qty:focus{outline:none;background:var(--surface);box-shadow:inset 0 0 0 2px var(--accent)}
/* Costo y subtotal editables en el Detalle de Compras (30-sep): la misma celda que la cantidad, alineada a la derecha y más ancha. */
.det-costo,.det-sub{width:92px;text-align:right;border:1px solid var(--border);border-radius:7px;background:var(--surface-2);padding:0 6px}
.det-sub{font-weight:600}
.det-pct{width:46px;border:1px solid var(--border);border-radius:7px;background:var(--surface-2);text-align:right;padding:0 4px;margin-right:2px;vertical-align:middle}
/* DESCUENTO EN MONTO (0.158, pedido de Alejandro: «la celda originalmente solo podria mostrar 3 digitos, asi que hay que expandirla
   un poco, lo suficiente para que quepan 6 digitos»): con el encabezado en «Desc. [moneda]» la casilla de cada renglón mide lo que
   pide su monto TAL COMO QUEDA al guardar (money(): siempre con sus dos decimales) —field-sizing:content—: 60 px hasta «999,99»,
   62 con «9.999,99», 70 con «12.345,67» y 78 con «999.999,99» (seis cifras enteras a 13 px; medido), con tope de 84; relleno de
   3 px y sin el margen que en % la separa del signo. Un ancho fijo que cupiera «999.999,99» (78 px o más) le quitaba ese ancho al
   nombre en todos los renglones, aun con «0,00», y en el Catálogo con un producto de tres tipos y sufijo hacía deslizar el Detalle
   16 px o más; así solo cede ancho cuando el monto lo pide (ese renglón cabe sin deslizarse hasta «9.999,99»). Un navegador sin
   field-sizing la deja en el tope (84). En % la casilla hace lo mismo con su propio rango (ajuste 5, abajo). La regla del Catálogo
   (más abajo, 38 px en %) también cede en monto. */
.det-pct.det-monto{width:auto;field-sizing:content;min-width:60px;max-width:84px;padding:0 3px;margin-right:0}
/* DESCUENTO EN PORCENTAJE (0.158, ajuste 5, decidido por Alejandro): money() lo repinta con dos decimales y la casilla fija
   cortaba: en Catálogo (38 px) ya «10,00», «12,50» o «55,56» (35 px de texto a 13 px en 28 útiles) y «100,00» (43 px) ni en los
   46 del Formulario. Como en monto, mide lo que pide su porcentaje (field-sizing:content) desde su ancho de siempre —46 px; 38 en
   Catálogo, más abajo— hasta lo que pide «100,00», el tope de un %: 53 px medidos; 56 de tope por el redondeo de la letra. Mismo
   relleno y el mismo margen antes del signo. Un navegador sin field-sizing la deja en el tope (56). */
.det-pct:not(.det-monto){width:auto;field-sizing:content;min-width:46px;max-width:56px}
.grid td.det-cant,.grid td.det-desc{white-space:nowrap}
/* UN RENGLÓN POR PRODUCTO, UN CONTADOR POR TIPO (02-oct-2026 tarde, idea de Alejandro): en la celda Cantidad los contadores
   van apilados (.det-stk > .det-ln), uno por tipo que admite la ficha, rotulados «Ent.» / «Unit.» / «Fracc.» dentro del
   contador; Precio, Desc. y Subtotal llevan una pila igual para que cada línea quede a la altura de su contador. El contador
   de un tipo sin usar va en 0, punteado y atenuado: es el lugar que se guarda. El sufijo («*10TAB») va a la izquierda del tipo. */
.det-stk{display:flex;flex-direction:column;align-items:flex-end;gap:3px}
.det-ln{display:flex;align-items:center;justify-content:flex-end;gap:6px;height:26px;white-space:nowrap}
.det-ln.vacia .det-step{opacity:.55;border-style:dashed}
.det-ln.vacia .det-qty{color:var(--muted)}
.det-g td{vertical-align:middle}
.det-g.multi td{vertical-align:top}
.det-g.multi td:first-child,.det-g.multi td:nth-child(2){padding-top:11px}
.det-eq{font-size:11px;margin-left:4px}
.det-step .det-tv{width:auto;min-width:46px;height:24px;padding:0 6px;display:inline-flex;align-items:center;justify-content:center;font-family:var(--font-ui);font-size:11.5px;font-weight:700;line-height:1;white-space:nowrap;color:var(--violet);background:rgba(51,69,110,.14);border-right:1px solid var(--border);box-sizing:border-box}
.det-step .det-tv.fijo{background:transparent;color:var(--muted)}
[data-theme="oscuro"] .det-step .det-tv{background:rgba(150,170,220,.16)}
[data-theme="oscuro"] .det-step .det-tv.fijo{background:transparent}
#venta-dialog.modo-cat .vm-detalle .det-step .det-tv{width:auto;min-width:34px;padding:0 3px;font-size:11px}
#venta-dialog.modo-cat .vm-detalle .det-ln{gap:3px}
/* En Catálogo la columna es angosta y el renglón con tres contadores y sufijo es lo más ancho que pinta: contadores de 18 px,
   cantidad de 30 y el descuento de 38, para que Artículo conserve ~100 px. */
#venta-dialog.modo-cat .vm-detalle .det-g .det-step button{width:18px}
#venta-dialog.modo-cat .vm-detalle .det-g .det-qty:not(.det-precio):not(.det-pct){width:30px}
#venta-dialog.modo-cat .vm-detalle .det-g .det-pct:not(.det-monto){width:auto;min-width:38px}   /* 0.158 (ajuste 5): en %, de 38 px a lo que pide «100,00» (tope 56) */
#venta-dialog.modo-cat .vm-detalle .det-g .det-pct.det-monto{width:auto}   /* 0.158: en monto, lo que pide el monto (60 a 84 px) como en Formulario */
#venta-dialog.modo-cat .vm-detalle .det-g .tv-suf{padding:0 3px}
/* SUFIJO del tipo vendido (02-oct-2026): qué es la unidad o la fracción de ese producto («*10TAB»), tras el nombre, en el
   naranja cálido del sistema. No se parte: si no cabe en la línea, baja entero. En la ficha, el campo lleva el mismo tono. */
.tv-suf{display:inline-block;white-space:nowrap;padding:0 6px;border-radius:6px;font-family:var(--font-ui);font-size:12px;font-weight:700;line-height:1.55;color:var(--accent-text);background:rgba(238,140,42,.24);border:1px solid rgba(238,140,42,.6);vertical-align:1px}
#venta-dialog.modo-cat .vm-detalle .tv-suf{font-size:10px;padding:0 4px}
.inp.inp-suf{width:112px;font-weight:700;color:var(--accent-text);border-color:rgba(238,140,42,.7)}
.inp.inp-suf::placeholder{font-weight:400;color:var(--muted);opacity:.7}
/* La misma abreviatura delante de la cantidad en el detalle de una venta o una cotización ya guardadas. */
.tv-abr{font-size:11.5px;font-weight:700;color:var(--muted);margin-right:2px}
/* Precio a mano (22-sep): con «Editar precios» la celda Precio del Detalle se escribe; el renglón tocado queda en
   naranja con ✎, y el campo Precio de la fila de captura se enmarca en naranja para decir que ya se puede escribir. */
.det-precio{width:92px;border:1px solid var(--border);border-radius:7px;background:var(--surface-2);text-align:right;padding:0 6px;vertical-align:middle}
.det-precio.manual,.det-manual{color:var(--accent-text);font-weight:700}
.det-manual{margin-right:4px;font-size:12px}
#v-precio.v-precio-abierto{border-color:var(--accent);box-shadow:var(--ring)}
/* Tarjeta: contador libre + Agregar + descuento; ya en la venta el contador va en slate */
.cat-accion{margin-top:auto;padding-top:4px;display:flex;flex-wrap:wrap;gap:6px;align-items:center}
.cat-step{display:flex;align-items:center;border:1px solid var(--border);border-radius:var(--r-ctl);overflow:hidden;background:var(--surface-2);height:32px;flex:none}
.cat-step button{border:0;background:transparent;color:var(--text);width:24px;height:32px;font-size:16px;cursor:pointer;font-family:inherit;line-height:1;padding:0}
.cat-step button:hover{background:var(--row-hover)}
.cat-step .cat-qty{width:32px;height:30px;border:0;background:transparent;text-align:center;font:inherit;font-size:14px;font-weight:600;font-variant-numeric:tabular-nums;color:var(--text);padding:0}
.cat-step .cat-qty:focus{outline:none;box-shadow:inset 0 0 0 2px var(--accent)}
.cat-step.en{background:linear-gradient(180deg,var(--btn-sec-hover) 0%,var(--btn-sec) 100%);border-color:var(--btn-sec-borde);box-shadow:inset 0 1px 0 rgba(255,255,255,.16),0 1px 2px rgba(16,24,40,.18)}
.cat-step.en button,.cat-step.en .cat-qty{color:var(--btn-sec-ink)}
.cat-step.en button:hover{background:rgba(255,255,255,.14)}
.cat-accion .cat-add,.cz-acciones .cat-add{flex:0 0 auto;width:auto;min-width:0;height:32px;padding:0 8px;font-size:12.5px;gap:3px}   /* width:auto: la regla base .cat-add lo ponía a 100 % y bajaba de fila */
.cat-add-ic{font-weight:700;font-size:15px;line-height:1}
.cat-en{flex:1;font-size:12px;font-weight:600;color:var(--ok);text-align:center;white-space:nowrap}
.cat-desc{display:flex;align-items:center;gap:4px;font-size:11.5px;color:var(--muted);width:100%}
.cat-desc .cat-pct{width:46px;height:24px;border:1px solid var(--border);border-radius:7px;background:var(--surface-2);text-align:right;font:inherit;font-size:12px;padding:0 4px;color:var(--text)}
.cat-desc .cat-pct:focus{outline:none;border-color:var(--accent);box-shadow:var(--ring)}
/* Lupa sobre la foto (aparece al pasar) */
.cat-foto{position:relative}
.cat-lupa{position:absolute;right:6px;top:6px;width:24px;height:24px;border-radius:7px;background:rgba(31,36,48,.55);color:#fff;display:inline-flex;align-items:center;justify-content:center;opacity:0;transition:opacity .12s ease;pointer-events:none}
.cat-card:hover .cat-lupa{opacity:1}
/* Zoom: foto grande + datos + la misma zona de acción */
.cat-zoom .cz-body{display:flex;gap:18px;padding:16px 20px;overflow:auto}
.cz-foto{width:380px;height:380px;flex:none;background:#fff;border-radius:var(--r-card);display:flex;align-items:center;justify-content:center;border:1px solid var(--border);padding:8px}
.cz-foto img{max-width:100%;max-height:100%;object-fit:contain;display:block}
.cz-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:8px;align-items:flex-start}
/* 04-oct-2026: en escritorio la ficha va AL LADO de la foto (el hueco de la derecha siempre estuvo vacío: .vm-body es una
   columna y esta regla no decía «fila»). Con la regleta por tipo, la ficha debajo de la foto dejaba «Agregar» fuera de la
   ventana a 1366×768. En el teléfono sigue debajo. */
@media (min-width:721px){.cat-zoom .cz-body{flex-direction:row}}
.cz-nombre{font-size:16px;font-weight:600;line-height:1.3}
.cz-precio b{font-size:24px;font-variant-numeric:tabular-nums;font-family:var(--font-display)}
.cz-acciones{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-top:6px;width:100%}
.cz-acciones .cat-add{flex:0 0 auto;height:36px;padding:0 18px}
.cz-acciones .cat-desc{width:auto}
/* Tamaño de tarjeta S/M/L (segmentado claro, en el panel; el .seg del header es para el navy) */
/* Título «Detalle» de la Caja con el descuento global (30-sep): el alternador Por producto | Global y su casilla, a la derecha. */
.v-det-tit{display:flex;align-items:center;justify-content:space-between;gap:8px;flex-wrap:wrap}
.v-dg{display:inline-flex;align-items:center;gap:6px;text-transform:none;letter-spacing:0;font-family:var(--font-ui);font-weight:500}
.v-dg-l{font-size:12px;color:var(--muted)}
#v-dg-val{width:86px;height:26px;padding:0 6px;text-align:right;font-size:13px}
#v-dg-un{font-size:12px}
.venta-modal .v-dg .seg{margin-left:0}
.seg-tam{background:var(--surface-2);border:1px solid var(--border);margin-left:0;padding:2px;border-radius:7px}
.seg-tam button{color:var(--muted);padding:3px 9px;font-size:12px;border-radius:5px}
.seg-tam button:hover{background:var(--row-hover);color:var(--text)}
.seg-tam button.on{background:var(--violet);color:#fff}
[data-theme="oscuro"] .seg-tam button.on{color:#14161b}
.v-cat-grid.tam-s{grid-template-columns:repeat(5,minmax(0,1fr));gap:10px}
.v-cat-grid.tam-s .cat-foto{height:100px}
.v-cat-grid.tam-s .cat-add-txt{display:none}
/* En S (5 columnas) precio y existencia no caben en una línea: la etiqueta baja a la siguiente */
.v-cat-grid.tam-s .cat-fila{flex-wrap:wrap;gap:2px 6px}
.v-cat-grid.tam-s .cat-precio b{font-size:14px}
.v-cat-grid.tam-l{grid-template-columns:repeat(3,minmax(0,1fr));gap:14px}
.v-cat-grid.tam-l .cat-foto{height:190px}
/* En modo catálogo el Detalle gana ancho (500) y muestra Desc.; solo se esconde Tarifa */
.venta-modal.modo-cat .vm-body{grid-template-columns:minmax(0,1fr) 520px}
/* Celdas más apretadas en la columna: con − N + y el descuento editable la tabla mide ~475px */
.venta-modal.modo-cat .vm-detalle .grid td{padding:5px 6px}
.venta-modal.modo-cat .vm-detalle .grid th{padding:6px 6px}
.venta-modal.modo-cat .vm-detalle .grid th:nth-child(5),.venta-modal.modo-cat .vm-detalle .grid td:nth-child(5){display:table-cell}
/* ─── DETALLE QUE NO SE SALE (27-sep-2026, Alejandro en la Caja 2: «al agregar cierta cantidad de productos en el Detalle se
   descuadran las filas: deja de verse el icono de eliminar y el SUBTOTAL se ve a medias o por fuera de la ventana»). Medido con la
   base real: en Catálogo la tabla medía ~597 px en una caja de ~480 y overflow-x:hidden la recortaba sin avisar (papelera 82 px
   fuera, Subtotal 48); en Formulario, a 1280 px, la papelera quedaba tras una barra horizontal. Causa: los nombres de farmacia
   juntan palabras con «+» («ACETAM.+FENIL.+CLORF.») y eso no es un punto de corte, así que la columna Artículo no podía angostarse.
   Ahora el nombre se parte después de + * / (<wbr> en el render del Detalle) y, si aun así no cabe, en cualquier letra; la barra
   vertical tiene su hueco reservado desde el primer renglón (no mueve nada al aparecer); y si algo raro no cupiera, la caja se
   desplaza de lado en vez de esconder la papelera. Solo la Caja: Compras tiene su propio ajuste de columnas. */
#venta-dialog .vm-detalle{scrollbar-gutter:stable}
#venta-dialog .vm-detalle .grid td:first-child{overflow-wrap:anywhere}
#venta-dialog.modo-cat .vm-detalle{overflow-x:auto}
/* El ancho que se ahorra va al NOMBRE: la papelera a su medida (la regla general .acciones le daba 72 px a un botón de 29). En la
   columna angosta del Catálogo, además, contador más justo y rellenos laterales de 4 px. Ojo: el descuento es «det-qty det-pct» y el
   precio a mano «det-qty det-precio»: el ancho del contador no debe alcanzarlos (con 32 px el descuento se leía «0,0(»). */
#venta-dialog .vm-detalle .grid td.acciones{width:40px}
#venta-dialog.modo-cat .vm-detalle .grid td,#venta-dialog.modo-cat .vm-detalle .grid th{padding-left:4px;padding-right:4px}
#venta-dialog.modo-cat .vm-detalle .grid td:first-child,#venta-dialog.modo-cat .vm-detalle .grid th:first-child{padding-left:6px}
#venta-dialog.modo-cat .vm-detalle .grid td.acciones{width:34px}
#venta-dialog.modo-cat .vm-detalle .det-step button{width:20px}
#venta-dialog.modo-cat .vm-detalle .det-qty:not(.det-precio):not(.det-pct){width:32px}
#venta-dialog.modo-cat .vm-detalle .det-pct{width:44px}
#venta-dialog.modo-cat .vm-detalle .det-precio{width:82px}

/* ─── MÉTODO DE PAGO como FICHAS (05-sep, propuesta B; la elegida en naranja). El <select> queda como estado oculto ─── */
.mp-sel{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none;overflow:hidden;left:0;bottom:0}
#v-metodo-wrap,#c-metodo-wrap{position:relative}
.mp-bar{display:flex;gap:8px;overflow-x:auto;scrollbar-width:thin;padding-bottom:2px;max-width:100%}
.mp-ficha{flex:none;display:inline-flex;flex-direction:column;align-items:center;justify-content:center;gap:5px;width:96px;height:62px;border-radius:10px;border:1px solid var(--border);background:var(--surface-2);color:var(--text);font:600 11.5px/1.15 var(--font-ui);text-align:center;box-shadow:inset 0 1px 0 var(--sutil-brillo);cursor:pointer;padding:0 6px;transition:border-color .12s ease,box-shadow .12s ease}
.mp-ficha .ic{color:var(--muted);display:inline-flex}
.mp-ficha .ic svg{width:22px;height:22px}
.mp-ficha:hover{border-color:var(--border-hover)}
.mp-ficha.on{background-color:var(--accent);background-image:linear-gradient(115deg,#ffa53f 0%,#ee8c2a 45%,#f2705a 100%);border-color:transparent;color:var(--accent-ink);box-shadow:0 3px 10px rgba(238,140,42,.3)}
.mp-ficha.on .ic{color:inherit}
.mp-ficha:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
/* ─── Iconos de categoría (05-sep): botón por fila del gestor + rejilla de SOLO iconos; chips y hueco de foto del catálogo ─── */
.cat-row{grid-template-columns:auto 1fr 110px auto}
.cat-ico-btn{width:34px;height:34px;border-radius:8px;border:1px solid var(--border);background:var(--surface-2);color:var(--violet);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;flex:none;padding:0}
.cat-ico-btn.vacio{border-style:dashed;color:var(--muted)}
.cat-ico-btn:hover{border-color:var(--border-hover)}
#cat-dialog .vm-body{position:relative}
.cat-ico-pop{position:absolute;z-index:40;background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);box-shadow:var(--shadow-md);padding:10px;width:392px;display:flex;flex-direction:column;gap:8px}
.cat-ico-pop[hidden]{display:none}
.cat-ico-grid{display:grid;grid-template-columns:repeat(9,1fr);gap:6px}
.cat-ico-grid button{height:36px;border-radius:8px;border:1px solid var(--border);background:var(--surface-2);color:var(--text);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;padding:0}
.cat-ico-grid button:hover{border-color:var(--border-hover);color:var(--violet)}
.cat-ico-grid button.on{border-color:var(--violet);background:color-mix(in srgb,var(--violet) 16%,transparent);color:var(--violet)}
.cat-ico-pie{display:flex;align-items:center;gap:10px;font-size:12px}
.v-cat-chips .chip{display:inline-flex;align-items:center;gap:6px}
.v-cat-chips .chip svg.ico-cat{width:15px;height:15px;flex:none}
.cat-sinfoto-ico svg.ico-cat{width:44px;height:44px;color:var(--violet);opacity:.8}
/* En la Caja maximizada el .fld va en fila (rótulo a la izquierda): la barra de fichas debe poder encoger y hacer scroll */
.venta-modal.maximized .vm-pago #v-metodo-wrap{flex:1 1 100%;min-width:0;flex-direction:column;align-items:stretch;gap:4px}   /* rótulo ARRIBA: así las 4 fichas (408 px) caben en la columna sin scroll */
#v-metodo-wrap .mp-bar,#c-metodo-wrap .mp-bar{flex:1;min-width:0}

/* ─── Vencimientos (06-sep-2026, opción B «Lotes al comprar»): semáforo de la columna Vence, cápsula de la Caja y modal Lotes ─── */
.badge.b-yel{background:rgba(212,160,23,.22);color:#7a5a00}
[data-theme="oscuro"] .badge.b-yel{background:rgba(212,160,23,.26);color:#f0cf6a}
.badge.b-mut{background:rgba(74,80,95,.12);color:var(--muted)}
/* AUDITORÍA (22-sep, pedido de Alejandro: «reajustar y ordenar las celdas… desalineadas y desiguales»): columnas de
   ancho FIJO y una sola línea en todo menos el Detalle, que es el único que se reparte en renglones. Lo que no cabe
   (una referencia larga, un usuario largo) se corta con «…» y se lee entero al pasar el ratón (title). */
#audit-list{table-layout:fixed}
#audit-list col.au-c-fecha{width:112px}
#audit-list col.au-c-hora{width:108px}
#audit-list col.au-c-accion{width:196px}
#audit-list col.au-c-ref{width:132px}
#audit-list col.au-c-monto{width:156px}
#audit-list col.au-c-usr{width:168px}
#audit-list td{vertical-align:middle;line-height:1.4;padding-top:8px;padding-bottom:8px}
#audit-list td:not(.au-det){white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#audit-list td.au-fecha,#audit-list td.au-hora{font-variant-numeric:tabular-nums}
#audit-list td.au-hora{color:var(--muted)}
#audit-list td.au-det{overflow-wrap:anywhere}
#audit-list .badge{max-width:100%;overflow:hidden;text-overflow:ellipsis;vertical-align:middle;letter-spacing:.02em}
/* ORIGEN de un registro (29-sep-2026, pedido de Alejandro: ir a la base / ver el detalle desde Auditoría). La referencia que
   apunta a un documento es un botón con ojo, del azul de los enlaces; abre la factura del movimiento o la ficha del registro. */
.au-ref-link{display:inline-flex;align-items:center;gap:5px;max-width:100%;padding:0;margin:0;border:0;background:none;font:inherit;color:var(--accent-text);cursor:pointer;vertical-align:middle}
.au-ref-link span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-decoration:underline dotted;text-underline-offset:3px}
.au-ref-link .ic16{width:14px;height:14px;flex:none;fill:currentColor}
.au-ref-link:hover span{text-decoration-style:solid}
.au-ref-link:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:3px}
/* LISTAS «PARA PAGAR» / «POR COBRAR» (01-oct-2026, Proveedores y Clientes; lienzo B): tres tarjetas arriba, buscador y
   «Solo con pendiente», y cada moneda en SU casilla (.mon-caja) — nunca «COP x + USD y». En una celda van una debajo de
   otra; en una tarjeta, una al lado de otra. .no = lo vencido. */
.mon-cajas{display:inline-flex;flex-direction:column;align-items:flex-end;gap:3px;vertical-align:middle}
.mon-caja{display:inline-flex;align-items:baseline;gap:4px;padding:1px 8px;border:1px solid var(--border);border-radius:8px;background:var(--surface-2);font-variant-numeric:tabular-nums;white-space:nowrap;line-height:1.5}
.mon-caja b{font-weight:700}
.mon-caja.no{color:var(--no);border-color:color-mix(in srgb,var(--no) 45%,var(--border))}
.mon-caja.no .cur{color:inherit}
a .mon-caja{color:var(--text)}
a:hover .mon-caja{border-color:var(--border-hover)}
.lp-kpis{margin:0 0 14px}
.lp-kpis .kpi{padding:11px 16px}
.lp-kpi-val{font-size:20px;margin-top:4px;min-height:32px;display:flex;align-items:center}
.lp-kpi-val .mon-cajas{flex-direction:row;flex-wrap:wrap;align-items:center;gap:6px 8px}
.lp-kpi-val .mon-caja{padding:2px 11px;font-size:19px}
.lp-nada{font-size:15px;font-weight:600}
.lp-kpi-sub{font-size:12px;margin-top:3px}
.lp-barra .search-wrap .inp{width:260px}
.lp-solo{display:inline-flex;align-items:center;gap:7px;font-size:13.5px;cursor:pointer;white-space:nowrap}
.lp-quien b{font-weight:600}
.lp-sub{color:var(--muted);font-size:12px;margin-top:1px;font-weight:400}
.lp-tabla td{vertical-align:middle}
.lp-tabla th{white-space:nowrap}
.lp-acc{white-space:nowrap;text-align:right}
.lp-btn{padding:4px 10px;font-size:12.5px;margin-right:6px;vertical-align:middle}
.lp-btn .ic16{width:14px;height:14px}
/* Teléfono (0.157, Clientes en el menú del administrador): en la fila, «Estado de cuenta» y «Abonar» muestran solo su icono —el
   rótulo sigue en title y aria-label, como «Mi clave» en el membrete— y la columna de acciones se acorta a la mitad. */
@media (max-width:720px){#tabla-cli .lp-btn-lbl{display:none}#tabla-cli .lp-btn{padding:4px 8px;margin-right:4px}#tabla-cli .lp-btn .ic16{margin-right:0}}
/* ESTADO DE CUENTA de un proveedor o un cliente (01-oct-2026, views/estado-cuenta.js): cabecera fija y cuerpo con su propio
   scroll; el resumen arriba (tres tarjetas + una fila por moneda) y debajo el libro, con el encabezado pegado al bajar. */
.ec-modal{width:min(1240px,97vw)}
.ec-modal[open]{display:flex;flex-direction:column}
.ec-body{display:flex;flex-direction:column;min-height:0;flex:1;overflow:auto;padding:12px 18px 16px}
/* .venta-modal form apila en columna (es la regla de la Caja): aquí el formulario es solo la barra de fechas, en fila. */
.ec-modal form.ec-filtros{display:flex;flex-direction:row;align-items:center;flex-wrap:wrap;gap:10px;max-height:none;margin-bottom:6px}
.ec-modal form.ec-filtros .spacer{flex:1}
/* Extendida (⛶, 0.157): «.venta-modal.maximized form» le da 100vh a TODO form del diálogo (y en el teléfono también
   «.venta-modal form»); aquí el form es solo la barra de fechas → su alto natural. El alto sobrante va al .ec-body. */
dialog.ec-modal form.ec-filtros{height:auto;max-height:none}
.ec-kpis{margin:8px 0 12px}
.ec-kpis .kpi{padding:10px 14px}
.ec-kpis .kpi-val{font-size:22px;margin-top:2px}
.ec-sub{font-size:12px;margin-top:2px}
.ec-pend{display:flex;flex-wrap:wrap;gap:4px 14px;align-items:center}
.ec-pend .mon-cajas{flex-direction:row;flex-wrap:wrap;align-items:center;gap:6px 8px}
.ec-pend .mon-caja{font-size:18px;padding:1px 10px}
/* El signo es el efecto sobre la deuda: un cargo la sube (+, rojo) y un abono la baja (−, verde). */
.ec-mas{color:var(--no);font-weight:600}
.ec-menos{color:var(--ok);font-weight:600}
.ec-ajuste{font-weight:500}
.ec-pend-nada{font-size:15px;font-weight:600}
.ec-resumen{margin-bottom:12px}
.ec-movs thead th{position:sticky;top:-12px;z-index:2}
/* 0.157: el overflow:hidden de .grid (para las esquinas) volvía al libro su propio contenedor de desplazamiento y el encabezado
   NO se pegaba al bajar en .ec-body. Como en .tabla-scroll .grid: visible. */
.grid.ec-movs{overflow:visible}
/* Cliente sin crédito (0.157): un resumen corto en vez del libro. */
.ec-modal.ec-contado{width:min(860px,96vw)}
.ec-contado-msg{margin:4px 0 10px;line-height:1.45}
.ec-contado-dev{display:flex;align-items:center;flex-wrap:wrap;gap:6px 10px;margin:0 0 12px}
.ec-contado-dev .mon-cajas{flex-direction:row;flex-wrap:wrap;gap:6px}
/* La barra de fechas del estado de cuenta (form.ec-filtros) es hija de un .ec-body en columna con scroll: con muchos movimientos
   el navegador la ENCOGÍA hasta su min-height (38 px) y, envuelta en varios renglones en el teléfono, quedaba debajo de las
   tarjetas (12-oct-2026, 0.163, visto con «Cliente al Mayor»). Nada del cuerpo se encoge: el que desliza es .ec-body. En la PC la
   barra cabe en un renglón y no cambia nada. */
.ec-body>*{flex-shrink:0}
/* NÚMEROS QUE NO SE SALEN DE SU TARJETA EN EL TELÉFONO (12-oct-2026, 0.163, Alejandro: «ajustar los números en los estados de
   cuenta de los clientes, ya que para "Consumidor final" y "Cliente al mayor" los números sobresalen de la celda»). Las tarjetas
   van a dos por fila (body[data-movil] .kpi) y una cifra en su casilla de moneda —«COP 1.334.116.640,00» a 18-19 px— no cabe en
   media pantalla (sale 30-50 px). La tarjeta que lleva casillas de moneda (Total por cobrar, Vencido, Ha comprado, Pendiente hoy)
   ocupa la fila entera, y la cifra se ajusta al ancho de su tarjeta (container query: de 19 px hasta 13 px como mucho) en lugar
   de salirse; las casillas nunca son más anchas que la tarjeta. Vale para cualquier cliente y cualquier moneda. */
body[data-movil] .kpi{min-width:0}
body[data-movil] .kpi:has(.mon-cajas){flex-basis:100%;container-type:inline-size}
body[data-movil] .kpi .mon-cajas{max-width:100%}
body[data-movil] .kpi .mon-caja{max-width:100%;min-width:0;font-size:clamp(13px,5.6cqi,19px)}
.ec-contado-pie{display:flex;align-items:center;flex-wrap:wrap;gap:10px;margin-top:6px}
.ec-contado-pie .muted{flex:1 1 320px;font-size:12.5px}
.ec-contado-pie #ec-ver-ventas{text-decoration:none}
.ec-contado [data-ec=fechas]{font-size:18px;white-space:nowrap}
/* Ventas filtrada por un cliente (/ventas?cliente=ID): la ficha del filtro, con su ✕ para quitarlo. */
.tb-cliente{display:inline-flex;align-items:center;gap:5px;cursor:default}
.tb-cliente-x{color:inherit;text-decoration:none;font-weight:700;padding:0 2px}
.ec-movs td{vertical-align:top}
.ec-fac{font-weight:600;white-space:nowrap}
.ec-cta{font-size:12px;white-space:nowrap}
.ec-det{font-size:12.5px;color:var(--muted);overflow-wrap:anywhere;min-width:180px}
.ec-anterior td{background:var(--surface-2)}
.ec-aviso td{background:var(--surface-2);font-size:12.5px}
.ec-imp{padding:1px 6px;vertical-align:middle}
.ec-imp .ic16{width:14px;height:14px}
.btn-estado .ic16{width:15px;height:15px}
.ab-imp-st,.ab-imp-td .cfg-ok{font-size:12px;margin:0}
.ab-imp-td{white-space:nowrap}
.cfg-ok .ab-imp{margin-left:10px;vertical-align:middle}
.au-origen{width:min(880px,96vw)}
.au-or-body{padding:14px 18px 16px;gap:12px;overflow:auto}
.au-or-bit{background:var(--surface-2);border:1px solid var(--border);border-radius:var(--r-card);padding:10px 12px;font-size:13px;line-height:1.5}
.au-or-bit .badge{vertical-align:middle}
.au-or-monto{margin-left:6px;font-weight:600;font-variant-numeric:tabular-nums}
.au-or-bit-det{margin-top:4px;color:var(--muted);overflow-wrap:anywhere}
.au-or-datos{display:grid;grid-template-columns:repeat(auto-fill,minmax(170px,1fr));gap:10px 18px}
.au-or-dato{display:flex;flex-direction:column;gap:2px;min-width:0}
.au-or-lbl{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--muted)}
.au-or-val{font-size:14px;font-weight:600;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}
.au-or-tabla{font-size:13px}
.au-or-aviso{margin:0}
.au-or-pie{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.vence-cell{white-space:nowrap}
.cat-stock.medio{background:rgba(212,160,23,.2);color:#7a5a00;border-color:rgba(212,160,23,.45)}
[data-theme="oscuro"] .cat-stock.medio{color:#f0cf6a}
.cap-vence{margin-left:4px}
.cat-vence:empty{display:none}
.v-add-linea .inp-lote{width:96px}
.v-add-linea .inp-vence{width:138px;padding-right:6px}
.lotes-tbl .inp{height:30px;padding:0 8px;font-size:12.5px}
.lotes-tbl .inp-lote{width:110px}
.lotes-tbl .inp-vence{width:138px;padding-right:4px}
.lotes-tbl .inp-cnt{width:84px}
.lote-conteo{display:inline-flex;gap:4px;align-items:center}
.lote-ori{font-size:10.5px;color:var(--muted);display:block;white-space:nowrap}
.lote-sin td{background:var(--surface-2)}
.vi-vence{display:inline-flex;gap:6px;align-items:center;margin-right:14px}
.v-add-linea .fn-br{flex-basis:100%;height:0;margin:0;padding:0}

/* KPI del reporte «Por vencer» (06-sep, Setup 2): 4 tarjetas con subtítulo y color de banda */
.kpis-vence .kpi{position:relative;border-left:4px solid var(--border)}
.kpis-vence .kpi-sub{font-size:11.5px;margin-top:3px;line-height:1.25}
.kpi.vk-no{border-left-color:var(--no)}
.kpi.vk-warn{border-left-color:var(--accent)}
.kpi.vk-yel{border-left-color:#d4a017}
.kpi.vk-mut{border-left-color:var(--muted)}
.kpi.vk-no .kpi-val{color:var(--no)}
.kpi.vk-warn .kpi-val{color:var(--accent-text)}
.kpi.vk-yel .kpi-val{color:#7a5a00}
[data-theme="oscuro"] .kpi.vk-yel .kpi-val{color:#f0cf6a}

/* Compras (06-sep, pedido Alejandro): Lote/Vencimiento/I.V.A. por línea ocultos hasta activar el toggle */
#compra-dialog:not(.avz) .c-avz{display:none}
.c-avz-tog{align-self:flex-end}
.c-avz-tog.on{background:var(--violet);color:#fff;border-color:var(--violet)}
.c-avz-tog.on .ic16{fill:#fff}
/* Combo de Producto de +Nueva Compra un ~20% más corto (no debe acaparar toda la fila) */
#compra-dialog .v-fld-art{flex:4 1 190px;min-width:180px;max-width:460px}

/* ─── Cajón de pago deslizable (06-sep, pedido Alejandro): totales FUERA (siempre visibles) y lo de
   cobrar/pagar en un cajón que se asoma desde la esquina inferior derecha; la ficha es su cabecera ─── */
/* CAJÓN: la ficha (lengüeta) va SIEMPRE justo debajo de los totales (pedido v2) — plegada o abierta
   no se mueve, solo se despliega su cuerpo hacia abajo. ABIERTO crece (flex:1) y su cuerpo hace
   scroll interno si el modo (mixto) no cabe en pantallas bajas como la caja 1366×768; así los
   totales de arriba y el botón Guardar de abajo nunca se van de vista. */
.pago-cajon{width:100%;display:flex;flex-direction:column;overflow:hidden;flex:none;min-height:0}
.pago-cajon.abierto{flex:1 1 auto}
.pago-ficha{display:flex;align-items:center;gap:9px;width:100%;padding:9px 14px;border:0;border-radius:11px;cursor:pointer;color:#14181f;font-weight:700;font-size:14px;font-family:var(--font-display);background-image:linear-gradient(115deg,#ffa53f 0%,#ee8c2a 45%,#f2705a 100%);box-shadow:0 2px 8px rgba(238,140,42,.3)}
.pago-ficha .pf-ic{width:20px;height:20px;flex:none}
.pago-ficha .pf-txt{margin-right:auto}
.pago-ficha .pf-chev{width:18px;height:18px;flex:none;transition:transform .2s ease}
.pago-ficha:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.pago-cajon.abierto .pago-ficha{border-radius:11px 11px 0 0}
.pago-cajon:not(.abierto) .pago-ficha .pf-chev{transform:rotate(180deg)}
.pago-body{overflow-y:auto;flex:1 1 auto;min-height:0;max-height:900px;opacity:1;transform:translateY(0) scale(1);transform-origin:bottom right;transition:max-height .22s ease,opacity .18s ease,transform .22s ease;background:var(--surface-2);border:1px solid var(--border);border-top:0;border-radius:0 0 11px 11px;padding:10px 14px;display:flex;flex-direction:column;align-items:flex-end;gap:8px}
/* Los botones se quedan pegados al fondo del cajón: si el cuerpo hace scroll (mixto en pantalla
   baja), "Guardar" sigue siempre a la vista. El fondo tapa lo que scrollea por detrás. */
.pago-body .vm-btns{position:sticky;bottom:-10px;z-index:1;background:var(--surface-2);width:100%;padding:8px 0 2px;margin-top:2px}
/* Sombra sutil arriba de los botones: avisa que hay controles scrolleando por detrás (mixto en
   pantalla baja). Solo cuando el cuerpo realmente scrollea. */
.pago-body{scrollbar-width:thin}
.pago-body .vm-btns::before{content:"";position:absolute;left:0;right:0;top:-10px;height:10px;pointer-events:none;background:linear-gradient(to top,var(--surface-2),transparent)}
.pago-cajon:not(.abierto) .pago-body{max-height:0;opacity:0;transform:translateY(10px) scale(.96);padding-top:0;padding-bottom:0;border-color:transparent}
.pago-body .vm-pago{border-top:0;padding-top:0}
@media (prefers-reduced-motion: reduce){.pago-body{transition:none}.pago-ficha .pf-chev{transition:none}}
/* Forma de pago: control segmentado unificado (venta y compra) */
.seg-fld{flex-direction:row!important;align-items:center;gap:8px}
.seg-pago{display:inline-flex;border:1px solid var(--border);border-radius:9px;overflow:hidden;background:var(--surface-2);box-shadow:inset 0 1px 0 var(--sutil-brillo)}
.seg-pago button{border:0;background:transparent;padding:6px 13px;font-size:12.5px;font-weight:600;color:var(--muted);cursor:pointer;font-family:var(--font-ui)}
.seg-pago button.on{background:var(--violet);color:#fff}
[data-theme="oscuro"] .seg-pago button.on{color:#14161b}

/* ─── CAJÓN v3 (07-sep, pedido Alejandro tras tocar la v2): UNA columna en su orden — Forma de pago · Moneda de Pago +
   casillas · Método + fichas en una fila · (contado/abonado del mixto) · imprimir/IVA · botones —, la FICHA-LENGÜETA SIEMPRE
   ABAJO del pie y el cuerpo desplegándose hacia ARRIBA, completo, sin barra de scroll (también a 1366×768). El pie de los
   modales maximizados queda en 520 px (las 4 fichas y los 4 botones caben en una fila) y se compactan totales, filas de
   moneda, fichas (44 px) y botones para que el peor caso (mixto) quepa entre los totales y la ficha. En el modo Catálogo
   de la Caja el pie mide lo que mide: plegado = totales + ficha → el Detalle recupera todo el alto (el fin del pedido). ─── */
.venta-modal.maximized .vm-foot{width:520px;padding:4px 16px 5px;gap:4px}
.venta-modal.maximized .vm-tot{gap:0}
.venta-modal.maximized .vm-tot span{font-size:13.5px;line-height:1.2}
.venta-modal.maximized .vm-tot .big{gap:6px}
.venta-modal.maximized .vm-tot .big b{font-size:22px;line-height:1.1}
/* La ficha ABAJO: el cajón va al fondo del pie (margin-top:auto) y se pinta invertido (column-reverse: cuerpo arriba,
   ficha abajo). Abierto crece hacia arriba; el espacio que sobre queda entre los totales y el cuerpo. */
.pago-cajon{margin-top:auto;flex-direction:column-reverse}
.pago-cajon.abierto{flex:1 1 auto;min-height:0}
.pago-ficha{padding:5px 14px;border-radius:11px}
.pago-cajon.abierto .pago-ficha{border-radius:0 0 11px 11px}
.pago-cajon:not(.abierto) .pago-ficha .pf-chev{transform:none}
.pago-cajon.abierto .pago-ficha .pf-chev{transform:rotate(180deg)}
.pago-body{flex:0 1 auto;min-height:0;border:1px solid var(--border);border-bottom:0;border-radius:11px 11px 0 0;padding:calc(4px + var(--aire, 16px) / 4) 12px calc(5px + var(--aire, 16px) / 4);gap:4px;transform-origin:bottom right;align-items:stretch}
.pago-cajon:not(.abierto) .pago-body{padding-top:0;padding-bottom:0;border-color:transparent}
.pago-body .vm-pago{display:flex;flex-direction:column;align-items:stretch;flex-wrap:nowrap;gap:4px;padding-top:0;border-top:0;width:100%}
.pago-body .vm-pago .seg-fld{flex-direction:row;align-items:center;gap:8px;width:100%}
.seg-pago button{padding:4px 12px}
.pago-cajon{--aire:16px}   /* 0.92: aire ENTRE secciones (16 = ≈ +20 % sobre la 0.90); app.js lo reduce solo si el cuerpo no cabe */
.pago-lbl{font-size:12px;font-weight:600;color:var(--text);line-height:1.1;margin-top:calc(2px + var(--aire, 16px))}
.pago-body .vm-monedas{gap:2px;width:100%}
.pago-body .vm-mon{min-height:19px;font-size:13px}
.venta-modal.maximized .pago-body .vm-mon b{font-size:14px}
.pago-body #v-metodo-wrap,.pago-body #c-metodo-wrap{flex:none;width:100%;flex-direction:column;align-items:stretch;gap:3px}
.pago-body #v-metodo-wrap .mp-bar,.pago-body #c-metodo-wrap .mp-bar{flex:none;flex-wrap:nowrap;gap:6px;overflow:visible;padding-bottom:0;width:100%}
.pago-body .mp-ficha{height:54px;width:auto;flex:1 1 0;max-width:112px;min-width:78px;gap:4px;font-size:11px;padding:0 4px}   /* 0.93: +10 % arriba y abajo (44→54) */
.pago-body .mp-ficha .ic svg{width:18px;height:18px}
.pago-body #v-contado-wrap,.pago-body #c-contado-wrap,.pago-body #v-propina-wrap{flex:none;width:100%;flex-direction:row;align-items:center;gap:6px;flex-wrap:wrap}
.pago-body #v-monto-contado,.pago-body #c-monto-contado,.pago-body #v-propina{max-width:120px;flex:1 1 90px;min-width:80px}
.pago-body #v-credito-info,.pago-body #c-deuda-aviso,.pago-body #v-apagar{font-size:12px;line-height:1.25;width:100%}
.pago-body .vm-print-auto,.pago-body .vm-iva{margin-top:2px;font-size:12.5px}
.pago-body .vm-btns{position:static;padding:calc(2px + var(--aire, 16px) * .75) 0 0;margin:0;width:100%}
.pago-body .vm-btns::before{display:none}
.pago-body .vm-btns .btn-primary,.pago-body .vm-btns .btn-ghost{padding:4px 12px}
.pago-body #v-cot-msg:empty{display:none}
/* Compras: el aviso del re-precio deja de RESERVAR un renglón bajo la fila de captura (va arriba a la derecha, junto al
   título «Agregar producto», sin ocupar alto): ese renglón es justo lo que le faltaba al cajón en mixto a 768 px. */
#compra-dialog .vm-sec:has(#c-util-aviso){position:relative}
#compra-dialog .c-util-aviso{position:absolute;top:8px;right:20px;min-height:0;margin:0;max-width:62%;text-align:right;white-space:normal;line-height:1.2}
/* Compactación medida por bloques (07-sep): la regla del media ≤860 (.venta-modal.maximized .vm-pago) ponía gap 6 y
   padding-top 6 → aquí con más especificidad; inputs de contado/abonado/propina a 28 px; filas de moneda a 20 px. */
.venta-modal.maximized .pago-body .vm-pago{gap:3px;padding-top:0}
.venta-modal.maximized .pago-body .vm-monedas{gap:2px}
.venta-modal.maximized .pago-body .vm-mon{min-height:20px}
.pago-body #v-contado-wrap .inp,.pago-body #c-contado-wrap .inp,.pago-body #v-propina-wrap .inp{height:28px;padding:3px 8px}
.pago-body .vm-print-auto,.pago-body .vm-iva{margin-top:calc(1px + var(--aire, 16px))}
/* «Factura en esta moneda» (21-sep, pedido de Alejandro) comparte renglón con el rótulo «Moneda de Pago:»:
   ese hueco ya estaba vacío, es donde se elige la moneda, y así la casilla no le cuesta un renglón al cajón.
   🪤 Aquí vivió un rato `.vm-print-row .vm-print-auto ~ .vm-print-auto{margin-top:2px}`, de cuando la casilla
   nueva iba en la fila de imprimir y la hacía envolver: al mudarla a este renglón la regla dejó de hacer falta
   y solo levantaba «Guardar en digital» 7,5 px sobre «Imprimir recibo» — desnivel que Alejandro vio a ojo. Las
   dos casillas de esa fila comparten el mismo margen de arriba y se quedan así. */
.pago-lbl-fila{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap}
.vm-fact-pago{display:flex;align-items:center;gap:5px;font-size:12px;font-weight:400;color:var(--muted);cursor:pointer;user-select:none}
.vm-fact-pago input{margin:0}
/* «Datos de la venta» en UNA línea: la Nota cede ancho (crece igual) y «Permitir sobregiro» sube a su derecha → el pie de
   la Caja gana ~35 px de alto para el cajón. */
.venta-modal.maximized #v-row-top .fld.grow{flex:1 1 130px;min-width:130px}
.venta-modal.maximized #v-row-top .fld.grow .inp.grow{flex:none;width:100%;min-width:0}
/* 0.92: el aire es GRADUADO (--aire, 16 px máx.): si el cuerpo no cabe (p. ej. 2 inventarios a 768 px en mixto), app.js
   baja --aire justo lo necesario (cada px de aire son ~3,75 px de alto) en vez de saltar a compacto. */
/* Modo Catálogo de la Caja: el pie mide su contenido (plegado: totales + ficha; abierto: + el cajón completo) */
.venta-modal.modo-cat .vm-foot{width:auto;flex:none;height:auto;max-height:none}

/* ─── INVENTARIO EN MODO CATÁLOGO (07-sep-2026, pedido Alejandro: la misma alternancia Tabla | Catálogo de
   +Nueva venta). Cada tarjeta trae TODOS los datos de la fila; la foto se agrega o cambia pulsando el marco;
   tamaños S/M/L/XL con columnas que se ajustan al ancho (auto-fill); la barra de categorías va encima. ─── */
.inv-vista{margin-left:0}
.inv-vista button{display:inline-flex;align-items:center;gap:5px}
.inv-vista button .ic16{width:14px;height:14px;margin:0;fill:currentColor}
.tabla-scroll .inv-cat-tabla{width:100%;min-width:0;table-layout:fixed}
.inv-cat-tabla thead{display:none}   /* el estado del autofiltro/orden sigue ahí (inputs), sin pintarse */
.tabla-scroll .inv-cat-tabla td{white-space:normal}
.inv-cat-tabla .inv-cat-row>td{padding:12px 14px 6px;background:var(--surface)}
.inv-cat-tabla .inv-cat-row:hover>td{background:var(--surface)}
.inv-cat-top{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:12px}
/* Barra de categorías: nombres completos en una fila que se desplaza (como en la Caja) + el gestor al final */
.inv-cat-chips{flex:1 1 640px;min-width:0;flex-wrap:nowrap;overflow-x:auto;overflow-y:hidden;padding-bottom:6px;scrollbar-width:thin;scrollbar-color:var(--border-hover) var(--surface-2);align-items:center}
.inv-cat-chips::-webkit-scrollbar{height:8px}
.inv-cat-chips::-webkit-scrollbar-track{background:var(--surface-2);border-radius:6px}
.inv-cat-chips::-webkit-scrollbar-thumb{background:var(--border-hover);border-radius:6px}
.inv-cat-chips .chip{padding:5px 12px;font-size:13px;white-space:nowrap;display:inline-flex;align-items:center;gap:6px;flex:none}
.inv-cat-chips .chip svg.ico-cat{width:15px;height:15px;flex:none}
.inv-cat-gear{flex:none;padding:5px 9px;margin-left:2px}
.inv-cat-gear .ic16{margin:0}
.inv-cat-n{font-size:12px;white-space:nowrap}
.inv-cat-orden{display:inline-flex;align-items:center;gap:6px;font-size:12.5px;font-weight:600;color:var(--muted);white-space:nowrap}
.inv-cat-orden .inp{height:32px;padding:0 6px;font-size:12px;width:auto;max-width:150px}
.inv-cat-top .spacer{flex:0 0 0}   /* 0.92: todo el ancho sobrante va a la barra de categorías */
/* Columnas FIJAS por tamaño (decisión Alejandro 07-sep, como en la Caja): S 6 · M 5 · L 4 · XL 3, en cualquier ancho */
.inv-cat-grid{display:grid;gap:12px;grid-template-columns:repeat(5,minmax(0,1fr))}
.inv-cat-grid.tam-s{grid-template-columns:repeat(6,minmax(0,1fr));gap:10px}
.inv-cat-grid.tam-l{grid-template-columns:repeat(4,minmax(0,1fr));gap:14px}
.inv-cat-grid.tam-xl{grid-template-columns:repeat(3,minmax(0,1fr));gap:16px}
.ic-card{display:flex;flex-direction:column;border:1px solid var(--border);border-radius:var(--r-card);background:var(--surface);overflow:hidden;box-shadow:var(--shadow-sm)}
.ic-card:hover{border-color:var(--border-hover);box-shadow:var(--shadow-md)}
.ic-card.ic-cargando .ic-foto{opacity:.45}
/* La foto va sobre BLANCO aunque el tema sea oscuro: es contenido (fotos de producto), como en la Caja */
.ic-foto{position:relative;height:150px;background:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;padding:6px;flex:none;border-bottom:1px solid var(--border)}
.inv-cat-grid.tam-s .ic-foto{height:112px}
.inv-cat-grid.tam-l .ic-foto{height:200px}
.inv-cat-grid.tam-xl .ic-foto{height:250px}
.ic-foto img{max-width:100%;max-height:100%;object-fit:contain;display:block}
.ic-foto .cat-sinfoto{font-size:11px}
.ic-foto-cta{position:absolute;left:0;right:0;bottom:0;display:flex;align-items:center;justify-content:center;gap:6px;padding:5px 8px;background:rgba(31,36,48,.72);color:#fff;font-size:11.5px;font-weight:600;opacity:0;transition:opacity .12s ease;pointer-events:none}
.ic-foto-cta i{font-style:normal}
.ic-foto-cta svg{flex:none}
.ic-foto:hover .ic-foto-cta,.ic-sinfoto .ic-foto-cta{opacity:1}
.ic-sinfoto .ic-foto-cta{background:color-mix(in srgb,var(--violet) 80%,transparent)}
.ic-foto-x{position:absolute;right:6px;top:6px;width:24px;height:24px;border-radius:7px;border:0;background:rgba(31,36,48,.6);color:#fff;font-size:13px;cursor:pointer;opacity:0;transition:opacity .12s ease;display:inline-flex;align-items:center;justify-content:center;padding:0;line-height:1}
.ic-foto:hover .ic-foto-x{opacity:1}
.ic-foto-x:hover{background:var(--no)}
.ic-info{padding:8px 10px 10px;display:flex;flex-direction:column;gap:5px;flex:1;font-size:12.5px}
.ic-l1{display:flex;align-items:center;justify-content:space-between;gap:6px}
.ic-cod{font-size:11.5px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ic-nombre{font-size:13px;font-weight:600;line-height:1.25;height:33px;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
.ic-sub{display:flex;align-items:center;justify-content:space-between;gap:8px;font-size:11.5px;color:var(--muted);min-width:0}
.ic-sub>span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:inline-flex;align-items:center;gap:4px;min-width:0}
.ic-sub svg.ico-cat{width:13px;height:13px;flex:none}
.ic-precios{display:flex;flex-direction:column;gap:2px;margin-top:2px}
.ic-p{display:flex;align-items:center;gap:6px;white-space:nowrap}
.ic-k{font-size:10.5px;font-weight:700;letter-spacing:.05em;color:var(--muted);width:38px;flex:none}
.ic-v{font-variant-numeric:tabular-nums;font-weight:600;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis}
.ic-v .cur{margin-right:3px}
.ic-costo{font-weight:500}
.ic-util{font-size:11px;font-weight:700;padding:1px 6px;border-radius:5px;flex:none;font-variant-numeric:tabular-nums}
.ic-fila{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-top:2px}
.ic-exist{border:1px solid var(--border);background:var(--surface-2);border-radius:20px;padding:2px 9px;font-size:11.5px;font-weight:600;cursor:pointer;color:var(--text);font-family:inherit;text-decoration:none;line-height:1.4}
.ic-exist b{font-variant-numeric:tabular-nums}
.ic-exist.ex-r,.ic-exist.ex-y,.ic-exist.ex-g{border-color:transparent}
.ic-exist.kardex-link:hover{box-shadow:inset 0 0 0 2px var(--accent)}
span.ic-exist{cursor:default}
/* 0.158 (pedido de Alejandro: «la ficha para modificar el vencimiento a penas es "-"»): en la tarjeta el vencimiento lleva SIEMPRE
   el calendario delante y, sin fecha, la pastilla gris «Sin fecha». Todo el conjunto es el botón (cursor de clic); el icono va en
   el gris de los rótulos y se enciende en naranja al pasar, junto con el borde de la pastilla. Para que la fila Exist. / Vence
   no se parta donde antes cabía (medido a 1366 px: en M, «Exist. 750» + «VENCIDO · dd/mm/aaaa» quedaba a 7 px del borde), el
   icono mide 12 px con 2 px de aire y la pastilla de la tarjeta lleva 5 px a los lados en vez de 9: el conjunto crece 6 px. */
.ic-vence{display:inline-flex;align-items:center;gap:2px;text-decoration:none;cursor:pointer}
.ic-vence>.ic16{width:12px;height:12px;margin:0;color:var(--muted);flex:none}
.ic-vence .badge{cursor:pointer;padding-left:5px;padding-right:5px}
.ic-vence.kardex-link:hover{box-shadow:none}
.ic-vence.kardex-link:hover .badge{box-shadow:inset 0 0 0 2px var(--accent)}
.ic-vence.kardex-link:hover>.ic16{color:var(--accent-text)}
.ic-tog .toggle{font-size:11px;padding:2px 9px 2px 19px}
.ic-acc{display:flex;align-items:center;justify-content:space-between;gap:6px;margin-top:auto;padding-top:4px}
.ic-btn{height:28px;padding:0 10px;font-size:12px;display:inline-flex;align-items:center;gap:4px}
.ic-btn .ic16{margin:0;width:14px;height:14px}
.ic-del{opacity:.55}
.inv-mas.inv-cat-mas{grid-column:1/-1;text-align:center;color:var(--muted);font-size:12.5px;padding:12px}
.inv-cat-vacio{grid-column:1/-1;text-align:center;padding:28px 10px;color:var(--muted);display:flex;flex-direction:column;gap:4px}
.inv-cat-vacio b{color:var(--text);font-size:14px}
/* S: sin la utilidad junto al precio (no cabe) · XL: texto un punto más grande */
.inv-cat-grid.tam-s .ic-p .ic-util{display:none}
.inv-cat-grid.tam-s .ic-k{width:34px}
.inv-cat-grid.tam-s .ic-info{padding:7px 8px 8px}
.inv-cat-grid.tam-xl .ic-nombre{font-size:14px;height:36px}
.inv-cat-grid.tam-xl .ic-info{font-size:13.5px}
/* ─── «EDICIÓN GENERAL» del inventario (07-sep, decisión de Alejandro: Tabla y Catálogo nacen en solo lectura; un botón
   abre la edición en la vista actual, como la "Modificación Masiva" del Excel). El botón en naranja Aurora cuando está
   activo y un aro naranja alrededor del inventario mientras se edita. ─── */
.inv-edit-btn{gap:6px}
.inv-edit-btn.on{background-color:var(--accent);background-image:linear-gradient(115deg,#ffa53f 0%,#ee8c2a 45%,#f2705a 100%);color:var(--accent-ink);border-color:transparent;box-shadow:0 3px 10px rgba(238,140,42,.3)}
.inv-edit-btn.on .ic16{fill:currentColor}
.tabla-scroll:has(#tabla[data-edit="1"]){box-shadow:0 0 0 2px var(--accent),var(--shadow)}
/* ─── Interruptor de la barra del inventario (03-oct-2026; nació con «Ocultar inactivos» y hoy lo usa «Fijar nombre»): la
   forma de los chips de tipo, pero con casilla. Apagado se lee como opción (borde punteado, casilla vacía); encendido
   toma el violeta del chip activo y la casilla se marca, para que lo puesto se vea de un vistazo. ─── */
.chip-tog{display:inline-flex;align-items:center;gap:7px;background:var(--surface);border:1px dashed var(--border-hover);border-radius:20px;padding:6px 14px 6px 11px;cursor:pointer;color:var(--muted);font-size:13.5px;font-weight:600;white-space:nowrap}
.chip-tog:hover{color:var(--text);border-color:var(--text)}
.chip-tog .ct-chk{position:relative;flex:none;width:14px;height:14px;border:1.5px solid currentColor;border-radius:4px}
.chip-tog.on{background:var(--violet);color:#fff;border:1px solid var(--violet);box-shadow:inset 0 1px 0 rgba(255,255,255,.18)}
.chip-tog.on .ct-chk::after{content:"";position:absolute;left:3.5px;top:.5px;width:4px;height:8px;border:solid currentColor;border-width:0 2px 2px 0;transform:rotate(45deg)}
[data-theme="oscuro"] .chip-tog.on{color:#14161b}
/* ─── Filtro «Estado» de tres posiciones (04-oct-2026, pedido de Alejandro: «agregar el filtro de 3 estados»): Todos · Solo
   activos · Solo inactivos. Rótulo + segmentado (el .seg-tam de Tabla | Catálogo) en la barra del Inventario (.inv-estado) y
   junto a las casillas de umbrales del reporte de existencias (.rep-eact). El grupo baja entero de renglón; solo en una
   pantalla más angosta que él (un teléfono chico) el rótulo sube encima del segmentado, que nunca se parte. ─── */
.inv-estado,.rep-eact,.lp-cred{display:inline-flex;align-items:center;gap:4px 6px;white-space:nowrap;flex-wrap:wrap;max-width:100%}
.inv-estado .ver-lbl,.rep-eact .ver-lbl,.lp-cred .ver-lbl{font-size:12.5px;font-weight:600;color:var(--muted)}
.rep-eact .seg-tam button{padding:5px 10px;font-size:12.5px}
/* ─── Interruptor «Crédito» de Clientes (05-oct-2026, 0.157): la píldora del Activo/Inactivo de los productos. «Sin crédito»
   (nunca se le dio cupo) va en gris y no en rojo: son casi todos los de contado y una columna entera en rojo no avisaría de nada;
   en rojo queda «Congelado». El cupo de un congelado se ve guardado y apagado. En la ficha encabeza el bloque «Crédito»; en la
   Caja, «Crédito» y «Mixto» se apagan con un cliente congelado. ─── */
.toggle.off.tog-sin{background:rgba(120,120,120,.13);color:var(--muted)}
[data-theme="oscuro"] .toggle.off.tog-sin{background:rgba(170,170,170,.12)}
.lp-cred-td{white-space:nowrap}
/* La columna nueva no cabía a 1366 px (la lista desbordaba 36 px y el nombre quedaba en 108 px): «Cupo / disponible» y «Último
   abono» pueden partir su rótulo en dos renglones cuando falta espacio, y el nombre gana ese ancho. */
.lp-tabla th.lp-th-ajusta{white-space:normal}
.lp-cupo-cong{color:var(--muted)}
.ficha-cred{display:flex;align-items:center;gap:8px 12px;flex-wrap:wrap;margin:0 0 12px}
.ficha-cred-lbl{font-size:13px;font-weight:600}
.ficha-cred .fld-help{flex:1 1 220px}
.ficha-cred .fld-help.cfg-err{color:var(--no)}
.ficha-modal.cred-modal{width:min(520px,96vw)}
.cred-intro{margin:0 0 12px}
.seg-pago button:disabled{opacity:.42;cursor:not-allowed}
/* La fila de un producto inactivo en el reporte de existencias lo dice junto al nombre (con «Todos» van mezclados). */
.ex-inact{display:inline-block;margin-left:6px;padding:1px 7px;border-radius:999px;border:1px dashed var(--border-hover);color:var(--muted);font-size:11px;font-weight:600;line-height:1.5;white-space:nowrap;vertical-align:1px}
/* ─── Existencia y conteo POR PIEZAS (03-oct-2026, decisión de Alejandro): en los productos que se venden sueltos la
   existencia se lee «9 Ent. · 2 Unit.» y se cuenta con una casilla por tipo, en el ajuste del kardex y en el masivo. ─── */
.aje-pz{white-space:nowrap}
.aje-pz-l{display:inline-flex;align-items:center;gap:5px;margin-left:10px;font-size:12px;font-weight:600;color:var(--muted)}
.aje-pz-l .inp{width:72px}
.kx-piezas .fld .inp-pz{width:84px}
/* ─── Inventario en TABLA: tamaño, columnas fijas y fila activa (03-oct-2026, lienzo «Inventario en tabla», decisiones de
   Alejandro: cambiar muchos precios seguidos sin perder de vista el nombre). A 1366 px la fila mide 2.042 px y la zona
   visible 1.320: el control «Tamaño» (100 · 85 · 75 · Ajustar) encoge la TABLA con zoom —todo a la vez: letra, celdas,
   campos—; el marco #inv-scroll no cambia. --inv-zoom lo pone app.js (invTablaAjustar). El min-width:100% de siempre ya se
   mide contra el marco tal como se ve (el zoom lo compensa solo): una tabla angosta sigue llenándolo. Los controles solo
   existen en la vista Tabla. ─── */
body:has(#tabla.inv-cat-tabla) .solo-tabla{display:none}
#tabla:not(.inv-cat-tabla){zoom:var(--inv-zoom,1)}
/* «Fijar nombre»: Código, Tipo y Nombre pegados a la izquierda del marco (los desplazamientos --fx2/--fx3 los mide app.js:
   el ancho de las columnas depende de los datos). Las celdas fijas van opacas —el fondo de la fila, el hover y el resalte
   se pintan en el <tr>, debajo—; la sombra del borde dice que lo demás pasa por detrás. */
#tabla.fijar thead th:nth-child(-n+3),#tabla.fijar tbody tr[id^="art-"]>td:nth-child(-n+3){position:sticky}
#tabla.fijar thead th:nth-child(-n+3){z-index:7}
#tabla.fijar tbody tr[id^="art-"]>td:nth-child(-n+3){z-index:2;background:var(--surface)}
#tabla.fijar tbody tr[id^="art-"]:hover>td:nth-child(-n+3){background:linear-gradient(var(--row-hover),var(--row-hover)),var(--surface)}
#tabla.fijar :is(th,td):nth-child(1){left:0}
#tabla.fijar :is(th,td):nth-child(2){left:var(--fx2,0px)}
#tabla.fijar :is(th,td):nth-child(3){left:var(--fx3,0px)}
#tabla.fijar thead th:nth-child(3)::after,#tabla.fijar tbody tr[id^="art-"]>td:nth-child(3)::after{content:"";position:absolute;top:0;bottom:0;right:-9px;width:9px;pointer-events:none;background:linear-gradient(90deg,rgba(16,24,40,.24),transparent)}
/* Fila ACTIVA: la fila donde se trabaja (clic, flechas arriba/abajo, o la celda que se edita). El tono va en el <tr>, como
   el hover, así las bandas de color de Utilidad y Existencia siguen leyéndose; la celda en edición lleva el aro naranja. */
#tabla tbody tr.fila-activa,#tabla tbody tr.fila-activa:hover{background:color-mix(in srgb,var(--accent) 20%,transparent)}
#tabla.fijar tbody tr.fila-activa>td:nth-child(-n+3),#tabla.fijar tbody tr.fila-activa:hover>td:nth-child(-n+3){background:linear-gradient(color-mix(in srgb,var(--accent) 20%,transparent),color-mix(in srgb,var(--accent) 20%,transparent)),var(--surface)}
#tabla tbody tr.fila-activa td.editing .cell-input{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent)}
/* Píldoras de Estado / I.V.A. en solo lectura: mismo look, sin gesto */
.toggle.tog-static{cursor:default;pointer-events:none}
/* Tarjeta en solo lectura: el marco de la foto no es un botón */
.ic-foto.ic-ro{cursor:default}
/* Tarjeta en edición: campos en línea */
.ic-inp{height:26px;padding:2px 6px;font-size:12.5px;width:100%;min-width:0;border-radius:6px;font-weight:500}
.ic-inp.num{text-align:right;font-variant-numeric:tabular-nums}
.ic-inp-nombre{font-weight:600}
.ic-nombre-ed{height:auto;display:block;overflow:visible;-webkit-line-clamp:unset}
.ic-l1 .ic-inp-cod{width:auto;flex:1 1 80px;font-size:11.5px;height:24px}
.ic-sub .ic-inp-marca{width:auto;flex:1 1 70px;font-size:11.5px;height:24px}
.ic-v-ed{display:inline-flex;align-items:center;gap:3px;flex:1;min-width:0}
.ic-v-ed .ic-inp{flex:1 1 60px;width:auto}
.ic-util-inp{width:66px;flex:none;padding:2px 4px}
.ic-edit .ic-p{gap:5px}
.inv-cat-grid.tam-s .ic-edit .ic-util-inp{display:none}
/* Precio y utilidad en la misma línea: sin spinners de number (se comían el ancho) y basis mínimos claros */
.ic-inp[type="number"]{appearance:textfield;-moz-appearance:textfield}
.ic-inp[type="number"]::-webkit-outer-spin-button,.ic-inp[type="number"]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
.ic-v-ed .ic-inp{flex:1 1 44px;min-width:44px;padding:2px 5px}
.ic-util-inp{width:58px;min-width:58px;padding:2px 4px}
.ic-edit .ic-k{width:34px}

/* ─── «Precio COP (coin)» → ver el precio en OTRA moneda al clic (07-sep, pedido Alejandro, 2ª forma): el rótulo cicla
   COP → USD → VES → COP; mientras la vista no es la moneda propia, un overlay sobre la celda muestra el precio convertido
   con la tasa del día y el campo queda en solo lectura (conserva el valor real). Al agregar vuelve a la moneda propia. ─── */
.v-fld-precio{position:relative}
.v-pv-lbl{cursor:pointer;user-select:none;display:inline-flex;align-items:center;gap:4px}
.v-pv-lbl:hover #v-precio-mon{text-decoration:underline dotted;text-underline-offset:3px}
.v-pv-coin{display:inline-flex;line-height:0}
.v-pv-coin .coin{margin:0;display:block}
.v-pv-otra{position:absolute;left:0;bottom:0;display:none;align-items:center;justify-content:flex-end;padding:0 10px;box-sizing:border-box;border-radius:var(--r-ctl);border:1px solid var(--accent);background:var(--surface);color:var(--accent-text);font-weight:700;font-variant-numeric:tabular-nums;font-size:14px;cursor:pointer;box-shadow:var(--ring);z-index:2;white-space:nowrap}
.v-pv-ro{opacity:.35}
/* Visor de moneda del modo Catálogo: (USD) (VES) (COP), COP por defecto; solo pinta, se agrega en la base */
.cat-ver button{display:inline-flex;align-items:center;gap:4px}
.cat-ver .coin{margin:0}
.cat-ref{font-style:normal;font-weight:400;color:var(--muted);margin-right:2px}

/* ─── Ajuste MASIVO de existencias (07-sep, pedido Alejandro): lista de la vista con la celda «Real (contada)» ─── */
.aje-scroll{max-height:52vh;overflow:auto;margin-top:10px}
.aje-t td{padding:5px 8px;vertical-align:middle}
.aje-t .aje-inp{width:112px;text-align:right;height:30px;padding:3px 8px}
.aje-t tr.aje-cambia td{background:color-mix(in srgb,var(--accent) 12%,transparent)}
.aje-resumen{font-weight:600;white-space:nowrap;padding-bottom:9px}
/* Documento digital (07-sep, pedido Alejandro): los dos checkbox del cajón en una fila y el estado «✓ PDF guardado · Abrir carpeta · Descargar». */
.vm-print-row{display:flex;flex-wrap:wrap;align-items:center;gap:0 14px}
.dig-estado{font-size:12.5px;display:inline-flex;flex-wrap:wrap;align-items:center;gap:0 5px}
.dig-link{font:inherit;font-size:12.5px;color:var(--violet);background:none;border:none;padding:0;cursor:pointer;text-decoration:underline dotted;text-underline-offset:3px}
.dig-link:hover{color:var(--accent)}
/* Segmentados «Formato» y «Destino» del guardado en digital (08-sep, pedido de Alejandro): salen SOLO con la casilla
   «Guardar en digital al guardar» marcada y son dos alternadores de UNA opción, con el mismo molde que el
   «Forma de pago» (.seg-pago) para que la Caja hable un solo idioma: |PDF|Imagen| y |Guardar en carpeta|Guardar en
   Destino|. Ocupan su propia línea, alineados a la derecha como el resto del cajón. */
.dig-opts{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:6px;width:100%}
/* 🪤 SIN esta regla los segmentados NO se escondían: el atributo hidden solo apaga el elemento por la hoja del
   navegador ([hidden]{display:none}), y la regla de arriba —más específica— la pisaba. Se veían SIEMPRE, con la
   casilla marcada o no (lo reportó Alejandro el 08-sep). El resto de clases del proyecto que usan hidden ya
   llevaban la suya (.v-combo-list, .th-menu, .cat-ico-pop, .setup-paso, .v-cat…). */
.dig-opts[hidden]{display:none}
/* Separación propia, con un mínimo garantizado (5 px arriba, 3 abajo) que el auto-apretado del cajón NO puede
   quitar: él pidió que no quedaran pegadas a las casillas ni a los botones de abajo. Con aire disponible crece. */
.pago-body .dig-opts{margin-top:calc(5px + var(--aire, 16px) * .45);margin-bottom:calc(3px + var(--aire, 16px) * .25)}
.dig-opts .seg-dig{max-width:100%}
.seg-dig button{padding:5px 11px;font-size:12px}
/* Ajuste de existencias (07-sep, pedido Alejandro): botón ⛶ que extiende el modal a toda la pantalla; la lista toma el alto sobrante. */
#aje-dialog{max-width:940px}
#aje-dialog.maximized{max-width:100vw}
#aje-dialog.maximized .vm-body{display:flex;flex-direction:column;height:calc(100vh - 60px);max-height:none}
#aje-dialog.maximized #aje-body{display:flex;flex-direction:column;flex:1;min-height:0}
#aje-dialog.maximized #aje-form{display:flex;flex-direction:column;flex:1;min-height:0;height:auto;max-height:none}
#aje-dialog.maximized .aje-scroll{flex:1;min-height:0;max-height:none}

/* ─── Card «Usuarios de caja móvil» (Ajustes, 09-sep-2026) ─── */
.usu-lista{display:flex;flex-direction:column;margin:8px 0 4px}
.usu-row{display:flex;align-items:center;flex-wrap:wrap;gap:6px 10px;padding:7px 0;border-bottom:1px solid var(--border)}
.usu-row:last-child{border-bottom:0}
.usu-nom{min-width:120px}
.usu-off .usu-nom{opacity:.55;text-decoration:line-through}
.usu-nota{font-size:12px}
.usu-inline{display:inline-flex;align-items:center;gap:6px;margin:0}
.usu-row .btn-ghost,.usu-row .btn-primary,.usu-clave summary{padding:4px 10px;font-size:12.5px}
.usu-clave{display:inline-block}
.usu-clave summary{list-style:none;cursor:pointer;display:inline-flex;align-items:center;border:1px solid var(--border);border-radius:var(--r-ctl);background:var(--surface-2);font-weight:600}
.usu-clave summary::-webkit-details-marker{display:none}
.usu-clave[open] summary{margin-bottom:6px}
/* Teléfonos emparejados (11-sep): la card de Ajustes */
.disp-cod{font-family:'IBM Plex Mono',ui-monospace,Consolas,monospace;font-weight:700;font-size:15px;letter-spacing:.08em;background:var(--surface-2);border:1px solid var(--border);border-radius:var(--r-ctl);padding:3px 9px;white-space:nowrap}
.disp-row .inp-sm{width:180px;padding:4px 8px;font-size:12.5px}
.disp-info{font-size:12.5px}
.disp-pend{background:rgba(238,140,42,.07);border-radius:8px;padding-left:6px;padding-right:6px}
.disp-vacio{margin:8px 0 4px}
.disp-refrescar{padding:3px 9px;font-size:12px;margin-left:6px;vertical-align:baseline}
#disp-card .resp-pie{word-break:normal;overflow-wrap:break-word}
.usu-clave .inp{width:150px;padding:5px 8px}
.usu-nuevo{margin-top:10px;padding-top:10px;border-top:1px dashed var(--border)}

/* ═══ TELÉFONO (09-sep-2026, «Exynol en el teléfono», primer paso): hasta 720 px el sistema se acomoda a una
   pantalla angosta. Es el MISMO servidor y las MISMAS pantallas (un usuario de caja móvil entra por el navegador
   del teléfono en el Wi-Fi del negocio, como una caja en red); solo cambia cómo se distribuyen. Sin cámara: el
   buscador, las categorías y el modo Catálogo hacen el trabajo del lector de barras (decisión de Alejandro). ═══ */
@media (max-width:720px){
  .appbar{padding:6px 10px;gap:8px}
  .appbar .ex-soft{display:none}   /* el tagline «Impulsado por ULTRACODE» ya NO se apaga aquí (12-oct): ver «MEMBRETE DEL TELÉFONO EN DOS PISOS» */
  .appbar .ex-exy{height:22px}
  .appbar-title{font-size:16px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}
  .appbar .btn-tema{padding:6px 9px;font-size:12px}
  .tema-movil{display:inline-flex;align-items:center;justify-content:center}
  .appbar-pie{font-size:10px}
  .launcher{flex-wrap:nowrap;overflow-x:auto;padding:4px 8px;gap:2px;scrollbar-width:none}
  .launcher::-webkit-scrollbar{display:none}
  .tile{min-width:64px;padding:6px 6px}
  .tile .ic,.tile .ic svg{width:24px;height:24px}
  .tile .lbl{font-size:11px}
  .content{padding:10px}
  .toolbar{gap:6px;margin-bottom:8px}
  .inp,select.inp{font-size:16px}   /* con letra ≥ 16 px iOS no hace zoom al enfocar un campo */
  .tabla-scroll{max-width:100%}
  /* La Caja a pantalla completa, en UNA columna que se desplaza; en Catálogo las tarjetas primero, a dos por fila,
     y debajo los datos de la venta, el Detalle y el cajón Cobrar. */
  .venta-modal,.venta-modal.maximized{width:100vw;max-width:100vw;height:100vh;max-height:100vh;border-radius:0}
  .venta-modal form,.venta-modal.maximized form{max-height:100vh;height:100vh}
  .venta-modal .vm-head{padding:8px 10px;gap:6px;flex-wrap:wrap;border-radius:0}
  .venta-modal .vm-head h2{font-size:16px}
  .venta-modal .prox-venta{font-size:12px}
  .venta-modal .seg{margin-left:0}
  .venta-modal .vm-body{display:flex;flex-direction:column;overflow-y:auto;overflow-x:hidden;-webkit-overflow-scrolling:touch}
  .venta-modal .vm-sec{padding:8px 10px}
  .venta-modal .vm-row-top{display:flex;flex-direction:column;flex:none}
  .venta-modal .vm-row-top .vm-sec:nth-child(2){display:none}   /* Tasas del día: en el teléfono valen las fijadas en la PC */
  .venta-modal .fn-row .fld{flex:1 1 45%;min-width:0}
  .venta-modal .v-add-wrap{flex-direction:column;gap:8px}
  .venta-modal .v-img-box{display:none}
  .venta-modal.modo-cat .vm-body{display:flex;flex-direction:column}
  /* Su lienzo (09-sep): el catálogo ocupa la pantalla (al menos 4 tarjetas a la vista: fotos más bajas y solo tamaño M),
     y debajo, al desplazar, Datos de la venta, Detalle y el cajón Cobrar. */
  .venta-modal.modo-cat #v-sec-agregar{order:-1;border-right:0;border-bottom:1px solid var(--border);flex:none;height:calc(100vh - 56px);min-height:640px;max-height:none}
  .venta-modal .v-cat-grid .cat-foto{height:96px}
  .venta-modal #cat-tam{display:none}
  /* El cajón Cobrar no puede quedar pegado al borde: la barra del navegador del teléfono lo tapaba. Aire al final del
     cuerpo (más la zona segura del teléfono) para que suba entero al desplazar. */
  .venta-modal .vm-body::after{content:"";display:block;flex:none;height:calc(120px + env(safe-area-inset-bottom, 0px))}
  /* Su lienzo (09-sep): en modo Catálogo el buscador vive en la cabecera del modal, en su propia fila y a todo el ancho,
     con la ✕ al lado (el espaciador deja de crecer para que el buscador ocupe la fila). */
  .venta-modal .vm-head .v-cat-buscar{flex:1 1 200px;min-width:0}
  .venta-modal.modo-cat .vm-head .vm-spacer{flex:0 0 0}
  .venta-modal .vm-head .v-cat-buscar .inp{height:38px;width:100%}
  .venta-modal.modo-cat .vm-row-bottom{flex:none;display:flex;flex-direction:column}
  .v-cat-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}
  .v-cat-top{gap:6px 8px}
  .v-cat-top .inp{min-width:0;flex:1 1 160px}
  .venta-modal .vm-foot,.venta-modal.maximized .vm-foot{width:100%;padding:8px 10px}
  .venta-modal .vm-detalle .grid{font-size:12.5px}
  .venta-modal .vm-detalle{padding-left:6px;padding-right:6px}
  .pago-body .vm-pago{gap:4px}
  .cfg-page{display:flex;flex-direction:column}
  .usu-row .usu-nom{min-width:0;flex:1 1 100%}
}

/* ═══ MEMBRETE DEL TELÉFONO EN DOS PISOS (12-oct-2026; Alejandro, tras instalar la app en su teléfono: «el membrete no se llega a
   ver bien en el teléfono, se ve cortado, incluso no se llega a ver el "Impulsado por ULTRACODE"»).
   CAUSA: hasta 720 px el membrete seguía siendo la fila ÚNICA de la PC —marca · título · botones con el pie debajo— metida en
   320–412 px. El bloque de la derecha no se encoge (los tres botones, ~153 px, y el pie «Facturar · Móvil · admin · v0.161.0»,
   ~205 px, sin salto de renglón), así que el título era lo único elástico: a 390 px quedaba en «Report…», «Client…», «Auditor…» y
   «Merca…», a 360 hasta «Ventas» se cortaba y a 320–340 px desaparecía; y para que todo eso cupiera, la 0.141 había APAGADO el
   tagline (.ex-tag{display:none}).
   ARREGLO: dos pisos, solo con CSS y dentro de esta media query (la PC no cambia; del marcado solo se envolvió el rótulo de «Salir»):
     1.º la marca ENTERA —«Exynol / Software ERP», el filete e «Impulsado por / ULTRACODE», la misma variante B de la PC— a la
         izquierda, y los botones (tema · Mi clave · Salir) a la derecha;
     2.º el título del módulo a la izquierda, ahora con sitio, y el pie (modo · caja · versión) a la derecha.
   Con flex-wrap: .appbar-der se disuelve (display:contents) para que cada hijo vaya a su piso y el .spacer hace de salto de renglón
   (ocupa un renglón propio de alto 0: por eso row-gap va a 0 y el aire del 2.º piso es un margen). Una rejilla no sirve: con
   columnas compartidas el pie (lo más ancho) ensancharía también la columna de los botones y la marca ya no cabría.
   PANTALLAS ANGOSTAS (teléfonos chicos o con «Tamaño de pantalla» grande en Android; escalones abajo): la marca se reduce ENTERA y
   en proporción con zoom —lockup, filete y pixel-art juntos, nunca un pedazo—, así ULTRACODE no se deforma ni se recorta; antes de
   achicarla de más, «Salir» queda en su icono. Si aun así no cupiera, los botones bajarían de renglón antes que salirse. ═══ */
@media (max-width:720px){
  .appbar{flex-wrap:wrap;row-gap:0}
  .appbar>.brand{order:1;padding:0 2px}
  .appbar-der{display:contents}
  .appbar-acc{order:2;margin-left:auto}
  .appbar>.spacer{order:3;flex:0 0 100%;height:0}
  .appbar-title{order:4;flex:1 1 0;margin-top:2px;font-size:15px;line-height:1.3}   /* 15 px: «Mercado Cambiario», el más largo del teléfono, entra entero a 390 px */
  .appbar-pie{order:5;margin:2px 0 0 auto;min-width:0;max-width:100%}
  /* Sin su caja (.appbar-der) los botones y el pie pierden el z-index de los hijos del membrete: sin esto quedarían bajo el patrón Exy. */
  .appbar-acc,.appbar-pie{position:relative;z-index:1}
  /* Un usuario largo («Móvil · …», hasta 32 caracteres) se acorta con «…» en vez de comerse el título: el nombre llega como mucho
     hasta donde el título conserva ~72 px («Auditoría»). 232 = 20 de márgenes + 8 de separación + 72 del título + ~132 del resto del
     pie (la ficha del modo, los «·» y la versión). */
  .appbar-pie b{min-width:0;overflow:hidden;text-overflow:ellipsis;max-width:max(48px,calc(100vw - 232px))}
  .appbar .ex-tag{margin-left:9px;padding-left:9px}
  /* Los avisos (window.toast) salían a 64 px, justo bajo el membrete de un piso (60 px): con dos pisos (70–74 px) tapaban el título
     y el pie. Siguen saliendo justo debajo. */
  .appbar>.toast-host{top:80px}
}
/* Escalones medidos con las dos ediciones (el recuadro «Software ERP Lite» es ~10 px más ancho) y ≥ 10 px de holgura en el 1.er piso:
   ≤379 px la marca a ×0,9 (360, el Android más común, y el iPhone de 375); ≤359 px «Salir» queda en su icono, como «Mi clave»
   (views/layout.js, .lbl-salir), en vez de encoger más la marca: a ×0,8 «Impulsado por» bajaría a 6 px; ≤319 px, el último recurso. */
@media (max-width:379px){.appbar .exbrand{zoom:.9}}
@media (max-width:359px){.appbar .lbl-salir{display:none}.appbar form .btn-tema .ic16{margin-right:0}}
@media (max-width:319px){.appbar .exbrand{zoom:.8}.appbar .btn-tema{padding:6px 7px}}
/* En HORIZONTAL el teléfono pasa de 720 px y toma el membrete de la PC, que ahí ya cabe en una fila y con el tagline: solo perdía el
   alternador de tema, que es del teléfono (09-sep) pero colgaba del ancho. body[data-movil] = sesión de un teléfono emparejado: la
   PC nunca lo lleva, así que en el escritorio sigue sin aparecer. */
body[data-movil] .tema-movil{display:inline-flex;align-items:center;justify-content:center}

/* ═══ TELÉFONO · EL DETALLE DE LA VENTA (06-oct-2026, 0.158; decisión de Alejandro sobre el lienzo «Teléfono: el Detalle de la
   venta»: «1. B  2. La captura se pliega.  4. Si esta bien.  5. Si.»). SOLO el teléfono: sesión de un navegador emparejado
   (body[data-movil]) y pantalla de hasta 720 px —la misma media query con que la Caja ya se acomoda—; la PC no cambia, ni con
   la ventana angosta. El markup del Detalle lo pinta views/ventas.js (detTelHtml) solo en ese caso.
   - B · RENGLÓN PLEGADO: un piso por producto (nombre, lo que lleva y su SubTotal); al tocarlo se abren sus contadores por tipo,
     el descuento de cada tipo con su alternador y «Quitar». El precio de cada tipo va en pequeño («× 1.500») bajo su SubTotal.
   - LA CAPTURA SE PLIEGA en la barra «+ Agregar otro producto» tras «+ Agregar» (Formulario).
   - EL PIE FIJO: los totales y la ficha «Cobrar» quedan abajo en los dos modos (ubicarPieMovil lo saca del cuerpo que se
     desliza); el cajón se sigue desplegando hacia arriba y, si no cabe, su cuerpo se desliza con los botones pegados abajo.
   - «DATOS DE LA VENTA» EN UN RENGLÓN («1. Plegamos en un renglón»): cliente · fecha, que se despliega al tocarlo o solo cuando
     una acción necesita uno de sus campos (vDatosPlegar). ═══ */
#v-add-otro[hidden]{display:none}   /* la barra es .btn-ghost (inline-flex): sin esto se vería en la PC */
#v-datos-pleg[hidden]{display:none}   /* el renglón de «Datos de la venta»: lo enciende solo el teléfono (abajo) */
@media (max-width:720px){
  /* La Caja mide lo que SE VE (dvh): con 100vh la barra del navegador del teléfono tapaba el fondo, justo donde va el pie. */
  body[data-movil] #venta-dialog,body[data-movil] #venta-dialog form{height:100vh;max-height:100vh;height:100dvh;max-height:100dvh}
  body[data-movil] #venta-dialog .vm-body{flex:1 1 0;min-height:0}
  body[data-movil] #venta-dialog .vm-body::after{height:14px}
  body[data-movil] #venta-dialog form>.vm-foot{flex:0 1 auto;min-height:0;width:100%;padding:6px 10px calc(7px + env(safe-area-inset-bottom, 0px));gap:3px;border-top:1px solid var(--border);border-radius:0;box-shadow:0 -6px 16px rgba(16,24,40,.14);position:relative;z-index:2}
  body[data-movil] #venta-dialog form>.vm-foot .pago-body .vm-btns{position:sticky;bottom:calc(-5px - var(--aire, 16px) / 4);z-index:1;background:var(--surface-2);padding-bottom:2px;flex-wrap:wrap;row-gap:6px}   /* a 360 px los cuatro botones no caben en una fila: «Procesar» se salía por la izquierda */
  /* Catálogo: las tarjetas ocupan lo que se ve entre la cabecera y el pie; debajo, al deslizar, Datos de la venta y el Detalle. */
  /* 0.163 (12-oct-2026, pedido de Alejandro: «hacer el scroll del modo catálogo horizontal en vez de vertical, de manera que se pueda
     simplemente bajar a la lista de detalle sin que se confunda bajando el catálogo»): en el teléfono el Catálogo es UNA fila de
     tarjetas que se desliza de LADO (con imán por tarjeta); bajar con el dedo lleva a Datos de la venta y al Detalle. Las tandas
     siguen cargando con el centinela #cat-mas, ahora a la derecha (el observador mira el cat-scroll en las cuatro direcciones).
     Antes la sección ocupaba toda la altura y el catálogo se deslizaba hacia abajo dentro de ella. La PC no cambia. */
  body[data-movil] #venta-dialog.modo-cat #v-sec-agregar{height:auto;min-height:0}
  body[data-movil] #venta-dialog.modo-cat .v-cat-scroll{flex:none;display:flex;align-items:stretch;gap:10px;min-height:0;overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;-webkit-overflow-scrolling:touch;scroll-snap-type:x proximity;padding:2px 2px 10px}
  body[data-movil] #venta-dialog.modo-cat .v-cat-grid,body[data-movil] #venta-dialog.modo-cat .v-cat-grid.tam-s,body[data-movil] #venta-dialog.modo-cat .v-cat-grid.tam-l{display:grid;grid-auto-flow:column;grid-template-columns:none;grid-auto-columns:minmax(0,46vw);gap:10px;flex:none}
  body[data-movil] #venta-dialog.modo-cat .v-cat-grid.tam-s{grid-auto-columns:minmax(0,38vw)}
  body[data-movil] #venta-dialog.modo-cat .v-cat-grid.tam-l{grid-auto-columns:minmax(0,62vw)}
  body[data-movil] #venta-dialog.modo-cat .cat-card{scroll-snap-align:start}
  body[data-movil] #venta-dialog.modo-cat .v-cat-grid .cat-foto{height:96px}
  body[data-movil] #venta-dialog.modo-cat .v-cat-grid.tam-l .cat-foto{height:130px}
  body[data-movil] #venta-dialog.modo-cat .v-cat-mas{flex:none;width:110px;display:flex;align-items:center;justify-content:center;text-align:center;padding:0 8px}
  body[data-movil] #venta-dialog.modo-cat .v-cat-mas[hidden]{display:none}
  /* 0.163 (12-oct-2026, pedido de Alejandro: «el modo formulario es un desorden: ni siquiera se mira el cuadro de la imagen del
     producto, y al desplegar la lista de Artículo esta expande de más la página entera»): Artículo va PRIMERO y a todo lo ancho,
     con la imagen del producto (64 px) a su derecha; debajo, por pares: Código · Inventario (si hay) · Tarifa (si hay) · Cantidad ·
     Precio · Descuento · SubTotal; al final «+ Agregar» y «Limpiar». La lista de Artículo se abre FIJA sobre la pantalla (antes era
     absoluta dentro del cuerpo que se desliza y estiraba la Caja hacia abajo). La PC no cambia. */
  body[data-movil] #venta-dialog .v-add-wrap{display:block;position:relative}
  body[data-movil] #venta-dialog .v-img-box{display:flex;position:absolute;right:0;top:0;width:64px;height:64px;margin:0;border-radius:8px;z-index:1}
  body[data-movil] #venta-dialog .v-img-box #v-img-ph{font-size:10px;text-align:center;line-height:1.1}
  body[data-movil] #venta-dialog .v-add-linea{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px 10px;align-items:end}
  body[data-movil] #venta-dialog .v-add-linea>.fld{flex:none;min-width:0;width:auto}
  body[data-movil] #venta-dialog .v-add-linea>.v-fld-art{grid-column:1/-1;order:-3;width:calc(100% - 74px);min-width:0}
  body[data-movil] #venta-dialog .v-add-linea>.fld:has(#v-codigo){order:-2}
  body[data-movil] #venta-dialog .v-add-linea>.fld:has(#v-inv-filtro){order:-1}
  body[data-movil] #venta-dialog .v-add-linea>#v-tar-wrap{order:0}
  body[data-movil] #venta-dialog .v-add-linea>#v-cant-fld{order:1}
  body[data-movil] #venta-dialog .v-add-linea>.v-fld-precio{order:2}
  body[data-movil] #venta-dialog .v-add-linea>.fld:has(#v-desc){order:3}
  body[data-movil] #venta-dialog .v-add-linea>.fld:has(#v-subtotal-pre){order:4}
  body[data-movil] #venta-dialog .v-add-linea>.btn-primary{order:5;grid-column:1;width:100%;justify-content:center}
  body[data-movil] #venta-dialog .v-add-linea>.btn-ghost{order:6;grid-column:2;width:100%;justify-content:center}
  body[data-movil] #venta-dialog .v-combo-list{position:fixed;left:8px;right:8px;top:8px;bottom:auto;max-height:calc(100dvh - 16px);z-index:60}
  body[data-movil] #venta-dialog .v-combo-items{flex:1 1 auto;min-height:0;max-height:calc(100dvh - 90px)}
  /* El Detalle con su alto natural (la página es la que se desliza) y sin deslizarse de lado. */
  body[data-movil] #venta-dialog .vm-row-bottom{flex:none;display:block}
  body[data-movil] #venta-dialog .vm-detalle{flex:none;overflow-x:auto;overflow-y:hidden;border:0;scrollbar-gutter:auto;padding-left:4px;padding-right:4px}
  /* public/vivo.js envuelve en el teléfono toda tabla .grid en un .tbl-scroll con 640 px de mínimo para deslizarla de lado: el
     Detalle ya no es una tabla ancha, así que su envoltorio no desliza ni le impone ancho. */
  body[data-movil] #venta-dialog .vm-detalle .tbl-scroll{overflow:visible}
  body[data-movil] #venta-dialog .vm-detalle .tbl-scroll>table.grid{min-width:0}
  body[data-movil] #venta-dialog .vm-detalle .grid{display:block;font-size:13px}
  body[data-movil] #venta-dialog .vm-detalle .grid>thead{display:none}
  body[data-movil] #venta-dialog #vlineas-body,body[data-movil] #venta-dialog #vlineas-body>tr,body[data-movil] #venta-dialog #vlineas-body>tr>td{display:block}
  body[data-movil] #venta-dialog #vlineas-body>tr>td{padding:0;border:0}
  body[data-movil] #venta-dialog #vlineas-body>tr>td.vacio{padding:26px 14px}
  body[data-movil] #venta-dialog #vlineas-body>tr+tr{border-top:1px solid var(--border)}
  body[data-movil] #venta-dialog #vlineas-body>tr:hover{background:transparent}
  /* El piso plegado: nombre (hasta 3 renglones) · SubTotal y lo que lleva · la flecha. Todo el piso es el botón. */
  body[data-movil] #venta-dialog .dt-cab{display:flex;align-items:center;gap:8px;width:100%;min-height:50px;margin:0;padding:8px 6px 8px 10px;border:0;background:transparent;color:var(--text);font:inherit;text-align:left;cursor:pointer;-webkit-tap-highlight-color:transparent}
  body[data-movil] #venta-dialog tr.abierta .dt-cab{background:var(--row-hover)}
  body[data-movil] #venta-dialog .dt-nomw{flex:1;min-width:0;display:block}
  body[data-movil] #venta-dialog .dt-nom{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;overflow:hidden;overflow-wrap:anywhere;font-size:13px;font-weight:600;line-height:17px}
  body[data-movil] #venta-dialog .dt-tar{display:inline-block;margin-left:4px;padding:0 4px;border:1px solid var(--violet);border-radius:5px;color:var(--violet);font-size:10px;font-weight:700;line-height:14px;vertical-align:1px}
  body[data-movil] #venta-dialog .dt-mods{display:block;margin-top:1px;font-size:11px;line-height:14px;color:var(--muted)}
  body[data-movil] #venta-dialog .dt-der{flex:none;min-width:92px;max-width:136px;display:flex;flex-direction:column;align-items:flex-end;gap:1px;text-align:right}
  body[data-movil] #venta-dialog .dt-der>b{font-size:14px;font-weight:700;line-height:18px;font-variant-numeric:tabular-nums;white-space:nowrap}
  body[data-movil] #venta-dialog .dt-res{font-size:11.5px;line-height:15px;color:var(--muted)}
  body[data-movil] #venta-dialog .dt-res em{font-style:normal;font-weight:700;color:var(--violet)}
  body[data-movil] #venta-dialog .dt-res .tv-suf{font-size:10px;line-height:13px;padding:0 3px;border-radius:5px;vertical-align:0}
  body[data-movil] #venta-dialog .dt-d{color:var(--accent-text);font-weight:600;white-space:nowrap}
  body[data-movil] #venta-dialog .dt-chev{flex:none;width:18px;height:18px;color:var(--muted);transition:transform .15s ease}
  body[data-movil] #venta-dialog tr.abierta .dt-chev{transform:rotate(180deg)}
  /* Abierto: una línea por tipo (contador · descuento con su alternador · SubTotal con el precio en pequeño) y «Quitar». Si algo
     no cupiera en una pantalla muy angosta, el SubTotal baja a su propio renglón: nunca se desliza de lado. */
  body[data-movil] #venta-dialog .dt-panel{display:flex;flex-direction:column;gap:4px;padding:6px 8px 8px;background:var(--surface-2);border-top:1px dashed var(--border)}
  body[data-movil] #venta-dialog .dt-ln{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:4px 6px;min-height:34px}
  body[data-movil] #venta-dialog .dt-step{flex:none;margin-right:auto;display:inline-flex;align-items:stretch;height:32px;border:1px solid var(--border);border-radius:8px;overflow:hidden;background:var(--surface)}
  body[data-movil] #venta-dialog .dt-step>button{width:26px;margin:0;padding:0;border:0;background:transparent;color:var(--text);font:inherit;font-size:17px;line-height:1;cursor:pointer}
  body[data-movil] #venta-dialog .dt-qty{width:28px;margin:0;padding:0;border:0;background:transparent;color:var(--text);text-align:center;font:inherit;font-size:14px;font-weight:600;font-variant-numeric:tabular-nums}
  body[data-movil] #venta-dialog .dt-qty:focus{outline:none;background:var(--surface-2);box-shadow:inset 0 0 0 2px var(--accent)}
  body[data-movil] #venta-dialog .dt-tv{display:inline-flex;flex-direction:column;align-items:center;justify-content:center;gap:1px;min-width:44px;padding:0 3px;border-right:1px solid var(--border);background:rgba(51,69,110,.14);color:var(--violet);font-family:var(--font-ui);font-size:11px;font-weight:700;line-height:12px;white-space:nowrap}
  body[data-movil] #venta-dialog .dt-tv.fijo{background:transparent;color:var(--muted)}
  [data-theme="oscuro"] body[data-movil] #venta-dialog .dt-tv{background:rgba(150,170,220,.16)}
  [data-theme="oscuro"] body[data-movil] #venta-dialog .dt-tv.fijo{background:transparent}
  body[data-movil] #venta-dialog .dt-tv .tv-suf{font-size:10px;line-height:12px;padding:0 3px;border-radius:5px;vertical-align:0}
  body[data-movil] #venta-dialog .dt-ln.vacia .dt-step{opacity:.55;border-style:dashed}
  body[data-movil] #venta-dialog .dt-ln.vacia .dt-qty{color:var(--muted)}
  body[data-movil] #venta-dialog .dt-desc{flex:none;display:inline-flex;align-items:center;gap:2px}
  body[data-movil] #venta-dialog .dt-desc:empty{display:none}
  /* La casilla conserva sus reglas de crecimiento (det-pct / det-monto, ajustes 4b y 5): aquí solo cambian el alto y el margen. */
  body[data-movil] #venta-dialog .dt-desc .det-pct{height:30px;margin:0;vertical-align:middle}
  body[data-movil] #venta-dialog .dt-desc .det-pct:disabled{opacity:.6}
  body[data-movil] #venta-dialog .dt-tog{min-width:30px;height:30px;margin:0;padding:0 3px;border:0;background:none;color:var(--text);font:600 12px var(--font-ui);text-decoration:underline dotted;text-underline-offset:2px;cursor:pointer}
  body[data-movil] #venta-dialog .dt-sub{flex:none;min-width:70px;display:flex;flex-direction:column;align-items:flex-end;line-height:1.2;font-variant-numeric:tabular-nums;text-align:right}
  body[data-movil] #venta-dialog .dt-sub>b{font-size:14px;font-weight:700;white-space:nowrap}
  body[data-movil] #venta-dialog .dt-pu{font-style:normal;font-size:11px;color:var(--muted);white-space:nowrap}
  body[data-movil] #venta-dialog .dt-pu .det-precio{width:72px;height:24px;font-size:12px;padding:0 4px}
  body[data-movil] #venta-dialog .dt-pu .det-eq{font-size:10.5px;margin-left:2px}
  body[data-movil] #venta-dialog .dt-pie{display:flex;justify-content:flex-end;padding-top:2px}
  body[data-movil] #venta-dialog .dt-quitar{display:inline-flex;align-items:center;gap:5px;height:32px;margin:0;padding:0 10px;border:1px solid var(--border);border-radius:8px;background:var(--surface);color:var(--no);font:600 12.5px var(--font-ui);cursor:pointer}
  body[data-movil] #venta-dialog .dt-quitar svg{width:16px;height:16px;fill:currentColor}
  /* LA CAPTURA SE PLIEGA (Formulario): la barra «+ Agregar otro producto» ocupa su lugar hasta que se toca. */
  body[data-movil] #venta-dialog.cap-plegada:not(.modo-cat) #v-sec-agregar>.vm-sec-t,
  body[data-movil] #venta-dialog.cap-plegada:not(.modo-cat) #v-sec-agregar>.v-add-wrap{display:none}
  body[data-movil] #venta-dialog.cap-plegada:not(.modo-cat) #v-add-otro{display:flex}
  body[data-movil] #venta-dialog #v-add-otro{align-items:center;justify-content:center;gap:6px;width:100%;height:42px;padding:0 14px;font-size:14px}
  body[data-movil] #venta-dialog #v-add-otro svg{width:18px;height:18px;flex:none}
  /* «DATOS DE LA VENTA» EN UN RENGLÓN (decisión de Alejandro: «1. Plegamos en un renglón»): «DATOS DE LA VENTA · cliente · fecha» y,
     solo si hay algo, una segunda línea con las fichas de lo encendido y el aviso del cliente. Al tocarlo se despliega la sección
     de siempre y el renglón queda de título (views/ventas.js → vDatosPlegar). */
  body[data-movil] #venta-dialog #v-sec-datos{padding-top:0}
  body[data-movil] #venta-dialog #v-sec-datos>.vm-sec-t{display:none}
  body[data-movil] #venta-dialog.datos-plegados #v-sec-datos{padding-bottom:0}
  body[data-movil] #venta-dialog.datos-plegados #v-sec-datos>.fn-row{display:none}
  body[data-movil] #venta-dialog #v-datos-pleg{display:flex;flex-wrap:wrap;align-items:center;gap:2px 6px;width:100%;min-height:40px;margin:0;padding:9px 0;border:0;background:transparent;color:var(--text);font:inherit;text-align:left;cursor:pointer;-webkit-tap-highlight-color:transparent}
  body[data-movil] #venta-dialog .v-datos-tit{flex:none;font:600 12px/18px var(--font-head);letter-spacing:.07em;text-transform:uppercase;color:var(--muted)}   /* .07em (no .08): «Consumidor Final · 06/10/2026» entero a 390 px */
  body[data-movil] #venta-dialog .v-datos-res{flex:1 1 0;min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-size:13px;line-height:18px}
  body[data-movil] #venta-dialog .v-datos-chev{flex:none;width:18px;height:18px;color:var(--muted);transition:transform .15s ease}
  body[data-movil] #venta-dialog #v-datos-pleg[aria-expanded="true"] .v-datos-chev{transform:rotate(180deg)}
  body[data-movil] #venta-dialog .v-datos-l2{flex:1 1 100%;min-width:0;display:flex;flex-wrap:wrap;align-items:center;gap:3px 6px}
  body[data-movil] #venta-dialog .v-datos-l2[hidden]{display:none}
  body[data-movil] #venta-dialog .v-datos-flags{display:inline-flex;flex-wrap:wrap;gap:4px}
  body[data-movil] #venta-dialog .v-datos-flags:empty{display:none}
  body[data-movil] #venta-dialog .v-datos-flag{padding:0 5px;border:1px solid var(--accent);border-radius:5px;color:var(--accent-text);font-size:10.5px;font-weight:700;line-height:15px;white-space:nowrap}
  body[data-movil] #venta-dialog .v-datos-aviso{flex:1 1 auto;min-width:0;font-size:12.5px;font-weight:600;line-height:17px}
  /* Desplegada, el selector de Cliente medía lo que el nombre más largo de la lista y se salía por la derecha: a su celda. */
  body[data-movil] #venta-dialog #cliente-wrap{display:block;min-width:0}
  body[data-movil] #venta-dialog #cliente-wrap>select{width:100%;max-width:100%}
  /* Con «Global» activo su unidad (% o la moneda) también alterna, como el alternador de cada línea. */
  body[data-movil] #venta-dialog #v-dg-un{min-width:28px;padding:4px 2px;text-align:center;text-decoration:underline dotted;text-underline-offset:2px;cursor:pointer}
}

/* ═══ TELÉFONO · EL DETALLE DE LOS MOVIMIENTOS DE REPORTES (12-oct-2026; propuesta C «La lista manda» del lienzo del 12-oct «El detalle
   de los movimientos, en el teléfono», https://claude.ai/artifact/QD18MWDy3UgBRwqqoxXdGT; decisión de Alejandro: «Usemos la C que
   recomiendas para la restructuración del teléfono»). SOLO el teléfono: el marcado nuevo lo pinta views/reportes.js solo en una
   sesión de teléfono (body[data-movil]) y aquí solo se enciende hasta 720 px —en horizontal el teléfono ve la tabla y la ventana de
   siempre—; la PC no recibe marcado nuevo. Lo de la PC que el teléfono aparta va envuelto en .rep-solo-pc.
   - Renglones de 58 px en lugar de las tablas («Ventas del día» y «Ventas por día / semana / mes»): hora o día · cliente o ventas ·
     venta y ganancia; la anulada atenuada y tachada; el renglón marcado (con su barra) en el gris de las filas con el filete naranja.
   - El total FIJO abajo (position:sticky dentro de su lista: se va con ella al pasarla). bottom:-24px = el relleno inferior de
     .content en el teléfono, para que quede pegado al borde y no flotando 24 px arriba.
   - LA FICHA de la venta (#rep-fac-dialog > .rep-ficha): pantalla entera, cabecera fija con ‹ › y la ✕ de 44 px, cuerpo que se
     desliza y pie fijo con los totales, «Abrir en Ventas» y «Cerrar»; productos plegados en dos pisos (public/app.js los abre). ═══ */
body[data-movil] .rep-tl,body[data-movil] .rep-ficha{display:none}
@media (max-width:720px){
  body[data-movil] .rep-solo-pc{display:none}
  body[data-movil] .rep-tl{display:block;margin:2px 0 16px}
  body[data-movil] .rep-tl-t{display:flex;flex-wrap:wrap;align-items:baseline;gap:0 8px;margin:6px 2px 7px}
  body[data-movil] .rep-tl-t small{font:400 12.5px var(--font-ui);color:var(--muted)}
  body[data-movil] .rep-tl-lista{display:flex;flex-direction:column;border:1px solid var(--border);border-radius:var(--r-card);background:var(--surface);box-shadow:var(--shadow-md);overflow:hidden}
  body[data-movil] .rep-tl-lista.rep-tl-late{animation:vivoTabla 1.5s ease}
  body[data-movil] .rep-tl-vacio{margin:0;padding:18px 14px;color:var(--muted);font-size:13.5px}
  body[data-movil] .rep-tl-fila{display:flex;align-items:center;gap:9px;width:100%;min-height:58px;margin:0;padding:7px 6px 7px 11px;border:0;border-top:1px solid var(--border);border-radius:0;background:transparent;color:var(--text);font:inherit;text-align:left;cursor:pointer;-webkit-tap-highlight-color:transparent;font-variant-numeric:tabular-nums}
  body[data-movil] .rep-tl-fila:first-child{border-top:0}
  body[data-movil] .rep-tl-fila:active{background:var(--row-hover)}
  body[data-movil] .rep-tl-fila.sel{background:var(--row-hover);box-shadow:inset 3px 0 0 var(--accent)}
  body[data-movil] .rep-tl-fila.vivo-fila{animation:vivoFila 2.3s cubic-bezier(.2,.7,.2,1) both}
  body[data-movil] .rep-tl-h{flex:none;width:46px;display:flex;flex-direction:column;line-height:1.15}
  body[data-movil] .rep-tl-h.ancha{width:auto;min-width:46px;max-width:42%}
  body[data-movil] .rep-tl-h b{font-size:15px;font-weight:700;white-space:nowrap}
  body[data-movil] .rep-tl-h small{font-size:11px;color:var(--muted);white-space:nowrap}
  body[data-movil] .rep-tl-per .rep-tl-h small{text-transform:uppercase;letter-spacing:.04em}
  body[data-movil] .rep-tl-m{flex:1;min-width:0;display:flex;flex-direction:column;line-height:1.3}
  body[data-movil] .rep-tl-m b{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-size:13.5px;font-weight:600}
  body[data-movil] .rep-tl-m small{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-size:12px;color:var(--muted)}
  body[data-movil] .rep-tl-m .cur-usd,body[data-movil] .rep-tl-m .cur-ves,body[data-movil] .rep-tl-m .cur-cop{padding:1px 5px;font-size:10.5px;margin-right:3px;vertical-align:0}
  body[data-movil] .rep-tl-m .rep-delta{padding:0 6px;font-size:11px;line-height:1.35;vertical-align:0}
  body[data-movil] .rep-tl-dev{color:var(--accent-text);font-weight:600}
  body[data-movil] .rep-tl-curso{font-style:normal;font-weight:600;font-size:11px;color:var(--muted)}
  body[data-movil] .rep-tl-d{flex:none;display:flex;flex-direction:column;align-items:flex-end;line-height:1.25;text-align:right}
  body[data-movil] .rep-tl-d b{font-size:15.5px;font-weight:700;white-space:nowrap}
  body[data-movil] .rep-tl-d small{font-size:12px;white-space:nowrap}
  body[data-movil] .rep-tl-g{font-weight:600;color:var(--ok)}
  body[data-movil] .rep-tl-est{display:inline-block;margin-top:1px;padding:1px 8px;border-radius:999px;background:var(--ex-min-bg);color:var(--ex-min-tx);font-size:11px;font-weight:700}
  body[data-movil] .rep-tl-chev{flex:none;width:16px;height:16px;color:var(--muted)}
  body[data-movil] .rep-tl-fila.anulada{opacity:.62}
  body[data-movil] .rep-tl-fila.anulada .rep-tl-d b{text-decoration:line-through}
  body[data-movil] .rep-tl-pie{position:sticky;bottom:-24px;z-index:3;display:flex;align-items:center;gap:12px;margin:8px -10px 0;padding:8px 12px 10px;background:var(--surface);border-top:1px solid var(--border);box-shadow:0 -6px 16px rgba(16,24,40,.14);font-variant-numeric:tabular-nums}
  [data-theme="oscuro"] body[data-movil] .rep-tl-pie{box-shadow:0 -6px 16px rgba(0,0,0,.4)}
  body[data-movil] .rep-tl-pie-iz{flex:1;min-width:0;display:flex;flex-direction:column;line-height:1.2}
  body[data-movil] .rep-tl-pie-iz b{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-size:14px}
  body[data-movil] .rep-tl-pie-iz small{font-size:11.5px;color:var(--muted);white-space:nowrap}
  body[data-movil] .rep-tl-pie-n{flex:none;display:flex;flex-direction:column;align-items:flex-end;line-height:1.15}
  body[data-movil] .rep-tl-pie-n span{font:600 10px var(--font-head);letter-spacing:.07em;text-transform:uppercase;color:var(--muted)}
  body[data-movil] .rep-tl-pie-n b{font-size:16px;white-space:nowrap}
  body[data-movil] .rep-tl-pie-n b.v,body[data-movil] .rf-kv b,body[data-movil] .rf-v,body[data-movil] .rf-tot-v{color:#2a6fb5}
  [data-theme="oscuro"] body[data-movil] .rep-tl-pie-n b.v,[data-theme="oscuro"] body[data-movil] .rf-kv b,[data-theme="oscuro"] body[data-movil] .rf-v,[data-theme="oscuro"] body[data-movil] .rf-tot-v{color:#7cb5ec}
  body[data-movil] .rep-tl-pie-n b.g,body[data-movil] .rf-kg b,body[data-movil] .rf-tot-g{color:var(--ok)}
  /* La ficha: la ventana entera es la pantalla; solo el cuerpo se desliza. */
  body[data-movil] #rep-fac-dialog>.vm-head,body[data-movil] #rep-fac-dialog>.vm-body{display:none}
  body[data-movil] #rep-fac-dialog[open]{display:flex;flex-direction:column;width:100vw;max-width:100vw;height:100vh;height:100dvh;max-height:100vh;max-height:100dvh;margin:0;border-radius:0;overflow:hidden}
  body[data-movil] #rep-fac-dialog>.rep-ficha{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;font-variant-numeric:tabular-nums}
  body[data-movil] .rf-cab{flex:none;padding:calc(8px + env(safe-area-inset-top, 0px)) 10px 8px;color:#fff;background:linear-gradient(135deg,#1d2c50,#0c1730)}
  body[data-movil] .rf-nav{display:flex;align-items:center;gap:6px;min-width:0}
  body[data-movil] .rf-ir{flex:none;display:inline-flex;align-items:center;justify-content:center;gap:2px;min-width:68px;height:44px;margin:0;padding:0 8px;border:1px solid rgba(255,255,255,.22);border-radius:10px;background:rgba(255,255,255,.12);color:#fff;font:600 13px var(--font-ui);cursor:pointer;-webkit-tap-highlight-color:transparent}
  body[data-movil] .rf-ir[hidden]{display:none}
  body[data-movil] .rf-ir:disabled{opacity:.4;cursor:default}
  body[data-movil] .rf-ir-ic{width:18px;height:18px;flex:none}
  body[data-movil] .rf-pos{flex:1;min-width:0;display:flex;flex-direction:column;align-items:flex-start;justify-content:center;padding-left:4px;font-size:11.5px;line-height:1.25;color:rgba(255,255,255,.82)}
  body[data-movil] .rf-con-nav .rf-pos{align-items:center;padding-left:0;text-align:center}
  body[data-movil] .rf-pos b{max-width:100%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font:600 15px var(--font-head);color:#fff}
  body[data-movil] .rf-pos-l{max-width:100%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
  body[data-movil] .rf-x{flex:none;width:44px;height:44px;display:grid;place-items:center;margin:0;padding:0;border:0;border-radius:12px;background:rgba(255,255,255,.16);color:#fff;cursor:pointer;-webkit-tap-highlight-color:transparent}
  body[data-movil] .rf-x-ic{width:20px;height:20px}
  body[data-movil] .rf-cuerpo{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;gap:8px;padding:12px 10px 14px;overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain;background:var(--surface)}
  body[data-movil] .rf-cuerpo>*{flex-shrink:0}
  body[data-movil] .rf-entra-der .rf-cuerpo{animation:rfEntraDer .22s ease-out}
  body[data-movil] .rf-entra-izq .rf-cuerpo{animation:rfEntraIzq .22s ease-out}
  body[data-movil] .rf-datos{margin:0;display:flex;flex-direction:column;gap:1px}
  body[data-movil] .rf-datos b{font-size:14.5px;overflow-wrap:anywhere}
  body[data-movil] .rf-datos span{font-size:12.5px;color:var(--muted)}
  body[data-movil] .rf-datos .cur-usd,body[data-movil] .rf-datos .cur-ves,body[data-movil] .rf-datos .cur-cop{padding:1px 5px;font-size:10.5px;margin-right:3px;vertical-align:0}
  body[data-movil] .rf-r3{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px}
  body[data-movil] .rf-k{display:flex;flex-direction:column;gap:1px;min-width:0;padding:7px 9px;border:1px solid var(--border);border-radius:var(--r-card);background:var(--surface);box-shadow:var(--shadow-md)}
  body[data-movil] .rf-k span{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font:600 10.5px var(--font-head);letter-spacing:.07em;text-transform:uppercase;color:var(--muted)}
  body[data-movil] .rf-k span i{font-style:normal;letter-spacing:.02em;opacity:.8}
  body[data-movil] .rf-k b{display:block;overflow:hidden;white-space:nowrap;font-size:17px;letter-spacing:-.01em}
  body[data-movil] .rf-kc b{color:var(--no)}
  body[data-movil] .rf-pcs{display:flex;flex-wrap:wrap;gap:6px}
  body[data-movil] .rf-u{display:inline-flex;align-items:center;padding:1px 7px;border-radius:999px;font-size:11.5px;font-weight:700;white-space:nowrap}
  body[data-movil] .rf-u:not([class*="util-"]){background:var(--surface-2);color:var(--muted)}
  body[data-movil] .rf-pcs .rf-u{padding:3px 9px;font-size:12px}
  body[data-movil] .rf-u.rf-mg{background:transparent;border:1px solid var(--border)}
  body[data-movil] .rf-u.rf-anul{background:var(--ex-min-bg);color:var(--ex-min-tx)}
  body[data-movil] .rf-prods{display:flex;flex-direction:column;border:1px solid var(--border);border-radius:var(--r-card);background:var(--surface);overflow:hidden}
  body[data-movil] .rf-prod+.rf-prod{border-top:1px solid var(--border)}
  body[data-movil] .rf-prod-cab{display:flex;align-items:center;gap:8px;width:100%;min-height:50px;margin:0;padding:6px 6px 6px 10px;border:0;border-radius:0;background:transparent;color:var(--text);font:inherit;text-align:left;cursor:pointer;-webkit-tap-highlight-color:transparent}
  body[data-movil] .rf-prod.abierta>.rf-prod-cab{background:var(--row-hover)}
  body[data-movil] .rf-w{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}
  body[data-movil] .rf-p1{display:flex;align-items:flex-start;gap:10px}
  body[data-movil] .rf-nom{flex:1;min-width:0;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;overflow-wrap:anywhere;font-size:13px;font-weight:600;line-height:17px}
  body[data-movil] .rf-v{flex:none;font-size:15px;font-weight:700;white-space:nowrap}
  body[data-movil] .rf-p2{display:flex;align-items:center;gap:8px;font-size:12px}
  body[data-movil] .rf-cant{flex:1;min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;color:var(--muted)}
  body[data-movil] .rf-cant em,body[data-movil] .rf-q em{font-style:normal;font-weight:700;color:var(--violet)}
  body[data-movil] .rf-prods .tv-suf{font-size:10px;line-height:13px;padding:0 3px;border-radius:5px;vertical-align:0}
  body[data-movil] .rf-desc{color:var(--accent-text);font-weight:600}
  body[data-movil] .rf-dev{flex:none;padding:0 5px;border-radius:5px;background:var(--ex-min-bg);color:var(--ex-min-tx);font-size:10.5px;font-weight:700;line-height:16px}
  body[data-movil] .rf-gan{flex:none;display:inline-flex;align-items:center;gap:5px;font-weight:600;color:var(--ok);white-space:nowrap}
  body[data-movil] .rf-gan .rf-u{font-size:11px}
  body[data-movil] .rf-chev{flex:none;width:18px;height:18px;color:var(--muted);transition:transform .15s ease}
  body[data-movil] .rf-prod.abierta .rf-chev{transform:rotate(180deg)}
  body[data-movil] .rf-panel{display:none;flex-direction:column;gap:6px;padding:7px 10px 10px;border-top:1px dashed var(--border);background:var(--surface-2)}
  body[data-movil] .rf-prod.abierta>.rf-panel{display:flex}
  body[data-movil] .rf-ln{display:flex;align-items:center;gap:8px;font-size:12.5px}
  body[data-movil] .rf-ln b{white-space:nowrap}
  body[data-movil] .rf-tv{flex:none;min-width:46px;display:inline-flex;flex-direction:column;align-items:center;gap:1px;padding:2px 4px;border-radius:6px;background:rgba(51,69,110,.14);color:var(--violet);font-size:11px;font-weight:700;line-height:1.2}
  [data-theme="oscuro"] body[data-movil] .rf-tv{background:rgba(150,170,220,.16)}
  body[data-movil] .rf-q{flex:1;min-width:0;color:var(--muted)}
  body[data-movil] .rf-cg{display:flex;flex-wrap:wrap;align-items:center;gap:4px 12px;font-size:12.5px}
  body[data-movil] .rf-c{font-weight:600;color:var(--no)}
  body[data-movil] .rf-g{font-weight:600;color:var(--ok)}
  body[data-movil] .rf-barra{display:flex;height:6px;border-radius:3px;overflow:hidden;background:rgba(51,69,110,.12)}
  body[data-movil] .rf-barra i{display:block;height:100%}
  body[data-movil] .rf-bc{background:rgba(220,53,69,.88)}
  body[data-movil] .rf-bg{background:rgba(38,166,91,.92)}
  body[data-movil] .rf-dato,body[data-movil] .rf-vacio{margin:0;font-size:12.5px;color:var(--muted)}
  body[data-movil] .rf-vacio{padding:14px}
  body[data-movil] .rf-pie{flex:none;display:flex;align-items:center;gap:8px;padding:8px 10px calc(10px + env(safe-area-inset-bottom, 0px));border-top:1px solid var(--border);background:var(--surface-2);box-shadow:0 -6px 16px rgba(16,24,40,.14);font-variant-numeric:tabular-nums}
  [data-theme="oscuro"] body[data-movil] .rf-pie{box-shadow:0 -6px 16px rgba(0,0,0,.4)}
  body[data-movil] .rf-tot{flex:1;min-width:0;display:flex;flex-direction:column;font-size:11px;line-height:1.25;color:var(--muted)}
  body[data-movil] .rf-tot>span{display:flex;align-items:baseline;gap:5px;min-width:0}
  body[data-movil] .rf-tot b{display:block;min-width:0;overflow:hidden;white-space:nowrap;font-size:14.5px}
  body[data-movil] .rf-abrir,body[data-movil] .rf-cerrar{flex:none;display:inline-flex;align-items:center;justify-content:center;gap:6px;height:42px;margin:0;padding:0 12px;font-size:14px;white-space:nowrap;text-decoration:none}
  body[data-movil] .rf-abrir .ic16,body[data-movil] .rf-abrir svg{width:16px;height:16px;flex:none}
}
@media (max-width:374px){body[data-movil] .rf-abrir svg{display:none}body[data-movil] .rf-abrir,body[data-movil] .rf-cerrar{padding:0 10px}}
@keyframes rfEntraDer{from{opacity:.35;transform:translateX(14px)}to{opacity:1;transform:none}}
@keyframes rfEntraIzq{from{opacity:.35;transform:translateX(-14px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){body[data-movil] .rf-chev{transition:none}body[data-movil] .rf-entra-der .rf-cuerpo,body[data-movil] .rf-entra-izq .rf-cuerpo,body[data-movil] .rep-tl-fila.vivo-fila,body[data-movil] .rep-tl-lista.rep-tl-late{animation:none}}

/* ─── GASTOS (10-sep-2026): detalle del gasto, formulario del modal y ajustes de teléfono ─── */
.gasto-det th{width:150px;text-align:left;color:var(--muted);font-weight:600;font-size:12.5px;vertical-align:top;background:transparent;text-transform:none;letter-spacing:0}
.gasto-det td,.gasto-det th{padding:6px 12px;border-top:1px solid var(--border)}
.gasto-form .fn-row .fld.grow{flex:1 1 240px}
#gastos-list .row-anulada td{opacity:.6}
@media (max-width:720px){
  .gasto-form .fn-row .fld{flex:1 1 45%;min-width:0}
  .gasto-form .fn-row .fld.grow{flex:1 1 100%}
  .gasto-form .fn-row .btn-primary{flex:1 1 100%;justify-content:center}
  #gastos-toolbar select.inp{max-width:48%}
  #gdet-dialog .vi-bar{flex-wrap:wrap;gap:6px}
  .gasto-form .fn-row .inp,.gasto-form .fn-row select.inp{height:40px}   /* con letra de 16 px los 34 px cortaban el texto de los selectores */
}

/* ─── COMPRAS 0.109 (10-sep): modo Catálogo, panel de imagen, interruptor Lote/Venc./I.V.A. y stepper del detalle ─── */
.c-sec-tit{display:flex;align-items:center;gap:12px}
.c-sec-tit>span:first-child{flex:none}
/* Interruptor (switch) — reemplaza al botón «Lote / Venc. / I.V.A.» */
.sw{display:inline-flex;align-items:center;gap:8px;cursor:pointer;font-size:12px;font-weight:600;color:var(--muted);margin-left:14px;text-transform:none;letter-spacing:0;position:relative;user-select:none;font-family:var(--font-ui)}
.sw input{position:absolute;opacity:0;width:0;height:0;margin:0}
.sw-track{width:36px;height:20px;border-radius:20px;background:var(--border-hover);position:relative;transition:background .15s ease;flex:none;box-shadow:inset 0 1px 2px rgba(0,0,0,.18)}
.sw-knob{position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;background:#fff;transition:left .15s ease;box-shadow:0 1px 2px rgba(0,0,0,.3)}
.sw input:checked+.sw-track{background:var(--accent)}
.sw input:checked+.sw-track .sw-knob{left:18px}
.sw input:focus-visible+.sw-track{box-shadow:var(--ring)}
.sw input:checked~.sw-lbl{color:var(--text)}
.sw:hover .sw-lbl{color:var(--text)}
/* Panel de imagen: el rótulo «Sin imagen» se esconde con foto (la regla de la Caja apuntaba a #v-img-ph) */
.v-img-box.con-img .img-ph{display:none}
/* 11-sep, su lienzo de Compras (x 1146 · y 228 a 1366×768): el cuadro baja 14 px y entra 62 px desde el borde derecho
   (19 px bajo el borde de la sección y 82 px del derecho); solo en Compras, la Caja conserva el suyo. */
/* 0.159 (él: «el texto "Utilidad contra el costo…" está ligeramente sobrepuesto encima»): el −13 px de arriba subía el
   cuadro a la franja del título, donde desde el 18-sep vive en posición absoluta la leyenda .c-util-aviso (top 8 px, hasta
   20 px del borde derecho): a 1366×768 su texto (y 217–233) pisaba 6 px el tope del cuadro (y 227) en x 1146–1284. Sin el
   margen el cuadro arranca a la par de las celdas (y 240): 7 px bajo el texto, mismo x, y el alto de la sección no cambia
   (la fila de captura, 184 px, es más alta que el cuadro). */
#c-img-box{margin-top:0;margin-right:62px}
/* Modo Catálogo del modal de Compras: misma rejilla que la Caja (catálogo izq · detalle + pie der) */
.venta-modal.modo-cat #c-row-top{grid-column:1/-1}
.venta-modal.modo-cat #c-sec-agregar{grid-column:1;grid-row:2;display:flex;flex-direction:column;min-height:0;border-bottom:0;border-right:1px solid var(--border);padding-bottom:0}
.venta-modal.modo-cat #c-sec-agregar .v-add-wrap{display:none}
/* El detalle de Compras es Producto · Cant. · Costo u. · I.V.A. % · Subtotal · papelera: en la columna angosta se esconde
   la tasa (4ª), no la cantidad ni el subtotal (la regla genérica de la Caja esconde la 2ª y la 5ª). */
#compra-dialog.modo-cat .vm-detalle .grid th:nth-child(2),#compra-dialog.modo-cat .vm-detalle .grid td:nth-child(2),
#compra-dialog.modo-cat .vm-detalle .grid th:nth-child(5),#compra-dialog.modo-cat .vm-detalle .grid td:nth-child(5){display:table-cell}
#compra-dialog.modo-cat .vm-detalle .grid th:nth-child(4),#compra-dialog.modo-cat .vm-detalle .grid td:nth-child(4){display:none}
#compra-dialog .cat-precio{font-size:11.5px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#compra-dialog .cat-precio b{font-size:13px;color:var(--text)}
#compra-dialog .cat-costo .cat-pct{width:82px;text-align:right}
@media (max-height:860px){ .venta-modal.maximized.modo-cat #c-sec-agregar{padding-bottom:0} }

/* Compras 0.110: Lote · Vence · I.V.A. % por tarjeta del Catálogo, solo con el interruptor encendido */
#compra-dialog:not(.avz) .cat-avz{display:none}
#compra-dialog .cat-avz>span{flex:none;width:46px}
#compra-dialog .cat-avz .cat-pct{flex:1;min-width:0;width:auto;text-align:left}
#compra-dialog .cat-avz .cc-vence{font-size:11px;padding:0 2px}
#compra-dialog .cat-avz .cc-iva{flex:none;width:54px;text-align:right}

/* ─── Mi clave (0.111, 10-sep): botón en el membrete y página ─── */
.appbar .btn-clave{display:inline-flex;align-items:center;gap:6px;text-decoration:none}
.mi-clave{max-width:480px;margin:8px auto 0}
.mi-clave .cfg-card{display:flex;flex-direction:column;gap:12px}
.mi-clave .cfg-ok{margin:0}
.mi-clave-form{display:flex;flex-direction:column;gap:12px}
.mi-clave-btns{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin:4px 0 0}
.mi-clave a.btn-ghost,.mi-clave a.btn-primary{text-decoration:none;display:inline-flex;align-items:center;gap:6px}
.usu-regla{margin:0 0 10px;font-size:13px}
@media (max-width:720px){ .appbar .btn-clave .lbl-clave{display:none} }

/* ── Leer factura con Exynol AI (Claude Vision, 16-sep-2026): pantalla de revisión en +Nueva Compra ── */
.cf-modal{width:min(1180px,96vw)}
.cf-body{padding:14px 20px;overflow:auto}
.cf-cab .fn-row{align-items:flex-start}
.cf-leido{display:block;font-size:12px;margin-top:3px}
.cf-cuadre{margin:12px 0;padding:10px 14px;border-radius:10px;font-size:14px;border:1px solid var(--border);background:var(--surface)}
.cf-cuadre.ok{border-color:#2e9e5b;background:rgba(46,158,91,.12)}
.cf-cuadre.no{border-color:#c0392b;background:rgba(192,57,43,.12)}
.cf-cuadre.sin{border-color:#c9a227;background:rgba(201,162,39,.12)}
.cf-tabla td{vertical-align:top}
.cf-tabla .inp{padding:6px 8px;min-height:0}
.cf-tabla .num .inp{max-width:110px;text-align:right}
.cf-desc{font-weight:600}
.cf-cod{font-size:11.5px;font-family:var(--font-mono,monospace)}
.cf-aviso{font-size:11.5px;color:#c0392b;margin-top:2px}
.cf-est{display:inline-block;font-size:11px;font-weight:700;padding:2px 8px;border-radius:12px;white-space:nowrap}
.cf-est.ok{background:#c6efce;color:#006100}
.cf-est.med{background:#dbeafe;color:#1e3a8a}
.cf-est.dud{background:#fff9b2;color:#9c8000}
.cf-est.no{background:#ffc7ce;color:#9c0006}
/* «Leer factura» v2 (30-sep-2026): renglón NUEVO (no está en el catálogo) con su botón «Crear producto» y lo que falta. */
.cf-est.nuevo{background:#ffe1c2;color:#8a3b00}
.cf-nuevo{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:4px}
.cf-nuevo .cf-crear{font-size:12px;padding:3px 10px;min-height:0;height:28px;display:inline-flex;align-items:center;gap:5px}
.cf-nuevo .cf-crear svg{width:14px;height:14px}
.cf-faltan,.cf-vence,.cf-nota{font-size:11.5px}
.cf-cands{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-top:4px;font-size:11.5px}
.cf-cand{font:inherit;font-size:11.5px;font-weight:600;padding:2px 9px;border-radius:12px;border:1px solid var(--border);background:var(--surface-2);color:var(--text);cursor:pointer;white-space:nowrap}
.cf-cand:hover{border-color:var(--accent);color:var(--accent-text)}
.cf-aprendido{font-size:10.5px;font-weight:700;color:#006100;background:#c6efce;padding:0 6px;border-radius:9px;margin-left:4px;font-family:var(--font-ui)}
.prod-desde-factura{margin:0 0 8px;font-size:13px;line-height:1.45}
.cf-prod{position:relative;min-width:260px}
.cf-prod .cf-q{width:100%}
.cf-lista{position:absolute;left:0;right:0;top:100%;z-index:30;background:var(--surface);border:1px solid var(--border);border-radius:8px;box-shadow:0 10px 30px rgba(0,0,0,.25);max-height:240px;overflow:auto}
.cf-it{padding:6px 10px;cursor:pointer;display:flex;flex-direction:column;gap:1px;font-size:13px}
.cf-it:hover{background:color-mix(in srgb,var(--accent) 18%,transparent)}
.cf-btns{margin-top:12px;justify-content:flex-end}
.c-ia-btn{margin-left:10px}
[data-theme="oscuro"] .cf-est.ok{color:#c6efce;background:rgba(0,97,0,.35)}
[data-theme="oscuro"] .cf-est.med{color:#dbeafe;background:rgba(30,58,138,.45)}
[data-theme="oscuro"] .cf-est.dud{color:#ffe98a;background:rgba(156,128,0,.24)}
[data-theme="oscuro"] .cf-est.no{color:#ffb3ba;background:rgba(156,0,6,.30)}
[data-theme="oscuro"] .cf-est.nuevo{color:#ffd0a8;background:rgba(138,59,0,.38)}
[data-theme="oscuro"] .cf-aprendido{color:#c6efce;background:rgba(0,97,0,.35)}
/* [hidden] dentro de .vm-btns (display:flex) perdía contra el display del botón: se fuerza (la trampa del 07-sep). */
.cf-btns [hidden]{display:none !important}
/* Botón «Leer factura» (16-sep, pedido de Alejandro: que se DESTAQUE con el icono de Exynol AI). El SVG del
   asistente no trae tamaño (es el del launcher, que lo dimensiona .tile): aquí se le da uno y el color de la marca. */
/* Vive en la cabecera OSCURA del modal (navy en los dos temas): pastilla oscura con borde luminoso, el
   orbital de Exynol AI en blanco + naranja de la marca, y el rótulo en dos renglones (Leer factura · Exynol AI). */
.c-ia-btn{display:inline-flex;align-items:center;gap:8px;padding:5px 14px 5px 10px;color:#fff;font-weight:700;border:1px solid rgba(255,255,255,.35);border-radius:999px;background:linear-gradient(135deg,#2b3d72 0%,#3f5aa6 55%,#5a3f8f 100%);box-shadow:0 0 0 1px rgba(255,255,255,.06) inset,0 4px 14px rgba(0,0,0,.35);text-shadow:0 1px 1px rgba(0,0,0,.35)}
.c-ia-btn:hover{background:linear-gradient(135deg,#34489a 0%,#4d6bc4 55%,#6d4bb0 100%);border-color:rgba(255,255,255,.6);box-shadow:0 0 0 1px rgba(255,255,255,.1) inset,0 6px 18px rgba(0,0,0,.4)}
.c-ia-btn:active{transform:translateY(1px)}
.c-ia-btn svg{width:22px;height:22px;overflow:visible;flex:none;filter:drop-shadow(0 0 4px rgba(255,255,255,.35))}
.c-ia-btn .ic-b{fill:rgba(255,255,255,.9)}
.c-ia-btn .ic-a{fill:var(--accent)}
.c-ia-btn .txt{display:flex;flex-direction:column;align-items:flex-start;line-height:1.05}
.c-ia-btn small{font-size:9.5px;font-weight:600;letter-spacing:.09em;text-transform:uppercase;opacity:.8;margin-top:2px}
.cf-modal .vm-head h2 svg{width:24px;height:24px;margin-right:8px;overflow:visible;flex:none}
.cf-modal .vm-head h2 .ic-b{fill:rgba(255,255,255,.85)}
.cf-modal .vm-head h2 .ic-a{fill:var(--accent)}

/* ─── COMPRAS · celdas de utilidad y precio de venta (17-sep-2026, lienzo «Compras · Utilidad y precios», B v2) ───
   Bloques «Costo» y «Precio de venta» en la fila de captura: rótulo · «Actual …» · campo + chip ▲/▼ (= 0 % si no
   cambia). Detalle con encabezados cortos, producto a dos líneas y columnas «Precio De Venta» / «Util.%». En modo
   Catálogo las tarjetas llevan filas Costo / P.V.P / segundo precio y el detalle esconde esas columnas. */
#compra-dialog .c-blk{display:flex;gap:6px;align-items:flex-end;padding:9px 8px 9px;border:1px solid var(--border);border-radius:8px;background:var(--surface-2);position:relative}
#compra-dialog .c-blk-t{position:absolute;left:10px;top:-8px;font-size:10.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);background:var(--surface-2);padding:0 4px;line-height:1.2}
#compra-dialog .c-blk .fld{font-size:12px}
#compra-dialog .c-blk .inp[readonly]{background:var(--surface)}
#compra-dialog .c-cel{gap:2px}
#compra-dialog .c-cel-l{font-size:12.5px;font-weight:600;color:var(--text);line-height:1.2;white-space:nowrap;display:inline-flex;align-items:center;gap:4px}
#compra-dialog .c-cel-l .cat-tar-lbl{margin:0;vertical-align:0}
#compra-dialog .c-cel-act{font-size:11px;font-weight:500;color:var(--muted);line-height:1.35;white-space:nowrap}
#compra-dialog .c-cel-act b{font-weight:600;color:var(--text);font-variant-numeric:tabular-nums}
#compra-dialog .c-cel-act b.ic-util{font-size:10.5px;padding:0 5px;border-radius:5px;line-height:1.5}
#compra-dialog .c-cel-act b.ic-util:not([class*="util-"]){background:transparent;padding:0}
#compra-dialog .v-add-linea .inp.c-precio-inp{width:84px;font-weight:600}
#compra-dialog .v-add-linea .inp.c-util-inp{width:76px;font-weight:700;text-align:right}
#compra-dialog .c-util-wrap{display:flex;align-items:center;gap:4px}
#compra-dialog .v-add-linea .btn-primary{padding-left:10px;padding-right:10px}
.c-delta{display:inline-block;font-size:11.5px;font-weight:700;padding:1px 6px;border-radius:10px;white-space:nowrap;line-height:1.45;font-variant-numeric:tabular-nums}
.c-delta.up{color:var(--ok);background:rgba(38,166,91,.14)}
.c-delta.down{color:var(--no);background:rgba(220,53,69,.12)}
.c-delta.eq{color:var(--info);background:rgba(124,181,236,.16)}
.c-delta-s{font-size:10.5px;padding:0 5px}
.c-delta[hidden]{display:none}
/* detalle */
#compra-dialog .vm-detalle .grid th{text-transform:none;letter-spacing:.01em}
#compra-dialog .vm-detalle .grid td,#compra-dialog .vm-detalle .grid th{padding-left:6px;padding-right:6px}
#compra-dialog:not(.modo-cat) .vm-detalle .grid td.c-prod{max-width:176px}
#compra-dialog .vm-detalle .grid td.c-prod .c-prod-n{font-size:12.5px;line-height:1.25;white-space:normal;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
#compra-dialog .vm-detalle .grid td.c-pv,#compra-dialog .vm-detalle .grid td.c-ut{white-space:nowrap;line-height:1.7;font-size:12.5px;font-variant-numeric:tabular-nums;vertical-align:middle}
#compra-dialog .vm-detalle .grid td.c-pv b{font-weight:600;font-size:13px}
#compra-dialog .vm-detalle .grid .c-pv-r{display:inline-flex;align-items:center;gap:4px}
#compra-dialog .vm-detalle .grid .c-pv-r .cat-tar-lbl{margin:0;min-width:38px;text-align:center}
#compra-dialog .vm-detalle .grid .c-antes{font-size:11px;color:var(--muted);font-weight:400;text-decoration:line-through}
#compra-dialog .vm-detalle .grid td.c-ut .ic-util{display:inline-block;min-width:52px;text-align:right;margin:0;font-size:10.5px;padding:0 5px;box-sizing:border-box}
#compra-dialog .vm-detalle .grid td.c-ut .c-delta-s{display:inline-block;min-width:52px;text-align:left}
/* 0.159: el precio de la tanda se corrige en su renglón (casilla como la del costo). Cada precio es un renglón de 22 px en
   «Precio De Venta» y en «Util.%», así la utilidad queda a la par de su precio aunque falte alguno. */
#compra-dialog .vm-detalle .grid td.c-pv .c-pv-r,#compra-dialog .vm-detalle .grid td.c-ut .c-pv-r{display:flex;height:22px}
#compra-dialog .vm-detalle .grid .det-pv{width:auto;field-sizing:content;min-width:52px;max-width:96px;height:20px;text-align:right;border:1px solid var(--border);border-radius:6px;background:var(--surface-2);padding:0 4px;font-weight:600}   /* crece con la cifra: la columna gana lo justo (la tabla va al límite a 1366) */
#compra-dialog .vm-detalle .grid .det-pv::placeholder{color:var(--muted);font-weight:400}
#compra-dialog .vm-detalle .grid thead th{white-space:nowrap}   /* con la tabla al límite, «Precio De Venta» se partía en dos y el encabezado crecía 16 px */
#compra-dialog:not(.modo-cat) .c-pv-mini{display:none}
#compra-dialog.modo-cat .vm-detalle .grid th:nth-child(6),#compra-dialog.modo-cat .vm-detalle .grid td:nth-child(6),
#compra-dialog.modo-cat .vm-detalle .grid th:nth-child(7),#compra-dialog.modo-cat .vm-detalle .grid td:nth-child(7){display:none}
#compra-dialog .c-pv-mini{font-size:10.5px;color:var(--muted);line-height:1.6;white-space:nowrap;margin-top:2px}
#compra-dialog .c-pv-mini b{color:var(--text);font-weight:600}
#compra-dialog .c-pv-mini .ic-util{font-size:10px;padding:0 4px;margin:0}
#compra-dialog .c-pv-mini .c-delta-s{font-size:10px;padding:0 4px}
#compra-dialog .c-pv-mini .cat-tar-lbl{margin:0 2px 0 0}
/* 0.159 · FILA DE CAPTURA (pedido de Alejandro): los bloques «Costo» y «Precio de venta» se ensanchan hasta el final de la
   fila y detrás van el SubTotal y los botones, LADO A LADO (su retoque). Base 0 con pesos ≈ el ancho de cada
   bloque con todos sus chips a la vista: el sobrante se reparte así y los chips aparecen en el hueco que ya tenían, sin
   mover nada; si un bloque necesita más, su contenido manda (min-width) y la fila solo baja de renglón si de verdad no
   cabe. Las casillas pasan de 82 px con 10 px de relleno (62 para la cifra) a 84 con 6 (72): cabe «999.999,99». Para que
   los botones lado a lado quepan a 1366 con los cinco chips anchos (medido: holgura 3 px): casillas 86 → 84, utilidad
   76 → 74, SubTotal 100 → 92, chips 11 → 10,5 px con relleno 4, bloques con 6 px de relleno y 5 de separación, botones con 6. */
@media (min-width:721px){
  #compra-dialog .v-add-linea > .c-blk{flex:278 1 0;min-width:max-content}
  #compra-dialog .v-add-linea > #c-blk-precio{flex-grow:640}
  #compra-dialog .v-add-linea > #c-blk-precio:not(:has(#l-pvm)){flex-grow:320}
  #compra-dialog .c-blk > .c-cel{flex:160 1 0;min-width:max-content}
  #compra-dialog .c-blk > .c-cel:has(.c-util-inp){flex-grow:150}
  #compra-dialog .c-blk > .c-cel:has(#l-costo-prom){flex-grow:86}
}
#compra-dialog .v-add-linea .c-blk .inp{width:84px;padding-left:6px;padding-right:6px}
#compra-dialog .v-add-linea .c-blk .inp.c-util-inp{width:74px}   /* 0.159: 62 px para la cifra, cabe «-100,00 %» */
#compra-dialog .v-add-linea #c-subtotal-pre{width:92px;padding-left:6px;padding-right:6px}
#compra-dialog .v-add-linea .c-util-wrap > .c-delta{font-size:10.5px;padding:1px 4px}   /* 0.159: −10 px por chip (11,5 → 10,5 px y relleno 6 → 4): con los botones lado a lado y los cinco chips a la vista la fila sigue en un renglón a 1366 */
#compra-dialog .v-add-linea .c-util-wrap{gap:3px}
#compra-dialog .v-add-linea > .c-blk{padding-left:6px;padding-right:6px;gap:5px}
/* Retoque de la 0.159 (él: «lado a lado»): + Agregar y Limpiar uno al lado del otro, con menos relleno (7 px). */
#compra-dialog .c-acc{display:flex;flex-direction:row;align-items:flex-end;gap:4px;flex:none}
#compra-dialog .c-sub-fld,#compra-dialog .c-acc{margin-bottom:10px}   /* a ras de las casillas de los bloques (relleno 9 + borde 1), no del borde del marco */
#compra-dialog .v-add-linea .c-acc > button{justify-content:center;padding-left:6px;padding-right:6px}
/* «= última» junto a «Factura» (copia el costo de la última compra: ultimoCostoCompra) y «= actual» junto al P.V.P y al
   segundo precio (vuelve al precio de la ficha: volverPrecioCompra). */
#compra-dialog .c-cel-l label{cursor:pointer}
#compra-dialog .c-mismo{font:inherit;font-size:10.5px;font-weight:700;line-height:1.35;padding:0 6px;border:1px solid var(--accent);border-radius:9px;background:transparent;color:var(--accent);cursor:pointer;white-space:nowrap}
#compra-dialog .c-mismo:hover:not(:disabled),#compra-dialog .c-mismo:focus-visible{background:var(--accent);color:#fff;outline:none}
#compra-dialog .c-mismo:disabled{border-color:var(--border);color:var(--muted);opacity:.6;cursor:default}
#compra-dialog .v-add-linea #l-costo::placeholder{font-size:11px;font-weight:400}   /* «últ. 125.000» se lee entero en los 72 px de la casilla */
/* catálogo: filas Costo / P.V.P / segundo precio en la tarjeta */
#compra-dialog .cat-desc.c-cd{align-items:center;gap:5px;margin-top:3px}
#compra-dialog .c-cd-l{display:flex;flex-direction:column;line-height:1.15;min-width:58px;flex:none}
#compra-dialog .c-cd-l i{font-style:normal;font-size:10px;color:var(--muted);white-space:nowrap}
#compra-dialog .c-cd-l .cat-tar-lbl{margin:0;align-self:flex-start}
#compra-dialog .cat-desc .cat-pct.c-cd-inp{width:64px;flex:none;text-align:right}
#compra-dialog .cat-desc .ic-util{font-size:10.5px;padding:0 4px;margin:0;flex:none}
#compra-dialog .cat-desc .ic-util[hidden]{display:none}
#compra-dialog .v-cat-grid.tam-s .c-cd-l i{display:none}
#compra-dialog .v-add-linea #l-costo{width:84px;font-weight:600}   /* el costo de la factura, del ancho de los precios (la clase la asertan los tests) */

/* Lista de Ventas · columna «Caja» (18-sep-2026): el mostrador o la caja móvil que registró la venta, con su icono. */
#ventas-list .td-caja{white-space:nowrap;max-width:240px;overflow:hidden;text-overflow:ellipsis}
/* Columna «Hora» (22-sep-2026): hh:mm a. m./p. m. como Auditoría; cifras de ancho fijo para que las filas se alineen. */
#ventas-list .td-hora{white-space:nowrap;font-variant-numeric:tabular-nums}
/* LA SERIE Y EL NÚMERO EN UNA SOLA LÍNEA (12-oct-2026, 0.163, Alejandro: «priorizar que la serie y el número de factura se vean
   siempre en una sola línea»; views/ventas.js → filaVenta). El número no se parte nunca («A-12184», no «A-» / «12184»): la columna lo
   toma como su ancho mínimo. En el teléfono, además, el cliente va en UN renglón recortado con «…» (el nombre entero en su title),
   así no le roba ancho al número ni alarga la fila. */
#ventas-list .v-num{white-space:nowrap}
body[data-movil] #ventas-list .v-cli{display:block;max-width:150px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
/* LOS TRES SELECTORES DE VENTAS EN UN RENGLÓN (12-oct-2026, 0.163, Alejandro: «reajustar las combo box de "Vendedores" y "Cajas" para
   que estén en una sola línea al lado de la selección de monedas»). En la PC .tb-selects no existe para el diseño (display:contents):
   la barra queda igual. En el teléfono es una fila que no se envuelve y reparte su ancho entre moneda, vendedor y caja; cada selector
   se achica (min-width:0) y su texto se recorta con «…» antes que partir la fila en dos. */
.tb-selects{display:contents}
body[data-movil] .tb-filtros .tb-selects{display:flex;flex:1 1 100%;flex-wrap:nowrap;align-items:center;gap:6px;min-width:0}
body[data-movil] .tb-selects>.sel-mon,body[data-movil] .tb-selects>.sel-vend{display:flex;align-items:center;flex:1 1 0;min-width:0}
body[data-movil] .tb-selects>.sel-mon .coin-slot{flex:none}
body[data-movil] .tb-selects select{flex:1 1 auto;width:100%;min-width:0;max-width:none;text-overflow:ellipsis}
/* «MONEDAS» · «VENDEDORES» · «CAJAS» ENTEROS (13-oct-2026, 0.163, Alejandro: «Sí» a «Monedas» en vez de «Todas las monedas»;
   views/ventas.js pone los rótulos cortos en la sesión del teléfono). A 360 px, a partes iguales y con los rellenos de la PC
   (10 + 30 px por la flecha), ni «Monedas» cabía («Mo…»). En el teléfono los selectores llevan rellenos más justos (8 + 22, la
   flecha a 7 px del borde) y, donde el navegador sabe medir un selector por su texto (field-sizing: Chromium, la PWA de Android),
   cada uno parte del ancho de lo que dice y el sobrante se reparte: «Vendedores» toma lo que le falta a costa de «Cajas». */
body[data-movil] .tb-selects select.inp{padding-left:8px;padding-right:22px;background-position:right 7px center}
@supports (field-sizing:content){
  body[data-movil] .tb-selects select.inp{field-sizing:content}
  body[data-movil] .tb-selects>.sel-mon,body[data-movil] .tb-selects>.sel-vend{flex:1 1 auto}
}
/* Y en la misma barra, Desde / Hasta (visto al medir la pieza anterior a 360 px): iban de lado con sus rótulos y anchos fijos
   (.tb-fechas flex:none, 140 px) y «Hasta» se salía por la derecha —la página entera se deslizaba de lado—. En el teléfono comparten
   un renglón, cada uno con su rótulo encima, y nunca son más anchos que la pantalla. */
body[data-movil] .tb-filtros .tb-fechas{flex:1 1 100%;min-width:0;gap:6px}
body[data-movil] .tb-filtros .tb-fecha{flex:1 1 0;min-width:0;flex-direction:column;align-items:stretch;gap:2px}
body[data-movil] .tb-filtros .tb-fecha .inp{width:100%;min-width:0}
#ventas-list .v-caja{display:inline-flex;align-items:center;gap:5px;vertical-align:middle}
#ventas-list .v-caja svg{width:14px;height:14px;margin:0;vertical-align:0;opacity:.65}
/* Barra de Ventas con los selectores Vendedor y Caja (18-sep): Desde/Hasta viajan JUNTOS (nunca uno en cada renglón), los selectores se acotan y el buscador cede ancho para que los filtros quepan en un renglón a 1366 px. */
.tb-fechas{display:inline-flex;align-items:center;gap:8px;flex:none}
.tb-filtros .sel-vend select{max-width:200px;text-overflow:ellipsis}
.tb-filtros:has(.sel-caja) .search-wrap{flex-basis:250px}

/* Editor de producto · recargo de unidad/fracción en tres lecturas (18-sep-2026): celda + lecturas al lado. */
.mk-fld{flex:0 1 auto}
.mk-wrap{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.mk-wrap>.inp{width:118px;flex:none}
.mk-lect{display:inline-flex;align-items:center;gap:6px;flex-wrap:wrap;font-size:12px;font-weight:400;text-transform:none;letter-spacing:0}
.mk-chip{display:inline-block;padding:2px 7px;border-radius:999px;border:1px solid var(--border);background:var(--surface-2);color:var(--text);font-variant-numeric:tabular-nums;white-space:nowrap}
.mk-chip.mk-precio{font-weight:600}
.mk-nota{color:var(--muted);white-space:nowrap}
.mk-nota.mk-mal{color:var(--no);white-space:normal}

/* Caja · REGLETA POR TIPO de las tarjetas del Catálogo y del zoom (04-oct-2026, pedido de Alejandro; hasta la 0.155 era un
   segmentado Ent. | Unit. | Fracc.): solo en las fichas que se venden por unidad. Una fila por tipo —abreviatura, sufijo de
   la ficha y su contador − n +— y debajo «Desc.» + «Agregar». La fila que el cajero toca es la ACTIVA (abreviatura en
   violeta): la tarjeta enseña su precio y su existencia. Si abreviatura y sufijo no caben en una línea junto al contador, el
   sufijo baja debajo de la abreviatura sin que la fila crezca (así va siempre en las tarjetas S y en el teléfono); uno muy
   largo se recorta con puntos (entero en el globo). */
.cat-reg{flex:1 1 100%;min-width:0;display:flex;flex-direction:column;gap:3px}
.cat-reg-f{display:flex;align-items:center;gap:4px;min-width:0;min-height:28px}
.cat-reg-t{flex:1 1 auto;min-width:0;display:flex;flex-wrap:wrap;align-items:center;gap:1px 4px}
.cat-reg .seg-tv-abr{flex:none;min-width:42px;border:0;border-radius:5px;background:transparent;color:var(--muted);font-family:var(--font-ui);font-size:11.5px;font-weight:700;line-height:1.15;padding:1px 3px;text-align:left;white-space:nowrap;cursor:pointer}
.cat-reg .seg-tv-abr:hover{background:var(--row-hover);color:var(--text)}
.cat-reg-f.on .seg-tv-abr{background:var(--violet);color:#fff}
[data-theme="oscuro"] .cat-reg-f.on .seg-tv-abr{color:#14161b}
.cat-reg .tv-suf{flex:0 1 auto;min-width:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;font-size:10px;padding:0 4px;line-height:1.3;vertical-align:0}
.cat-reg .cat-step{height:28px}
.cat-reg .cat-step button{width:22px;height:28px}
.cat-reg .cat-step .cat-qty{width:28px;height:26px;font-size:13.5px}
/* Contador en 0: libre = ese tipo no se agrega (número atenuado); ya en la venta = el lugar de un tipo sin línea (punteado, como en el Detalle). */
.cat-reg .cat-step.cero .cat-qty{color:var(--muted);font-weight:500}
.cat-reg .cat-step.en.cero{background:var(--surface-2);border-color:var(--border);border-style:dashed;box-shadow:none}
.cat-reg .cat-step.en.cero button{color:var(--text)}
.cat-reg .cat-step.en.cero button:hover{background:var(--row-hover)}
/* Debajo de la regleta: «Desc. [0] %» a la izquierda y «Agregar» (o «En la venta») a la derecha, en la misma línea; si no caben, bajan y ocupan el ancho. */
.cat-reg ~ .cat-desc{width:auto;flex:0 1 auto;min-width:0}
.cat-accion .cat-reg ~ .cat-desc .cat-pct{width:40px}
.cat-accion .cat-reg ~ .cat-add{flex:1 0 auto;height:28px}
.cat-accion .cat-reg ~ .cat-en{flex:1 0 auto;display:inline-flex;align-items:center;justify-content:center;gap:3px;min-height:28px}
.cat-en-ic{display:none;font-size:14px;line-height:1}
/* Tarjetas S: el sufijo siempre debajo de la abreviatura, contador más justo y «En la venta» solo con ✓ (como «Agregar», solo +). */
.v-cat-grid.tam-s .cat-reg-f{min-height:26px}
.v-cat-grid.tam-s .cat-reg-t{flex-direction:column;flex-wrap:nowrap;align-items:flex-start;gap:1px}
.v-cat-grid.tam-s .cat-reg .seg-tv-abr{font-size:10.5px;min-width:0;padding:0 3px}
.v-cat-grid.tam-s .cat-reg .tv-suf{font-size:9.5px;padding:0 3px}
.v-cat-grid.tam-s .cat-reg .cat-step{height:26px}
.v-cat-grid.tam-s .cat-reg .cat-step button{width:19px;height:26px;font-size:15px}
.v-cat-grid.tam-s .cat-reg .cat-step .cat-qty{width:26px;height:24px;font-size:13px}
.v-cat-grid.tam-s .cat-reg ~ .cat-desc{font-size:10.5px;gap:3px}
.v-cat-grid.tam-s .cat-reg ~ .cat-desc .cat-pct{width:36px}
.v-cat-grid.tam-s .cat-reg ~ .cat-add,.v-cat-grid.tam-s .cat-reg ~ .cat-en{height:26px;min-height:26px}
.v-cat-grid.tam-s .cat-en-txt{display:none}
.v-cat-grid.tam-s .cat-en-ic{display:inline}
/* «Desc.» DE LA TARJETA Y DEL ZOOM (0.158, ajuste 5): cortaba como la del Detalle —en % «100,00» (39 px de texto a 12 px en los
   36 útiles de 46) y en la regleta (40 px; 36 en S) ya «55,56»; en monto, desde «999,99»—. Mide lo que pide lo que lleva
   (field-sizing:content) desde su ancho de siempre (46; 40 en la regleta, 36 en S) hasta lo que pide «999.999,99» (74 px medidos;
   tope 78), y no se encoge en la fila. Solo en la Caja: Compras usa la misma clase con sus propios anchos. */
#venta-dialog .cat-desc .cat-pct,#cat-zoom .cat-desc .cat-pct{width:auto;field-sizing:content;min-width:46px;max-width:78px;flex:none}
#venta-dialog .cat-accion .cat-reg ~ .cat-desc .cat-pct{min-width:40px}
#venta-dialog .v-cat-grid.tam-s .cat-reg ~ .cat-desc .cat-pct{min-width:36px}
/* Zoom: filas de hasta 280 px (la regleta ocupa la línea entera; «Desc.» y «Agregar» van debajo) y contadores más cómodos. */
.cz-acciones .cat-reg{gap:5px}
.cz-acciones .cat-reg-f{max-width:280px;min-height:32px}
.cz-acciones .cat-reg .seg-tv-abr{font-size:13px;min-width:50px;padding:2px 6px}
.cz-acciones .cat-reg .tv-suf{font-size:12px;padding:0 6px;line-height:1.45}
.cz-acciones .cat-reg .cat-step{height:32px}
.cz-acciones .cat-reg .cat-step button{width:28px;height:32px}
.cz-acciones .cat-reg .cat-step .cat-qty{width:38px;height:30px;font-size:14px}
/* Teléfono (tarjeta M de ~169 px): el sufijo debajo de la abreviatura y contadores a la medida del dedo; «Agregar» baja a su línea, a todo el ancho. */
@media (max-width:720px){.cat-fila{flex-wrap:wrap;gap:2px 6px}.cat-accion .cat-reg-f{min-height:30px}.cat-accion .cat-reg-t{flex-direction:column;flex-wrap:nowrap;align-items:flex-start;gap:1px}.cat-accion .cat-reg .seg-tv-abr{min-width:0}.cat-accion .cat-reg .cat-step{height:30px}.cat-accion .cat-reg .cat-step button{width:26px;height:30px}.cat-accion .cat-reg .cat-step .cat-qty{width:30px;height:28px}.cat-accion .cat-reg ~ .cat-add,.cat-accion .cat-reg ~ .cat-en{height:32px;min-height:32px}}

/* Caja · la celda «Cantidad» con el TIPO dentro (03-oct-2026, alternativa A del lienzo «La fila de captura»): con un producto
   que se vende suelto, la celda es una sola caja «Unit. ▾ |*10TAB| 1» — prefijo (abre .v-tv-menu), lugar del sufijo y número.
   Los dos sufijos de la ficha comparten casilla (rejilla de una celda; solo se ve el del tipo elegido): la celda mide lo mismo
   con cualquier tipo y nada salta en la fila. Con un producto que solo se vende entero es el campo de 82 px de siempre. */
.v-cant{display:inline-flex;align-items:stretch}
.v-tv{display:inline-flex;align-items:stretch}
.v-cant.con-tv{border:1px solid var(--border);border-radius:var(--r-ctl);background:var(--surface-2);box-shadow:inset 0 1px 2px var(--inp-sombra)}
.v-cant.con-tv:focus-within{border-color:var(--accent);box-shadow:var(--ring)}
.v-add-linea .v-cant.con-tv #v-cantidad{width:60px;height:32px;border:0;background:transparent;box-shadow:none;padding-left:4px;padding-right:8px;border-radius:0 calc(var(--r-ctl) - 1px) calc(var(--r-ctl) - 1px) 0}
.v-tv-btn{min-width:60px;border:0;border-right:1px solid var(--border);border-radius:calc(var(--r-ctl) - 1px) 0 0 calc(var(--r-ctl) - 1px);padding:0 7px;display:inline-flex;align-items:center;justify-content:center;gap:4px;font-family:var(--font-ui);font-size:12px;font-weight:700;line-height:1;white-space:nowrap;color:var(--violet);background:rgba(51,69,110,.14);cursor:pointer}
.v-tv-btn i{font-style:normal;font-size:9px;opacity:.8}
.v-tv-btn:hover{background:rgba(51,69,110,.24)}
.v-tv-btn:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
[data-theme="oscuro"] .v-tv-btn{background:rgba(150,170,220,.16)}
[data-theme="oscuro"] .v-tv-btn:hover{background:rgba(150,170,220,.26)}
.v-tv-suf{display:inline-grid;align-items:center;justify-items:center;padding-left:6px}
.v-tv-suf[hidden]{display:none}
.v-tv-suf>.tv-suf{grid-area:1/1;font-size:11px;padding:0 5px;vertical-align:0;visibility:hidden}
.v-tv-suf>.tv-suf.on{visibility:visible}
.v-tv-suf>.tv-suf:empty{display:none}
.v-tv-menu{position:fixed;z-index:70;min-width:230px;display:flex;flex-direction:column;gap:1px;padding:4px;background:var(--surface);border:1px solid var(--border);border-radius:9px;box-shadow:var(--shadow-md);font-family:var(--font-ui)}
.v-tv-menu button{display:flex;align-items:baseline;gap:8px;border:0;border-radius:6px;background:transparent;color:var(--text);font:inherit;font-size:13px;text-align:left;padding:6px 9px;cursor:pointer;white-space:nowrap}
.v-tv-menu button b{min-width:44px;color:var(--violet)}
.v-tv-menu button small{margin-left:auto;padding-left:14px;color:var(--muted);font-size:12px;font-variant-numeric:tabular-nums}
.v-tv-menu button:hover,.v-tv-menu button:focus-visible{background:var(--row-hover);outline:none}
.v-tv-menu button.on{background:rgba(51,69,110,.16)}
[data-theme="oscuro"] .v-tv-menu button.on{background:rgba(150,170,220,.2)}
.v-tv-menu .tv-suf{font-size:11px;margin-left:4px}

/* Ajustes · «Sonidos y animaciones» (18-sep-2026): la fila de botones Probar. */
.son-probar{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin:4px 0 12px}

/* «Procesado» (19-sep-2026): confirmación central al guardar venta/cotización. Reusa .msgbox (mismo fondo oscuro). */
.msgbox-ok{text-align:center;min-width:360px;padding:26px 28px 22px}
.msgbox-ok .mok-ico{position:relative;width:72px;height:72px;margin:0 auto 12px}
.msgbox-ok .mok-ico svg{position:absolute;inset:0;width:100%;height:100%;display:block;overflow:visible}
.msgbox-ok .mok-fondo{position:absolute;inset:5px;border-radius:50%;background:color-mix(in srgb,var(--ok) 14%,transparent)}
.msgbox-ok .mok-giro{transform:rotate(-90deg)}   /* el aro arranca a las 12, como el punto */
.msgbox-ok .mok-aro{fill:none;stroke:var(--ok);stroke-width:3;stroke-linecap:round;stroke-dasharray:164;stroke-dashoffset:0}
.msgbox-ok .mok-v{fill:none;stroke:var(--ok);stroke-width:5;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:40;stroke-dashoffset:0}
.msgbox-ok .mok-orb{position:absolute;inset:0;opacity:0}   /* quieto = sin punto: el estado final es el de reposo */
.msgbox-ok .mok-punto{position:absolute;left:50%;top:0;width:10px;height:10px;margin-left:-5px;border-radius:50%;background:var(--accent);box-shadow:0 0 0 4px color-mix(in srgb,var(--accent) 26%,transparent)}
.msgbox-ok .mok-tit{margin:0;font-size:19px;font-weight:700;font-family:var(--font-display)}
.msgbox-ok .mok-det{margin:4px 0 0;color:var(--muted);font-size:14px;font-variant-numeric:tabular-nums}
.msgbox-ok .mok-preg{margin:16px 0 0;font-size:15px}
.msgbox-ok .vm-btns{justify-content:center;margin-top:16px}
.msgbox-ok .vm-btns .btn-primary,.msgbox-ok .vm-btns .btn-ghost{min-width:96px}
/* APERTURA DE CAJA al vender (22-sep-2026): el aviso de «+Nueva venta» (Abrir caja · Ver productos · Cancelar) con
   el fondo de apertura editable, y la franja del MODO VISUALIZACIÓN dentro de la Caja. */
.msgbox-caja{max-width:560px}
.msgbox-caja .caja-ap-tit{display:flex;align-items:center;gap:6px;margin:0 0 6px;font-size:18px;font-weight:700;font-family:var(--font-display)}
.msgbox-caja .caja-ap-tit .ic16{width:20px;height:20px;fill:var(--accent)}
.msgbox-caja .caja-ap-txt{margin:0 0 14px}
.msgbox-caja .caja-ap-fondo{margin:4px 0 0}
.msgbox-caja .caja-ap-fondo .inp{width:120px}
.msgbox-caja .caja-ap-nota{margin:6px 0 0;font-size:12px}
.msgbox-caja .caja-ap-err{margin:8px 0 0;color:var(--no);font-size:13px;font-weight:600}
.msgbox-caja .caja-ap-err:empty{display:none}
.msgbox-caja .vm-btns{margin-top:16px;flex-wrap:wrap}
.modo-vista{flex:none;display:flex;align-items:center;gap:10px;padding:8px 16px;background:color-mix(in srgb,var(--accent) 16%,var(--surface));border-bottom:2px solid var(--accent);font-size:13.5px}
.modo-vista[hidden]{display:none}
.modo-vista>span{flex:1}
.modo-vista .ic16{width:20px;height:20px;fill:var(--accent-text);margin-right:0}
/* ÓRBITA (variante C del lienzo «Procesado animado», elegida por Alejandro el 19-sep): el punto da UNA vuelta dejando
   el aro dibujado (misma curva y misma duración: 0,62 s), se funde, aparece el fondo y se traza el visto. Una sola vez.
   Apagada con «reducir movimiento» o con el interruptor de animaciones de Ajustes (.sin-anim): queda el visto quieto. */
@media (prefers-reduced-motion:no-preference){
  .msgbox-ok[open]:not(.sin-anim){animation:mok-entra .18s ease-out}
  .msgbox-ok[open]:not(.sin-anim) .mok-aro{animation:mok-aro .62s cubic-bezier(.5,0,.3,1) backwards}
  .msgbox-ok[open]:not(.sin-anim) .mok-orb{animation:mok-orb .775s backwards}
  .msgbox-ok[open]:not(.sin-anim) .mok-fondo{animation:mok-fondo .3s .5s ease-out backwards}
  .msgbox-ok[open]:not(.sin-anim) .mok-v{animation:mok-visto .3s .6s ease-out backwards}
  @keyframes mok-entra{from{opacity:0;transform:scale(.94)}to{opacity:1;transform:none}}
  @keyframes mok-aro{from{stroke-dashoffset:164}to{stroke-dashoffset:0}}
  @keyframes mok-orb{0%{transform:rotate(0deg);opacity:1;animation-timing-function:cubic-bezier(.5,0,.3,1)}80%{transform:rotate(360deg);opacity:1}100%{transform:rotate(360deg);opacity:0}}
  @keyframes mok-fondo{from{opacity:0}to{opacity:1}}
  @keyframes mok-visto{from{stroke-dashoffset:40}to{stroke-dashoffset:0}}
}
@media (max-width:720px){.msgbox-ok{min-width:0;width:min(340px,88vw)}}

/* Cuentas (19-sep-2026): el detalle de una cuenta es una VENTANA aparte, no un bloque debajo de la tabla. */
.cuenta-modal{width:min(1080px,96vw);padding:16px 18px 18px;overflow:auto}
.cuenta-modal .kardex{margin-top:0}
.cuenta-modal .kardex-head{flex-wrap:wrap;row-gap:6px}
/* Como en .kardex-modal: la regla «.venta-modal form» (columna, pensada para el único form de la Caja) no aplica al
   form de fila del abono — sin esto quedaba apilado y pegado a la derecha. */
.cuenta-modal form.fn-row{flex-direction:row;align-items:flex-end;flex-wrap:wrap;max-height:none;height:auto;gap:10px}
.cuenta-modal form.fn-row .fld.grow{flex:1 1 220px}
@media (max-width:720px){.cuenta-modal{width:100vw;max-width:100vw;max-height:100vh;border-radius:0;padding:12px}}

/* FICHA de cliente / proveedor (19-sep-2026): ventana con los campos rotulados, para crear y para modificar. */
.ficha-modal{width:min(640px,96vw)}
.ficha-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px 14px}
.ficha-grid .ficha-ancho{grid-column:1 / -1}
.ficha-grid .inp{width:100%;box-sizing:border-box}
.ficha-grid .fld-help{font-weight:400}
.ficha-modal .vm-body{display:block;overflow:auto;max-height:calc(92vh - 130px)}
.ficha-modal .vm-sec:last-child{border-bottom:none}
.ficha-modal h2 .ic16{vertical-align:-2px;margin-right:6px}
@media (max-width:720px){.ficha-modal{width:100vw;max-width:100vw;border-radius:0}.ficha-grid{grid-template-columns:1fr}}
/* «✕ Cerrar» fijo arriba a la derecha de la ventana (la cabecera envuelve en dos renglones y lo arrastraba abajo). */
.cuenta-modal .kardex-head{position:relative;padding-right:112px}
.cuenta-modal .kardex-head>button:last-child{position:absolute;top:-2px;right:0}

/* ─── Pronto pago (26-sep-2026): escalera de descuento del proveedor en la ficha, el cajón de Compras y el panel de CxP ─── */
.pp-tramos{display:flex;flex-direction:column;gap:4px;margin-top:4px}
.pp-tramo-fila{display:flex;align-items:center;gap:6px;font-size:13px}.pp-tramo-fila .inp{max-width:84px}
.pp-escalera{margin:8px 0 0;padding:8px 10px;border:1px solid var(--border);border-radius:8px;display:flex;flex-direction:column;gap:6px;font-size:13px;background:var(--surface-2)}
.pp-tramos-linea{display:flex;flex-wrap:wrap;gap:6px 10px}
.pp-tramo{padding:2px 9px;border-radius:999px;border:1px solid var(--border);white-space:nowrap}
.pp-tramo.vigente{border-color:var(--ok);box-shadow:inset 0 0 0 1px var(--ok);font-weight:600}
.pp-tramo.vencido{opacity:.5;text-decoration:line-through}
#c-pp-tot{font-size:13px}#c-pp-tot .tot-lbl{margin-left:6px}

/* BARRA «A» DEL INVENTARIO (04-oct-2026, 0.157; lienzo «Barra del Inventario», decisión de Alejandro: «Construye la A»): tres
   franjas con nombre —Buscar y filtrar · Vista · Acciones—, cada una con su rótulo pequeño a la izquierda y una raya fina entre
   ellas. Medidas del lienzo: franja de 46 px (5 px arriba y abajo), rótulo de 78 px en Poppins 10 px, separadores de 1 × 22 px,
   botones de 33 px con letra de 13,5 px; «Importar / Exportar / Fotos por código» a la derecha en el estilo sutil y, desde
   1600 px de ancho, «Importar inventario» / «Exportar inventario». El plegado de la cabecera pegada y el resto no cambian. */
#inv-filtros.inv-barra{display:flex;flex-direction:column;align-items:stretch;flex-wrap:nowrap;gap:0;margin-bottom:4px;min-height:0}
.inv-franja{display:flex;align-items:center;flex-wrap:wrap;gap:6px 10px;min-height:46px;padding:5px 0}
.inv-franja+.inv-franja{border-top:1px solid var(--border)}
.inv-rot{flex:none;width:78px;font-family:var(--font-head);font-size:10px;font-weight:600;letter-spacing:.08em;line-height:1.3;text-transform:uppercase;color:var(--muted)}
.inv-sv{flex:none;width:1px;height:22px;background:var(--border)}
.inv-gr{display:inline-flex;align-items:center;gap:6px;flex:none}
.inv-gr .ver-lbl{font-size:12.5px;font-weight:600;color:var(--muted)}
.inv-franja .chips{flex:none;flex-wrap:nowrap;gap:6px}
.inv-franja .chips-ver{gap:6px}
.inv-franja .search-wrap{flex:1000 1 220px;min-width:220px;max-width:640px}
.inv-franja .search-wrap .inp{flex:1 1 auto;max-width:none;height:36px}
.inv-franja .inv-bus{margin-left:0}
.inv-franja .spacer{flex:1 1 0}
.inv-franja>.btn-ghost,.inv-franja>.btn-primary{flex:none;height:33px;padding:0 12px;font-size:13.5px;gap:6px;white-space:nowrap;display:inline-flex;align-items:center}
.inv-franja>.btn-ghost.btn-sutil{padding:0 10px;font-size:12.5px}
/* El giro de «cargando» (button.htmx-request::after, arriba) ensanchaba el botón 18 px mientras viajaba la petición: en la franja
   Acciones, que a 1366 px va justa, eso la partía en dos renglones y la cabecera crecía 37 px por un momento (y movía la lista).
   Dentro de la barra el giro va encima de la esquina del botón, sin ocupar sitio. */
.inv-franja>button.htmx-request{position:relative}
.inv-franja>button.htmx-request::after{position:absolute;top:-5px;right:-5px;margin:0;width:9px;height:9px;background:var(--surface);border-radius:50%}
.inv-lbl-largo{display:none;text-transform:lowercase}
@media (min-width:1600px){.inv-lbl-largo{display:inline}}
/* Teléfono: el rótulo va encima de su franja y el buscador ocupa el ancho; todo envuelve sin salirse. */
@media (max-width:720px){
  .inv-franja{gap:6px;padding:6px 0}
  .inv-rot{width:100%}
  .inv-franja .search-wrap{flex:1 1 100%;min-width:0;max-width:none}
  .inv-franja .chips{flex-wrap:wrap}
  .inv-sv{display:none}
  .inv-franja .spacer{display:none}
}

/* Aviso flotante DENTRO de un modal abierto (window.toastDlg): #toast-host queda debajo de la capa del modal. */
.toast-dlg{z-index:100}
/* 0.160 toasts / aplicar ajustes */
/* Aviso de ÉXITO (window.toastOk): el .toast de siempre con un check verde (--ok, que ya tiene su versión clara y oscura); el
   trazo va en --surface para leerse sobre el verde en los dos temas. Se desvanece al irse (.sale). En el teléfono los avisos
   se estiran entre los dos márgenes: un texto largo parte en renglones en vez de salirse por la izquierda. */
.toast.toast-ok{display:flex;align-items:center;gap:9px}
.toast.toast-ok .toast-ic{flex:none;width:20px;height:20px;padding:3px;border-radius:50%;background:var(--ok);fill:none;stroke:var(--surface);stroke-width:3;stroke-linecap:round;stroke-linejoin:round;box-sizing:border-box}
.toast.toast-ok .toast-txt{min-width:0;overflow-wrap:anywhere}
.toast.sale{opacity:0;transform:translateY(-4px);transition:opacity .3s ease,transform .3s ease}
@media (max-width:720px){.toast-host:not(.toast-dlg){left:12px;right:12px}}
@media (prefers-reduced-motion:reduce){.toast.sale{transition:none}}
/* «Aplicar Ajustes» (botón de la Edición General con al menos un cambio guardado): el mismo naranja de «activa», el rótulo
   nuevo y un chip con los artículos tocados; un pulso corto al pasar a este estado avisa sin mover nada de la barra. */
.inv-edit-n{display:inline-flex;align-items:center;justify-content:center;min-width:19px;height:19px;padding:0 5px;margin-left:1px;border-radius:999px;background:var(--accent-ink);color:#fff;font-size:11.5px;font-weight:700;line-height:1;font-variant-numeric:tabular-nums}
.inv-edit-n[hidden]{display:none}
.inv-edit-btn.aplicar{font-weight:600;animation:inv-aplicar-pulso .6s ease-out 1}
@keyframes inv-aplicar-pulso{0%{box-shadow:0 0 0 0 rgba(238,140,42,.55)}100%{box-shadow:0 0 0 9px rgba(238,140,42,0),0 3px 10px rgba(238,140,42,.3)}}
@media (prefers-reduced-motion:reduce){.inv-edit-btn.aplicar{animation:none}}

/* 0.160 ventana de venta */
/* LA VENTA Y LA COMPRA EN SU PROPIA VENTANA (09-oct-2026, propuesta A del lienzo «Caja en ventana propia»). Reemplaza a la píldora
   «en curso» de la 0.159. (1) El CONTADOR sobre los mosaicos Ventas y Compras de la barra de módulos: los artículos de la venta (o
   las líneas de la compra) en curso en esta PC; tocarlo trae su ventana (public/app.js → exyVentana). Naranja del sistema con la
   tinta oscura de siempre, en los dos temas; un aro del color de la barra lo despega del mosaico (también del activo, naranja).
   (2) La pregunta de la X de la ventana con trabajo: «Guardar para después · Descartar · Volver a la venta» (reusa .msgbox).
   (3) La ventana de la compra reusa la página de Compras: la barra de filtros y la lista quedan ocultas detrás del diálogo. */
.tile{position:relative}
.tile-n{position:absolute;top:2px;right:6px;min-width:20px;height:20px;padding:0 6px;box-sizing:border-box;border-radius:10px;
  background:#E2882C;color:#14161b;font:700 11.5px/20px var(--font-head);text-align:center;font-variant-numeric:tabular-nums;
  box-shadow:0 0 0 2px var(--surface);cursor:pointer;animation:tile-n-in .3s cubic-bezier(.2,.85,.3,1.15)}
.tile-n:hover{background:#f39a3d}
.tile-n:focus-visible{outline:2px solid #E2882C;outline-offset:2px}
.tile.active .tile-n{box-shadow:0 0 0 2px var(--surface),0 0 0 3px rgba(20,22,27,.35)}
@keyframes tile-n-in{from{opacity:0;transform:scale(.5)}}
@media (prefers-reduced-motion:reduce){.tile-n{animation:none}}
.vcierre{max-width:min(92vw,520px)}
.vcierre .vcierre-tit{margin-bottom:6px;font-weight:600}
.vcierre .vcierre-det{margin:0 0 18px;font-size:13.5px;line-height:1.45;color:var(--muted)}
.vcierre .vcierre-btns{flex-wrap:wrap}
.vcierre .vcierre-btns .vcierre-desc{background:transparent;color:var(--no);border:1px solid color-mix(in srgb,var(--no) 55%,transparent)}
.vcierre .vcierre-btns .vcierre-desc:hover{background:color-mix(in srgb,var(--no) 12%,transparent);color:var(--no)}
body[data-ventana] .content > .toolbar,body[data-ventana] .content > p.muted,body[data-ventana] #compras-list{display:none}
/* 0.160 umbrales: rojo · amarillo · verde, menos pálidos */
/* Pedido de Alejandro (09-oct): en Reportes → «Existencias por umbral» los mínimos van en ROJO, los dos promedios en AMARILLO y
   los máximos en VERDE (en la 0.158 promedio salía verde y máximo azul porque la tabla tomaba los tokens --ex-prom-* / --ex-max-*,
   que son los de «Completada» de los Movimientos del día), y con tonos menos pálidos. Tokens PROPIOS --um-* para no cambiar
   «Anulada» / «Completada» (que siguen con --ex-*); los usan la etiqueta de Estado, los números y rótulos de Mín / Prom. ini /
   Prom. fin / Máx y las casillas «Ver mínimos / promedios / máximos». Mismo semáforo que el Inventario y el editor de producto.
   Texto ≥ 4,5:1 sobre las filas de la tabla y sobre el fondo de su etiqueta, en claro y en oscuro (e2e-umbral-colores.mjs). */
:root{--um-min-bg:#fdc0c0;--um-min-tx:#a50f15;--um-prom-bg:#fde68a;--um-prom-tx:#7a5300;--um-max-bg:#b7ecc4;--um-max-tx:#0f6428}
[data-theme="oscuro"]{--um-min-bg:#6b1d24;--um-min-tx:#ff8a8a;--um-prom-bg:#5c4500;--um-prom-tx:#ffd54a;--um-max-bg:#14532d;--um-max-tx:#5fdc84}
#rep-exist .ex-min{background:var(--um-min-bg);color:var(--um-min-tx)}
#rep-exist .ex-prom{background:var(--um-prom-bg);color:var(--um-prom-tx)}
#rep-exist .ex-max{background:var(--um-max-bg);color:var(--um-max-tx)}
#rep-exist .ex-col-min{color:var(--um-min-tx)}
#rep-exist .ex-col-prom{color:var(--um-prom-tx)}
#rep-exist .ex-col-max{color:var(--um-max-tx)}
.rep-chks .rep-chk-minimo{background:var(--um-min-bg);color:var(--um-min-tx)}
.rep-chks .rep-chk-promedio{background:var(--um-prom-bg);color:var(--um-prom-tx)}
.rep-chks .rep-chk-maximo{background:var(--um-max-bg);color:var(--um-max-tx)}
.rep-chks .rep-chk-minimo input{accent-color:var(--um-min-tx)}
.rep-chks .rep-chk-promedio input{accent-color:var(--um-prom-tx)}
.rep-chks .rep-chk-maximo input{accent-color:var(--um-max-tx)}
/* 0.160 existencias: miniaturas */
/* Decisión de Alejandro (09-oct): en Reportes → «Existencias por umbral» la opción A del lienzo —miniatura de 56 px en una columna
   «Foto» entre el código y el nombre, y la vista ampliada de 240 px al pasar el ratón (o al tocar / llegar con el teclado)—. Filas
   de ~66 px (56 + 5 + 5). Con foto: cuadro blanco con la imagen contenida; sin foto: cuadro punteado con el icono de la categoría,
   más callado para que no compita con las fotos. La capa ampliada (#ex-pop) la crea el script de views/reportes.js, fija al
   viewport y por encima del encabezado pegado. En el teléfono (≤ 720 px) la miniatura baja a 40 px para no ensanchar más la tabla. */
#rep-exist-tabla th.ex-th-foto{width:72px}
#rep-exist-tabla td.ex-foto{padding:5px 8px;width:72px}
#rep-exist .ex-mini{box-sizing:border-box;width:56px;height:56px;margin:0;padding:0;border-radius:7px;background:#fff;border:1px solid var(--border);display:flex;align-items:center;justify-content:center;overflow:hidden;position:relative;font:inherit;color:inherit}
#rep-exist button.ex-mini{cursor:zoom-in}
#rep-exist button.ex-mini:hover,#rep-exist button.ex-mini[aria-expanded="true"]{border-color:var(--accent)}
#rep-exist button.ex-mini:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
#rep-exist .ex-mini img{max-width:100%;max-height:100%;object-fit:contain;display:block}
#rep-exist .ex-mini.ex-sinfoto{background:var(--surface-2);border-style:dashed;flex-direction:column;gap:1px;cursor:default}
#rep-exist .ex-mini.ex-sinfoto svg{color:var(--violet);opacity:.55}
#rep-exist .ex-mini.ex-sinfoto i{display:none}
.ex-pop{position:fixed;z-index:1000;left:0;top:0;box-sizing:border-box;width:252px;background:var(--surface);color:var(--text);border:1px solid var(--border);border-radius:12px;box-shadow:0 14px 40px rgba(15,25,45,.28);padding:6px;pointer-events:none}
.ex-pop[hidden]{display:none}
.ex-pop .ex-pop-img{height:240px;background:#fff;border-radius:8px;display:flex;align-items:center;justify-content:center;overflow:hidden}
.ex-pop .ex-pop-img img{max-width:100%;max-height:100%;object-fit:contain;display:block}
.ex-pop .ex-pop-t{font-size:12px;font-weight:600;padding:7px 6px 3px;line-height:1.3}
.ex-pop .ex-pop-s{font-size:11px;color:var(--muted);padding:0 6px 4px}
[data-theme="oscuro"] .ex-pop{box-shadow:0 14px 40px rgba(0,0,0,.6)}
@media (max-width:720px){
  #rep-exist-tabla th.ex-th-foto,#rep-exist-tabla td.ex-foto{width:52px;padding:4px 6px}
  #rep-exist .ex-mini{width:40px;height:40px;border-radius:6px}
  #rep-exist .ex-mini.ex-sinfoto svg{width:20px!important;height:20px!important}
}
/* 0.160 existencias: armar pedido */
/* «Armar pedido» (decisión de Alejandro del 09-oct) en la barra Exportar de Reportes, solo con «Existencias por umbral»: el
   interruptor (encendido con el aro naranja, como «Por caja»), y a su lado el resumen «N artículos · X unidades», «Generar pedido»
   y «Limpiar». Encendido, la tabla gana al final la columna «Pedir» (la pinta el script de views/reportes.js): celdas numéricas
   alineadas a la derecha; la que lleva cantidad se resalta. */
.rep-pedido{display:inline-flex;align-items:center;gap:8px;flex-wrap:wrap}
.rep-pedido .rep-pedido-on{display:inline-flex;align-items:center;gap:6px}
.rep-pedido .rep-pedido-on[aria-pressed="true"]{border-color:var(--accent);color:var(--accent-text);box-shadow:inset 0 0 0 1px var(--accent)}
.rep-pedido-barra{display:inline-flex;align-items:center;gap:8px;flex-wrap:wrap}
.rep-pedido-barra[hidden]{display:none}
.rep-pedido-barra .btn-primary{display:inline-flex;align-items:center;gap:6px}
.rep-pedido-res{font-size:13px;font-weight:600;color:var(--muted);white-space:nowrap;font-variant-numeric:tabular-nums}
#rep-exist-tabla th.rep-pedir-th{width:86px}
#rep-exist-tabla td.rep-pedir{padding-top:4px;padding-bottom:4px}
#rep-exist-tabla .rep-pedir-inp{width:72px;padding:6px 8px;text-align:right;font-variant-numeric:tabular-nums;font-weight:600}
#rep-exist-tabla .rep-pedir-inp.con{border-color:var(--accent);box-shadow:inset 0 0 0 1px var(--accent)}
@media (max-width:720px){#rep-exist-tabla .rep-pedir-inp{width:58px;padding:5px 6px}}
/* «En el teléfono» (10-oct-2026, Ajustes → Cajas en red): la dirección con nombre es larga; en el teléfono se parte en vez de
   quedar cortada por el borde de la card. */
#tel-dominio code.red-dir{word-break:break-all;white-space:normal}
/* «Instalar la app» (12-oct-2026, views/config.js → filaQrInstalar): UN solo QR para el teléfono, a /instalar. Junto al código, en
   columna: qué hace, la dirección que lleva y «Imprimir QR». En el teléfono la fila va en una columna (el QR mide 180 px). */
.qr-nota{display:flex;flex-direction:column;align-items:flex-start;gap:8px;max-width:340px;min-width:0}
.qr-fila .qr-nota .muted{max-width:none}
.qr-url{font-family:var(--font-mono);font-size:12px;word-break:break-all;white-space:normal}
.qr-imprimir{font-size:13px;padding:5px 12px}
@media (max-width:560px){.fld-row.qr-instalar{grid-template-columns:minmax(0,1fr);gap:6px}}
/* «Instale Exynol en su teléfono» (12-oct-2026, views/instalar.js): la página del QR. La escena del login (login-wrap + login-card)
   con la tarjeta al ancho del teléfono; cada estado (instalar, instalada, los pasos de cada navegador…) es un <section data-para>
   que el script de la página muestra. Claro y oscuro con las variables de siempre; de 320 px en adelante sin desliz lateral. */
.inst-wrap{padding:16px 12px}
.login-card.inst-card{width:min(440px,100%);gap:14px;padding:22px 24px 20px}
.inst-card .brand-login{padding:6px 0 2px;margin:0}
.inst-app{display:flex;align-items:center;justify-content:center;gap:12px;min-width:0}
.inst-icono{width:56px;height:56px;border-radius:14px;flex:none;box-shadow:0 2px 8px rgba(2,8,20,.28)}
.inst-app-t{display:flex;flex-direction:column;min-width:0;line-height:1.25}
.inst-app-t b{font-family:var(--font-head);font-size:17px;color:var(--text)}
.inst-app-t span{color:var(--muted);font-size:13.5px;overflow-wrap:anywhere}
.inst-t{font-family:var(--font-head);font-weight:700;font-size:20px;line-height:1.2;margin:2px 0 0;text-align:center;color:var(--text)}
.inst-p{margin:0;font-size:14px;line-height:1.45;color:var(--muted);text-align:center}
.inst-paso{display:flex;flex-direction:column;gap:10px;margin:0;padding:14px;border-radius:12px;background:var(--surface-2);border:1px solid var(--border);min-width:0}
.inst-paso[hidden]{display:none}
.inst-paso h2{font-family:var(--font-head);font-size:15px;font-weight:600;margin:0;color:var(--text)}
.inst-pasos{margin:0;padding-left:22px;display:flex;flex-direction:column;gap:8px;font-size:14.5px;line-height:1.45;color:var(--text)}
.inst-ic{display:inline-block;width:18px;height:18px;vertical-align:-4px;fill:currentColor}
.inst-btn{width:100%;padding:13px 16px;font-size:17px;display:inline-flex;align-items:center;justify-content:center;gap:6px;text-decoration:none;text-align:center}
.inst-btn .ic16{width:19px;height:19px;margin-right:2px}
.inst-nota{margin:0;font-size:12.5px;line-height:1.4;color:var(--muted);text-align:center}
.inst-ok{margin:0;font-size:17px;font-weight:700;color:var(--ok);text-align:center}
.inst-aviso{margin:0;font-size:14px;line-height:1.45;color:var(--text)}
.inst-aviso[hidden]{display:none}
.inst-espera{display:flex;align-items:center;justify-content:center;gap:8px;margin:0;font-size:14px;color:var(--muted)}
.inst-url{font-family:var(--font-mono);font-size:12px;word-break:break-all;white-space:normal}
.inst-dir{margin:0;font-size:12.5px;color:var(--muted);text-align:center;overflow-wrap:anywhere}
.inst-dir code{font-family:var(--font-mono);font-size:12px;word-break:break-all}
.inst-entrar{width:100%;justify-content:center;padding:10px 15px}
.inst-card[data-estado="instalada"] .inst-entrar,.inst-card[data-estado="ya"] .inst-entrar{display:none}
@media (max-width:400px){.login-card.inst-card{padding:18px 16px 16px;gap:12px}.inst-t{font-size:18.5px}.inst-paso{padding:12px}}

/* ═══ VISTA PREVIA DE REPORTES (12-oct-2026, 0.163; pedido de Alejandro: ver qué datos van en el archivo antes de generarlo). Un
   diálogo con el reporte en tablas (mismas celdas que el PDF / Excel); en el teléfono ocupa la pantalla y las tablas se deslizan. ═══ */
.rep-prev-modal{width:min(1100px,96vw);max-width:none}
.rep-prev-modal[open]{display:flex;flex-direction:column}
.rep-prev-body{overflow:auto;max-height:calc(92vh - 130px)}
.rep-prev-cab h3{margin:0 0 2px;font-family:var(--font-head);font-size:17px}
.rep-prev-cab p{margin:0;font-size:13px}
.rep-prev-sec{margin-top:14px}
.rep-prev-sec h4{margin:0 0 6px;font-family:var(--font-head);font-size:12.5px;text-transform:uppercase;letter-spacing:.05em;color:var(--muted)}
.rep-prev-tabla th,.rep-prev-tabla td{white-space:nowrap}
.rep-prev-tabla td.num,.rep-prev-tabla th.num{text-align:right;font-variant-numeric:tabular-nums}
.rep-prev-tabla tfoot td{font-weight:700;border-top:2px solid var(--border)}
.rep-prev-modal .vm-foot.rep-prev-foot{width:auto;flex-direction:row;align-items:center;flex-wrap:wrap;gap:8px;border-radius:0 0 var(--r-modal) var(--r-modal)}
.rep-prev-mas,.rep-prev-grafica{margin:6px 0 0;font-size:12.5px}
@media (max-width:720px){.rep-prev-modal{width:100vw;height:100dvh;max-height:100dvh;border-radius:0}.rep-prev-body{max-height:none;flex:1 1 0;min-height:0}.rep-prev-foot{flex-wrap:wrap}.rep-prev-nota{flex-basis:100%}}
