/* public/css/estilos.css -- mobile-first, responsive desde el arranque.
   CONTEXTO-ERP-ALIMENTOS.md seccion 1 ("Acceso mobile"): el dueño necesita usar el
   dashboard/reportes desde el celular -- misma SPA responsive, no una app nativa aparte.

   Sistema de diseno (restyle ago-2026): paleta/tipografia/espaciados adaptados de un
   mockup de referencia, aplicados a las clases que YA usa app.js -- ningun nombre de
   clase cambia, solo sus reglas. app.js no sabe ni le importa como se ve cada clase. */

:root {
  --surface-1: #fcfcfb;
  --page: #f7f7f4;
  --text-primary: #14140f;
  --text-secondary: #55534c;
  --muted: #8b8a82;
  --gridline: #e4e2d8;
  --border: rgba(20, 20, 15, 0.10);
  --brand: #1a7a5e;
  --brand-dark: #103f30;
  --good: #0ca30c;
  /* --good-texto: el mismo verde, oscurecido hasta pasar AA COMO TEXTO (10/09/2026).
     --good (#0ca30c) da 3,27:1 sobre --surface-1 -- MEDIDO, con el mismo instrumento que
     reproduce los 2,70 y 5,95 de --warning/--warning-texto -- y no llega al 4,5:1 de texto
     normal. #0a820a es el primer tono de la misma familia que pasa sobre los dos fondos:
     4,86:1 sobre --surface-1 y 4,65:1 sobre --page. Lo estrena el indicador "En vivo" de
     Pedidos externos. Los usos viejos de --good como letra (.exito, KPIs, estados) siguen en
     el backlog -- misma regla que --warning-texto: un elemento NUEVO no nace fallando. */
  --good-texto: #0a820a;
  --warning: #d98a00;
  /* --warning-texto: el mismo ambar, oscurecido hasta pasar AA COMO TEXTO.
     --warning (#d98a00) da 2,70:1 sobre el fondo de las tarjetas -- MEDIDO -- y no llega al
     4,5:1 de texto normal. Sirve como fondo o como borde, no como letra.
     #8f5400 da 5,95:1 medido sobre rgb(252,252,251), con margen y no pegado al umbral.
     Los usos viejos de --warning como texto (.aviso, .compra-fila-antes) siguen en el backlog;
     lo que NO puede pasar es que un elemento NUEVO nazca ya fallando.
     Sin tema oscuro en este proyecto (cero @media prefers-color-scheme), asi que un solo valor. */
  --warning-texto: #8f5400;
  /* --texto-atenuado: para lo que se MUESTRA pero no compite -- un dato que es contexto, no
     senal. --muted (#8b8a82) es el gris que el repo ya usa para eso (.caja-renglon-vacio),
     pero da 3,38:1 sobre --surface-1 y 3,23:1 sobre --page -- MEDIDO -- y no llega al 4,5:1
     de texto normal. #72716b, la misma familia de gris calido oscurecida, da 4,77:1 y 4,56:1
     sobre esos dos fondos: pasa AA en los dos y se sigue leyendo mas tenue que
     --text-secondary (7,50:1). Los usos viejos de --muted como texto siguen en el backlog;
     un elemento NUEVO no nace fallando (misma regla que --warning-texto, arriba). */
  --texto-atenuado: #72716b;
  --serious: #c1440e;
  --critical: #c73838;
  --good-bg: rgba(12, 163, 12, 0.10);
  --warning-bg: rgba(217, 138, 0, 0.10);
  --serious-bg: rgba(193, 68, 14, 0.10);
  --critical-bg: rgba(199, 56, 56, 0.10);
  --radius-card: 14px;
  --radius-control: 9px;
  --shadow-card: 0 1px 3px rgba(20, 20, 15, 0.08);
  --sidebar-bg: var(--brand-dark);
  --sidebar-text: #cfe3da;
  --sidebar-text-dim: rgba(207, 227, 218, 0.55);
  --sidebar-active: var(--brand);
  --ancho-sidebar: 230px;
}

*, *::before, *::after {
  box-sizing: border-box;
}

/* [hidden] SIEMPRE gana (fix 2026-08-22, bug encontrado en el smoke test del deploy).
   El atributo `hidden` se aplica desde la hoja de estilos del USER AGENT, que tiene MENOS
   precedencia que cualquier regla de AUTOR: alcanza con que una clase declare `display`
   para que el elemento no se oculte nunca, aunque app.js ponga hidden=true correctamente.
   Habia dos casos vivos en produccion: .aviso-global (display:flex) -- el banner rojo con
   la "x" flotando vacio en todas las pantallas -- y .imagen-preview (display:block) -- el
   recuadro de vista previa que no desaparecia sin imagen.
   Se arregla UNA VEZ y para toda la CLASE de bug, no con parches tipo .aviso-global[hidden]:
   cualquier elemento futuro que combine `hidden` con una regla de `display` va a funcionar
   sin que nadie tenga que acordarse de esto. El !important es necesario y correcto -- compite
   contra reglas de autor con display, y "oculto" siempre debe ganarle a "asi me veo".
   Auditoria del fix: de los 48 elementos que app.js togglea con .hidden, SOLO esos 2 tenian
   una regla de autor con `display` sobre si mismos, asi que esta regla no cambia como se ve
   ningun otro (los demas ya se ocultaban bien). */
[hidden] {
  display: none !important;
}

body {
  margin: 0;
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  background: var(--page);
  color: var(--text-primary);
  -webkit-tap-highlight-color: transparent;
}

body:has(#pantalla-login[hidden]) {
  padding-left: var(--ancho-sidebar);
}
body:has(#barra-lateral.colapsada) {
  padding-left: 0;
}

.encabezado {
  background: var(--surface-1);
  color: var(--text-primary);
  padding: 1rem 1.25rem;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

.encabezado h1 {
  margin: 0;
  font-size: 1.1rem;
  font-weight: 700;
}

/* .encabezado::before llevaba content: "🍦" -- el cono de helado a la izquierda de
   "ERP Alimentos" en la barra superior. Quitado el 20/09/2026 por decision del dueño,
   junto con el gemelo que vivia en el marcado (.sb-head-icono, index.html). Motivo: el
   producto es multi-tenant y el encabezado no puede declarar un nicho -- mango-bike no es
   una heladeria. >>> NO VUELVE SIN DECISION NUEVA <<< -- lo hace cumplir el caso
   [ENCABEZADO-2] de test/encabezado-sin-nicho.test.js.
   No hace falta tocar el espaciado: .encabezado es flex con `gap: 0.6rem`, y el gap solo
   separa ENTRE items; sin el ::before queda un unico hijo y no sobra ningun hueco. */

.contenedor {
  padding: 1rem;
  display: flex;
  flex-direction: column;
  gap: 1rem;
  max-width: 1100px;
  margin: 0 auto;
}

.tarjeta {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: 1.25rem;
  box-shadow: var(--shadow-card);
}

.tarjeta h2 {
  margin-top: 0;
  font-size: 1.05rem;
}

form {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

label {
  font-weight: 600;
  font-size: 0.85rem;
  color: var(--text-secondary);
}

input, select {
  padding: 0.7rem 0.75rem;
  font-size: 1rem;
  font-family: inherit;
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  width: 100%;
  background: var(--surface-1);
  color: var(--text-primary);
}

input:focus, select:focus {
  outline: 2px solid var(--brand);
  outline-offset: 1px;
}

button {
  padding: 0.75rem 1rem;
  font-size: 1rem;
  font-weight: 700;
  border: none;
  border-radius: var(--radius-control);
  background: var(--brand);
  color: #fff;
  cursor: pointer;
  font-family: inherit;
}

button:hover {
  background: var(--brand-dark);
}

.error {
  color: var(--critical);
  font-size: 0.85rem;
  min-height: 1.2em;
  margin: 0;
}

.texto-ayuda {
  color: var(--text-secondary);
  font-size: 0.85rem;
  margin: 0.25rem 0;
}

/* EL ROJO ES PARA EL ERROR, NO PARA EL REQUISITO (arquitecto, 14/09/2026). Un requisito ("Elegí el
   medio de pago.", "Escribí el nombre del pedido.") se dice en gris desde el principio; se pone
   ROJO solo cuando la persona intenta avanzar sin cumplirlo. Rojo antes de que nadie se equivoque
   es acusar de un error que todavia no ocurrio. Es la misma correccion que quedo anotada para el
   "elegí 1" del panel de modificadores. */
.texto-ayuda.requisito-incumplido {
  color: var(--critical);
  font-weight: 600;
}

/* POS del vendedor externo: el medio elegido, a la vista en el modal "Enviar pedido". */
.pos-modal-medio-pedido {
  text-align: center;
  margin: 0.25rem 0 0.75rem;
}

/* Mostrador: lo que anoto el vendedor. A la vista, sin precargar el select (el cajero elige). */
.pext-propuesto {
  margin: 0.5rem 0;
}

.imagen-preview {
  max-height: 120px;
  max-width: 100%;
  object-fit: contain;
  display: block;
  margin-top: 4px;
}

.exito {
  color: var(--good);
  font-size: 0.85rem;
  min-height: 1.2em;
  margin: 0;
}

/* .aviso: tercera variante del trio .error/.exito -- NO bloquea nada (Bloque 4+5 Parte A,
   aviso de vencimiento de plan a 3 dias), usa --warning que ya existe (severidad del
   resumen del dashboard), ningun color nuevo. */
.aviso {
  color: var(--warning);
  font-size: 0.85rem;
  min-height: 1.2em;
  margin: 0;
}

/* Sidebar de navegacion -- fijo a la izquierda (restyle ago-2026, estructura tomada de
   LISTO ERP, paleta propia -- ver comentario en index.html). Fase D2 Parte 2 sigue
   aportando el colapsable + acordeon por grupo, ahora sobre esta base fija. */
.barra-lateral {
  position: fixed;
  left: 0;
  top: 0;
  bottom: 0;
  width: var(--ancho-sidebar);
  background: var(--sidebar-bg);
  display: flex;
  flex-direction: column;
  gap: 0;
  padding: 0.75rem 0.6rem 1rem;
  overflow-y: auto;
  z-index: 200;
  box-shadow: 2px 0 16px rgba(10, 20, 15, 0.18);
  transition: transform 0.25s ease;
}

.barra-lateral.colapsada {
  transform: translateX(-100%);
}

#pantalla-login:not([hidden]) ~ .barra-lateral {
  display: none;
}


/* Los <nav id="nav-grupo-*"> conservan class="tarjeta" en el HTML (no se toca) pero
   dentro del sidebar oscuro esa tarjeta clara desentona -- se neutraliza aca, scoped,
   en vez de sacar la clase del HTML (menos riesgo de romper algo que dependa de ella). */
.barra-lateral .tarjeta {
  background: none;
  border: none;
  border-radius: 0;
  padding: 0;
  box-shadow: none;
}

.sb-head {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.5rem 0.4rem 0.9rem;
  margin-bottom: 0.4rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
}

/* .sb-head-icono se fue con el cono de helado del 20/09/2026: era la regla del unico
   elemento que usaba esa clase (index.html), asi que quedaba como CSS muerto. */

.sb-head-nombre {
  font-size: 0.95rem;
  font-weight: 700;
  color: #fff;
  line-height: 1.2;
}

.sb-head-sub {
  font-size: 0.62rem;
  font-weight: 600;
  color: var(--sidebar-text-dim);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* Boton "Fijar barra lateral" -- integrado al sidebar oscuro (variables --sidebar-text/
   --sidebar-active, NUNCA un boton claro tipo .boton-secundario/.boton-nav de contenido).
   Estado normal: texto atenuado, mismo tono que .sb-seccion-label/.boton-nav en reposo.
   .active: color --sidebar-active, para que se note con claridad cuando esta prendido. */
.boton-fijar-sidebar {
  display: block;
  width: 100%;
  text-align: left;
  background: transparent;
  border: none;
  color: var(--sidebar-text-dim);
  font-size: 0.72rem;
  font-weight: 600;
  font-family: inherit;
  padding: 0.35rem 0.5rem;
  margin: 0 0 0.4rem;
  border-radius: 6px;
  cursor: pointer;
}

.boton-fijar-sidebar:hover {
  background: rgba(255, 255, 255, 0.06);
  color: var(--sidebar-text);
}

.boton-fijar-sidebar.active {
  color: var(--sidebar-active);
  background: rgba(26, 122, 94, 0.18);
}

.sb-seccion-label {
  font-size: 0.62rem;
  font-weight: 700;
  color: var(--sidebar-text-dim);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin: 0.6rem 0.5rem 0.3rem;
}

.separador-nav {
  border: none;
  border-top: 1px solid rgba(255, 255, 255, 0.14);
  margin: 0.5rem 0.3rem;
}

.nav-grupo-header {
  display: flex;
  align-items: center;
  width: 100%;
  text-align: left;
  background: rgba(255, 255, 255, 0.06);
  border: none;
  border-radius: 8px;
  padding: 0.55rem 0.6rem;
  margin: 0.15rem 0 0.3rem;
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--sidebar-text);
  cursor: pointer;
  font-family: inherit;
}

.nav-grupo-header:hover {
  background: rgba(255, 255, 255, 0.11);
  color: #fff;
}

.nav-grupo-colapsado .menu-config {
  display: none;
}

.nav-grupo-colapsado .nav-grupo-header {
  margin-bottom: 0.15rem;
}

.menu-config {
  list-style: none;
  margin: 0 0 0.3rem;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}

.boton-nav {
  width: 100%;
  text-align: left;
  background: none;
  color: var(--sidebar-text-dim);
  font-weight: 500;
  font-size: 0.78rem;
  padding: 0.45rem 0.6rem 0.45rem 1.2rem;
  border-radius: 6px;
  border-left: 3px solid transparent;
}

.boton-nav:hover {
  background: rgba(255, 255, 255, 0.07);
  color: var(--sidebar-text);
}

.boton-nav.active {
  background: rgba(26, 122, 94, 0.45);
  color: #fff;
  font-weight: 600;
  border-left-color: var(--sidebar-active);
}

/* .nav-punto -- el puntico de aviso en un item del menu (20/09/2026, pedidos externos). PRIMER
   indicador de este tipo en la app: hasta hoy no habia ningun badge, punto ni contador en el
   sidebar. Se deja generico a proposito (`nav-punto`, no `pext-punto`) para que el proximo lo
   reuse en vez de inventar otro.
   - inline-block con `vertical-align: middle` y alto fijo de 8px: mas chico que la caja de linea
     del texto (0.78rem), asi que NO cambia el alto del boton. Medido a 390 px: el boton mide lo
     mismo con el punto y sin el.
   - SIN numero, por decision del arquitecto: dice "hay algo", no "hay siete".
   - var(--critical), el rojo que ya usa .error -- no se inventa un color nuevo. */
.nav-punto {
  display: inline-block;
  width: 8px;
  height: 8px;
  margin-left: 0.4rem;
  vertical-align: middle;
  background: var(--critical);
  border-radius: 50%;
}

/* .solo-lectores -- texto para lectores de pantalla, invisible en la pantalla. El patron estandar
   (1px + clip) en vez de display:none o visibility:hidden, que lo esconderian TAMBIEN del lector,
   que es justo lo contrario de lo que se busca. */
.solo-lectores {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.boton-secundario {
  background: #f0efe9;
  color: var(--text-secondary);
}

.boton-secundario:hover {
  background: #e6e4da;
  color: var(--text-primary);
}

.boton-secundario:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.campo-checkbox {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-weight: 400;
}

.campo-checkbox input {
  width: auto;
}

.lista-sucursales, .lista-catalogo {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.fila-sucursal, .fila-catalogo {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  padding: 0.7rem 0.5rem;
  border-bottom: 1px solid var(--gridline);
  border-radius: 8px;
}

.fila-sucursal:hover, .fila-catalogo:hover {
  background: #f9f8f4;
}

.fila-sucursal span, .fila-catalogo span {
  flex: 1 1 auto;
  font-weight: 600;
}

.fila-sucursal select, .fila-catalogo input, .fila-catalogo select {
  flex: 1 1 auto;
  width: auto;
}

/* Fase D2, Control de Inventario -- resalta una fila cuando bajoMinimo:true. Reusa
   var(--critical), el mismo color que ya usa .error, no inventa uno nuevo. */
/* DOS NIVELES de severidad en Control de Inventario (ronda 2026-08-24). Reusa los tokens
   que YA existen con esta misma semantica en .mp-estado-agotado/.mp-estado-bajo del Maestro
   de Productos -- var(--critical) rojo para agotado, var(--warning) ambar para bajo. No se
   agrego ningun token nuevo. .fila-bajo-minimo CAMBIA de rojo a ambar: antes era el unico
   nivel y por eso era rojo; ahora el rojo es de .fila-agotado. */
.fila-agotado {
  color: var(--critical);
  font-weight: 700;
}

.fila-bajo-minimo {
  color: var(--warning);
  font-weight: 700;
}

/* Pantalla Compras -- restyle a grilla de filas editables (Fase D2 Parte 2). Reusa
   .fila-catalogo como base (misma familia visual que el resto de la app, ver nota
   extendida en index.html) -- estas reglas solo ajustan los anchos relativos de sus
   controles, no reemplazan nada de la base. */
.compra-proveedor-selector, .compra-items-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}

.compra-proveedor-selector input {
  flex: 1 1 auto;
}

.fila-compra-item select {
  flex: 0 1 130px;
}

.fila-compra-item input[type="text"] {
  flex: 2 1 200px;
}

.fila-compra-item input[type="number"] {
  flex: 0 1 110px;
}

.compra-fila-costo-total {
  flex: 0 0 auto;
  font-weight: 700;
  min-width: 80px;
  text-align: right;
}

/* Bloque 2 ("precio de venta editable en Compras") -- texto chico de referencia con el
   precioVenta ACTUAL del producto que ya matchea por nombre exacto (mismo tono que
   .grid-celda-label, sin competir visualmente con el precio de venta NUEVO que se tipea en
   campoPrecioVenta al lado). Oculto para filas itemTipo:"insumo" (via [hidden] nativo).
   .compra-fila-costo-anterior (ronda 2026-08-22) es su gemelo para el COSTO vigente del item
   -- misma familia visual a proposito, porque cumplen la misma funcion (referencia, no dato
   editable); el costo del campo SI se precarga desde la ultima compra desde 2026-08-24 (ver la
   historia completa en app.js), y este span sigue siendo el CPP vigente, que es otra cosa.
   Este si se muestra para insumos Y productos. */
/* Aviso EN VIVO de nombre ya usado por otro item ACTIVO, debajo del campo Nombre de los
   formularios de Productos e Insumos (ronda 2026-08-22). Reusa .error como base -- es la misma
   senal de "esto hay que corregirlo" del resto de la app -- y solo ajusta el tamaño para que
   se lea como una nota al pie del campo y no como un bloque de error de pantalla completa. Se
   oculta con el [hidden] nativo cuando no hay choque. NO deshabilita el boton de guardar: el
   rechazo real vive en productos.js/insumos.js. */
.aviso-nombre-duplicado {
  margin: 0.2rem 0 0.6rem;
  font-size: 0.85rem;
}

/* Aviso de datos maestros incompletos (ronda 2026-08-22) -- Configuracion > Productos
   (activos sin precio de venta) y Configuracion > Insumos (activos sin costo unitario).
   Reusa la clase .error como base (misma senal visual que el resto de la app para "esto hay
   que arreglarlo"), estas reglas solo dan el encuadre de bloque: el .error suelto esta
   pensado para una linea de texto, no para un titulo + explicacion + lista. Se oculta con el
   [hidden] nativo cuando no queda ninguno -- no hay boton para taparlo. */
.aviso-datos-incompletos {
  border: 1px solid currentColor;
  border-radius: 6px;
  padding: 0.6rem 0.8rem;
  margin: 0.5rem 0;
}

.aviso-datos-incompletos p {
  margin: 0.3rem 0;
  font-size: 0.85rem;
}

.aviso-datos-incompletos ul {
  margin: 0.3rem 0 0;
  padding-left: 1.2rem;
  font-size: 0.85rem;
}

.compra-fila-precio-actual,
.compra-fila-costo-anterior {
  flex: 0 1 160px;
  font-size: 0.78rem;
  color: var(--text-secondary);
}

.grilla-pos {
  display: grid;
  /* Columnas mas angostas + tarjetas de altura FIJA (mejora de UX POS, ronda 2) --
     pensado para categorias de hasta ~20 productos, grilla pareja y predecible en vez de
     tarjetas gigantes cuando hay pocos productos (altura elastica de antes). */
  grid-template-columns: repeat(auto-fill, minmax(116px, 1fr));
  gap: 0.5rem;
  margin-bottom: 1rem;
}

/* pos-secciones: contenedor EXTERIOR de pos-grilla-productos (index.html) -- cada hijo es
   1 seccion completa de categoria (<h4> + su .grilla-pos interna). Clase separada a
   proposito, mejora de UX POS: .grilla-pos es una grilla de columnas angostas pensada SOLO
   para los botones de producto DENTRO de una categoria (renderGrillaProductosPos,
   app.js) -- reusarla tambien en el contenedor exterior empaquetaba cada seccion completa
   como si fuera 1 sola tarjeta angosta (bug de estructura ya corregido). */
.pos-secciones {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

/* pos-buscador: filtro de texto del POS (mejora de UX, ver index.html) -- hereda
   padding/border/radius de la regla base "input, select" (mas arriba), solo le agrega el
   margen inferior que ya usa .cat-tabs debajo, para que no queden pegados. */
.pos-buscador {
  margin-bottom: 0.75rem;
}

/* pos-categoria-titulo: encabezado de cada grupo de categoria en la vista "Todas" (mejora
   de UX POS) -- ancla visual de la grilla, por eso tiene que ser MAS grande que
   .boton-producto-pos-nombre (0.82rem) y .boton-producto-pos-precio (0.92rem) -- nunca mas
   chico que lo que agrupa. */
.pos-categoria-titulo {
  margin: 0 0 0.6rem;
  font-size: 1.05rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--brand-dark);
  border-bottom: 2px solid var(--gridline);
  padding-bottom: 0.4rem;
}

.grilla-pos .boton-producto-pos {
  /* Altura FIJA (no min-height elastico) -- ver nota de .grilla-pos arriba.
     5rem -> 7.25rem (2026-08-27): la tarjeta ahora tiene que contener CUATRO lineas de
     nombre, no dos (ver -webkit-line-clamp abajo). El numero esta MEDIDO en el navegador,
     no estimado: 4 lineas x 18,50px + 20,74px de la fila del precio + 8,8px x 2 de padding
     = 112,34px. 7rem (112px) se queda 0,3px corto y CORTA EL PRECIO; 7,25rem (116px) deja
     3,66px de holgura. Si alguna vez se cambia el font-size del nombre o del precio, este
     numero se vuelve a medir -- no se ajusta a ojo. */
  height: 7.25rem;
  background: var(--surface-1);
  border: 1px solid var(--border);
  color: var(--text-primary);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  font-weight: 700;
  text-align: left;
  padding: 0.55rem 0.6rem;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

/* Jerarquia visual nombre/precio (mejora de UX) -- MISMO criterio que
   .tarjeta-kpi-label/.tarjeta-kpi-valor. Tamaños reducidos respecto a la ronda anterior
   para que nombre + fila inferior (precio + insignia de stock) entren comodos en la
   columna mas angosta (ronda 2, ~104px) sin verse apretados.

   >>> EL CLAMP PASO DE 2 A 4 LINEAS (2026-08-27). ES UN ERROR DE COBRO, NO ESTETICA. <<<
   Con 2 lineas, CUATRO productos reales del catalogo pintaban exactamente "ACEITE DE COCO…"
   y solo se distinguian por el precio: $20.000, $30.000, $35.000 y $50.000. Seis pares
   indistinguibles, medidos en el navegador con los nombres reales -- alguien cobra el de
   $20.000 creyendo que toca el de $50.000.

   POR QUE 4 Y NO 3: se midio. Con 3 lineas bajaba de 6 pares a 2, pero dejaba
   "Cosmetico 250" y "Cosmetico 500" todavia iguales -- el token que los separa cae en la
   CUARTA linea. Tres lineas arregla la mitad del problema, que en un error de cobro no
   sirve de nada.
   POR QUE NO SE TRUNCA AL MEDIO: "ACEITE DE…250 ML" pierde Comestible/Cosmetico, que es la
   otra mitad de la distincion. Ninguna de las dos puntas se puede tirar.
   POR QUE NO SE ENSANCHO LA TARJETA: tambien resolvia (minmax 116->132px, 0 pares), pero
   baja de 5 columnas a 4 y cuesta MAS scroll que las 4 lineas.

   >>> "0 PARES" ES UNA MEDICION CON FECHA, NO UNA GARANTIA. <<<
   Vale para los 62 nombres activos del 27/08/2026. El primer producto nuevo con nombre
   largo puede volver a colisionar, y nada en el codigo lo va a impedir. El chequeo que si
   lo impediria esta en el backlog, pegado al aviso de nombre duplicado. */
.boton-producto-pos-nombre {
  font-weight: 700;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.01em;
  color: var(--text-primary);
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
}

.boton-producto-pos-fila-inferior {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.35rem;
}

.boton-producto-pos-precio {
  font-weight: 700;
  font-size: 1rem;
  color: var(--brand);
}

/* Insignia de stock (mejora de UX POS, ronda 2, SOLO tipo:"reventa") -- gris/discreta en
   estado normal, var(--warning) cuando esta baja/agotada (mismo tono que .mp-estado-bajo
   en Maestro de Productos, no se inventa un color nuevo). */
.boton-producto-pos-stock {
  flex: 0 0 auto;
  font-size: 0.74rem;
  font-weight: 700;
  color: var(--text-secondary);
  background: #f0efe9;
  border-radius: 999px;
  padding: 0.12rem 0.48rem;
}

.boton-producto-pos-stock-bajo {
  color: #fff;
  background: var(--warning);
}

/* Feedback tactil sutil (referencia hiOPOS ya documentada en el proyecto) -- leve
   translateY + sombra mas marcada al pasar el mouse/tocar, :active vuelve a su lugar (da
   sensacion de "boton fisico" presionado). */
.grilla-pos .boton-producto-pos:hover {
  background: #f9f8f4;
  transform: translateY(-2px);
  box-shadow: 0 4px 10px rgba(20, 20, 15, 0.15);
}

.grilla-pos .boton-producto-pos:active {
  transform: translateY(0);
  box-shadow: var(--shadow-card);
}

/* Producto reventa sin stock disponible (mejora de UX POS, ronda 2) -- deshabilitado real
   (atributo disabled, ver renderGrillaProductosPos/app.js) + atenuado visualmente, para
   que el cajero vea de entrada que no hay mas sin tener que hacer clic. Anula el efecto
   hover/active de arriba -- un boton disabled no dispara click, pero :hover igual aplica
   por CSS si no se neutraliza aca. */
.grilla-pos .boton-producto-pos:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.grilla-pos .boton-producto-pos:disabled:hover,
.grilla-pos .boton-producto-pos:disabled:active {
  background: var(--surface-1);
  transform: none;
  box-shadow: var(--shadow-card);
}

/* Icono "x" circular del carrito (mejora de UX POS, ronda 2) -- reemplaza el boton de
   texto "Quitar" anterior. Gris discreto en reposo, var(--critical) (mismo rojo que ya
   usa .error/.fila-bajo-minimo, no se inventa un color nuevo) con texto blanco al hover. */
.boton-quitar-carrito-pos {
  flex: 0 0 auto;
  width: 1.8rem;
  height: 1.8rem;
  padding: 0;
  border-radius: 50%;
  background: #f0efe9;
  color: var(--text-secondary);
  font-size: 1.1rem;
  line-height: 1;
  font-weight: 700;
}

.boton-quitar-carrito-pos:hover {
  background: var(--critical);
  color: #fff;
}

.pos-modificadores-linea {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin-top: 0.4rem;
  padding: 0.5rem 0.6rem;
  background: #f7f7f4;
  border-radius: 8px;
  border: 1px solid var(--gridline);
  flex-basis: 100%;
}

.pos-modificadores-linea fieldset {
  border: none;
  padding: 0;
  margin: 0;
}

.pos-modificadores-linea legend {
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--text-secondary);
  padding: 0;
}

.pos-modificadores-linea label {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-weight: 400;
  font-size: 0.85rem;
  color: var(--text-primary);
}

/* ---- PANEL DE MODIFICADORES DEL POS (12/09/2026) ----
   Aprobado por Alejandro sobre claude/prototipos/pos-modificadores.html; lo arma
   renderPanelModificadoresPos (app.js). Se abre en la zona de productos al tocar un producto CON
   grupos: un tenant sin modificadores (Puerto Coco) nunca lo ve. TODAS las clases son pm-*, nuevas:
   ninguna regla de aca toca algo que ya existia.
   Por que botones de FILA COMPLETA con la casilla a la izquierda: en el carrito, la casilla vivia
   dentro de un bloque alineado a la DERECHA, y su posicion dependia de cuanto medía la ultima linea
   del nombre -- "Sin pimienta" en dos lineas dejaba su casilla en otro lugar que la de "Sin tajin".
   Aca la casilla es el primer hijo de una fila alineada a la izquierda: queda en columna siempre.
   Color por la PRESENTACION del grupo (el campo del paso 2): agregar = verde, quitar = rojo, sin
   marcar = neutro. Colores: los tokens del repo, no los del prototipo. */
.pm-panel {
  border: 2px solid var(--brand);
  border-radius: var(--radius-card);
  background: var(--surface-1);
  padding: 1rem;
  box-shadow: var(--shadow-card);
}
.pm-cabecera { display: flex; justify-content: space-between; align-items: baseline; gap: 0.75rem; margin-bottom: 1rem; }
.pm-cabecera-nombre { font-size: 1.1rem; font-weight: 800; text-transform: uppercase; color: var(--text-primary); }
.pm-cabecera-precio { font-size: 1.05rem; font-weight: 700; color: var(--brand); white-space: nowrap; }
.pm-grupo { margin-bottom: 1rem; }
.pm-grupo-titulo { display: flex; justify-content: space-between; align-items: baseline; gap: 0.5rem; margin-bottom: 0.5rem; }
.pm-grupo-nombre { font-size: 0.78rem; font-weight: 800; letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-secondary); }
.pm-grupo-regla { font-size: 0.78rem; color: var(--text-secondary); white-space: nowrap; }
.pm-grupo-regla.pm-falta { color: var(--critical); font-weight: 700; }
/* SIN scroll interno: todas las opciones a la vista, scrollea la pagina. A 390 una por renglon. */
.pm-opciones { display: grid; grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr)); gap: 0.5rem; }
@media (max-width: 699.98px) {
  .pm-opciones { grid-template-columns: 1fr; }
}
/* min-height 3rem: blanco de toque para el dedo, no la casilla de 13 px del carrito. */
.pm-opcion {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  min-height: 3rem;
  padding: 0.6rem 0.75rem;
  border: 1.5px solid var(--border);
  border-radius: var(--radius-control);
  background: var(--surface-1);
  color: var(--text-primary);
  font: inherit;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}
/* `button:hover` (0-1-1, arriba en este archivo) le gana a `.pm-opcion` (0-1-0) y pintaria de verde
   oscuro una opcion SIN marcar al pasarle el dedo o el mouse. Estas dos reglas (0-2-0 y 0-3-0) lo
   impiden; las opciones MARCADAS ya ganan con sus propias reglas (0-3-0, mas abajo). */
.pm-opcion:hover { background: #f9f8f4; border-color: var(--brand); }
.pm-opcion:disabled:hover { background: var(--surface-1); border-color: var(--border); }
.pm-casilla {
  flex: 0 0 1.25rem;
  width: 1.25rem;
  height: 1.25rem;
  border: 2px solid #a5a39b;
  border-radius: 5px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-size: 0.8rem;
  line-height: 1;
}
.pm-nombre { flex: 1 1 auto; font-size: 0.95rem; line-height: 1.2; }
.pm-precio { flex: 0 0 auto; font-size: 0.9rem; font-weight: 700; color: var(--brand); white-space: nowrap; }
/* El fondo verde es --brand (#1a7a5e) al 10%: no hay token de fondo para --brand. */
.pm-opcion.pm-sel.pm-agregar { background: rgba(26, 122, 94, 0.10); border-color: var(--brand); }
.pm-opcion.pm-sel.pm-agregar .pm-casilla { background: var(--brand); border-color: var(--brand); }
.pm-opcion.pm-sel.pm-quitar { background: var(--critical-bg); border-color: var(--critical); }
.pm-opcion.pm-sel.pm-quitar .pm-casilla { background: var(--critical); border-color: var(--critical); }
.pm-opcion.pm-sel.pm-neutro { border-color: var(--text-primary); }
.pm-opcion.pm-sel.pm-neutro .pm-casilla { background: var(--text-primary); border-color: var(--text-primary); }
.pm-cantidad { display: flex; align-items: center; gap: 0.75rem; margin: 0.25rem 0 1rem; }
.pm-cantidad-rotulo { font-size: 0.78rem; font-weight: 800; letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-secondary); }
.pm-cantidad button { width: 2.75rem; height: 2.75rem; font-size: 1.3rem; font-weight: 700; padding: 0; }
.pm-cantidad-numero { font-size: 1.2rem; font-weight: 800; min-width: 2rem; text-align: center; }
.pm-acciones { display: flex; gap: 0.6rem; }
.pm-acciones button { padding: 0.85rem 1rem; font-size: 1rem; font-weight: 700; }
.pm-acciones .pm-agregar-boton { flex: 2 1 auto; }
.pm-acciones .pm-cancelar { flex: 1 1 auto; }
/* El aviso al lado de Cobrar apagado mientras el panel esta abierto. */
.pm-aviso-cobrar { margin: 0.5rem 0 0; font-size: 0.9rem; font-weight: 600; color: var(--text-secondary); }
/* La linea del carrito de un producto CON grupos: sus cambios abajo, en chico. El monto de cada
   modificador es por la CANTIDAD de la linea -- lo que se cobra y lo que dice la remision. */
.carrito-linea .pm-mods { margin: 0.3rem 0 0; font-size: 0.82rem; line-height: 1.55; }
.carrito-linea .pm-mod-mas { color: var(--brand); font-weight: 700; }
.carrito-linea .pm-mod-men { color: var(--critical); font-weight: 700; }
.carrito-linea .pm-mod-neutro { color: var(--text-primary); font-weight: 600; }
.carrito-linea .pm-sin-cambios { color: var(--text-secondary); }
.carrito-linea .pm-linea-pie { display: flex; justify-content: space-between; align-items: center; gap: 0.5rem; margin-top: 0.35rem; }
.carrito-linea .pm-editar { background: none; border: 0; padding: 0; font-size: 0.85rem; font-weight: 700; color: var(--brand); cursor: pointer; }

/* Rediseno de la linea del carrito del POS (fix visual/UX sobre mockup aprobado) -- 2
   filas internas FIJAS dentro de cada <li class="carrito-linea"> (renderCarritoPos,
   app.js), en vez del armado plano anterior (4 hijos con flex:1 1 auto heredado de
   .fila-catalogo) que se desarmaba con nombres de producto largos + modificadores
   debajo. ADITIVO -- .fila-catalogo/.boton-quitar-carrito-pos NO se tocan, las siguen
   usando otras pantallas. `.carrito-linea` es un bloque normal (no flex): fila1,
   [.pos-modificadores-linea si aplica] y fila2 se apilan en flujo normal, en ese orden. */
.carrito-linea {
  padding: 0.7rem 0.5rem;
  border-bottom: 1px solid var(--gridline);
  border-radius: 8px;
}

.carrito-linea:hover {
  background: #f9f8f4;
}

.carrito-linea-fila1 {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

/* min-width:0 en el nombre es lo que permite que el ellipsis funcione DENTRO de un flex
   item -- sin esto, un nombre largo sigue empujando el boton "x" fuera de la fila en vez
   de cortarse. */
.carrito-linea-nombre {
  flex: 1 1 auto;
  min-width: 0;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Icono "x" circular -- MISMO look que .boton-quitar-carrito-pos (gris discreto en
   reposo, var(--critical) al hover, ya usado por .error/.fila-bajo-minimo, ningun color
   nuevo), clase separada porque esta linea ya no comparte .fila-catalogo. */
.carrito-linea-x {
  flex: 0 0 auto;
  width: 1.8rem;
  height: 1.8rem;
  padding: 0;
  border-radius: 50%;
  background: #f0efe9;
  color: var(--text-secondary);
  font-size: 1.1rem;
  line-height: 1;
  font-weight: 700;
}

.carrito-linea-x:hover {
  background: var(--critical);
  color: #fff;
}

.carrito-linea-fila2 {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin-top: 0.5rem;
}

/* stepper-cantidad: pildora compacta (-/input/+) -- reemplaza el <input type="number">
   suelto de antes. El input sigue siendo el MISMO campo tipeable (renderCarritoPos, para
   pedidos grandes), solo se le saca la apariencia nativa de spinner (dos reglas de abajo)
   para que conviva visualmente con los botones -/+ sin duplicar controles. */
.stepper-cantidad {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
  border: 1px solid var(--border);
  border-radius: 999px;
  overflow: hidden;
}

.stepper-cantidad button {
  flex: 0 0 auto;
  width: 1.8rem;
  height: 1.8rem;
  padding: 0;
  border: none;
  border-radius: 0;
  background: var(--surface-1);
  color: var(--brand-dark);
  font-size: 1rem;
  font-weight: 700;
  line-height: 1;
}

.stepper-cantidad button:hover {
  background: var(--brand);
  color: #fff;
}

.stepper-cantidad input {
  width: 2.4rem;
  border: none;
  border-left: 1px solid var(--border);
  border-right: 1px solid var(--border);
  border-radius: 0;
  text-align: center;
  padding: 0.3rem 0;
  -moz-appearance: textfield;
}

.stepper-cantidad input::-webkit-outer-spin-button,
.stepper-cantidad input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.carrito-linea-subtotal {
  flex: 0 0 auto;
  font-weight: 700;
}

@media (min-width: 600px) {
  .contenedor {
    padding: 1.5rem;
  }
}

.pos-layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
  align-items: start;
}

@media (min-width: 700px) {
  .pos-layout {
    grid-template-columns: 1fr 320px;
  }
}

.cart-panel {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: 1rem;
  box-shadow: var(--shadow-card);
}

/* Carrito del POS acotado (auditoria de UX) -- con muchos items, scroll INTERNO de la
   lista en vez de empujar Total/medios de pago/boton Cobrar fuera de la pantalla. Selector
   por ID (#pos-lista-carrito), NO en .lista-catalogo -- esa clase es compartida por otras
   listas de la app (Productos, Insumos, etc.) que no deben acotarse. */
#pos-lista-carrito {
  max-height: 40vh;
  overflow-y: auto;
}

/* pos-carrito-total: el numero mas prominente del panel de carrito -- el cajero lo
   necesita confirmar de un vistazo antes de cobrar (mejora de UX POS). */
.pos-carrito-total {
  font-size: 1.3rem;
  font-weight: 800;
  color: var(--text-primary);
  border-top: 1px solid var(--gridline);
  padding-top: 0.6rem;
  margin-top: 0.4rem;
}

@media (min-width: 700px) {
  .cart-panel {
    position: sticky;
    top: 1rem;
  }
}

.cat-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-bottom: 0.75rem;
}

.cat-tab {
  background: #f0efe9;
  color: var(--text-secondary);
  font-weight: 700;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding: 0.5rem 0.9rem;
  border-radius: 999px;
}

.cat-tab.active {
  background: var(--brand);
  color: #fff;
}

.modal-bg {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(20, 20, 15, 0.45);
  align-items: center;
  justify-content: center;
  z-index: 50;
  padding: 1rem;
}

.modal-bg.show {
  display: flex;
}

.modal-box {
  background: var(--surface-1);
  border-radius: 16px;
  padding: 1.5rem;
  width: 100%;
  max-width: 360px;
  text-align: center;
}

.modal-box button {
  width: 100%;
  margin-top: 0.5rem;
}

/* Cuadro "¿Imprimir esta venta?" (20/09/2026) -- index.html, #pos-dialogo-imprimir.

   z-index: aparece ARRIBA del modal del cobro, que sigue abierto con el ticket a la vista.
   .modal-bg base es 50; este va por encima a proposito, sin depender del orden del DOM.

   >>> LA SEPARACION ENTRE LOS BOTONES NO ES ESTETICA. <<< La base de .modal-box button los
   deja apilados a 0.5rem (8 px), que es la distancia correcta para botones que hacen cosas
   parecidas. Estos hacen cosas DISTINTAS -- "IMPRIMIR" arriba, "NO IMPRIMIR" abajo -- y ademas se
   parecen mucho al leerlos rapido: el ultimo es el primero con un "NO" adelante. A 8 px, un dedo
   apurado que apunta al de arriba cancela una impresion que si iba, y ese papel no vuelve solo:
   hay que ir a "Buscar una venta" a reimprimirlo, y la comanda de cocina NO se reimprime nunca
   (remision.js: la copia sale sin comanda). Por eso cada boton de abajo lleva margen propio,
   2.5rem, en vez del heredado.
   Desde el 08/10/2026 son TRES (IMPRIMIR / SOLO COMANDA / NO IMPRIMIR) y los dos pares hacen cosas
   distintas: 40 px entre cada uno, a 1366 px y a 390 px. Lo MIDE el caso 13 de
   test/pos/confirmar-impresion.js (hasta el 08/10 este comentario lo afirmaba sin test). */
#pos-dialogo-imprimir {
  z-index: 60;
}

#pos-dialogo-imprimir-detalle {
  margin: 0.25rem 0 0;
  font-size: 1rem;
  color: var(--text-secondary);
}

/* Lo que saca cada boton va DENTRO del boton (08/10/2026): con tres salidas, un renglon arriba que
   las describa obliga a cruzar la vista; adentro se lee lo que se toca. Mas chico y en peso normal:
   dice QUE sale, y no compite con la accion. */
.pos-dialogo-imprimir-que {
  display: block;
  margin-top: 0.15rem;
  font-size: 0.8rem;
  font-weight: 400;
}

#pos-dialogo-imprimir-si {
  margin-top: 1.5rem;
}

#pos-dialogo-imprimir-comanda {
  margin-top: 2.5rem;
}

#pos-dialogo-imprimir-no {
  margin-top: 2.5rem;
}

/* Modificador opcional para modales con formularios mas anchos que el modal de cobro del
   POS (ej. el modal "+ Crear nuevo" de Compras, Fase D2 hallazgo de UX bullet 3) -- NUNCA
   toca las reglas base de .modal-box/.modal-bg, que son compartidas. */
.modal-box-ancho {
  max-width: 480px;
  text-align: left;
}

.modal-total {
  font-size: 1.6rem;
  font-weight: 800;
  margin: 0.5rem 0 1rem;
}

.ticket-preview {
  font-family: 'Courier New', monospace;
  font-size: 0.75rem;
  text-align: left;
  background: #fff;
  border: 1px dashed var(--border);
  padding: 0.6rem;
  border-radius: 6px;
  margin-top: 0.5rem;
  white-space: pre-wrap;
}

.pago-grupo {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 8px;
  margin: 8px 0;
}
.pago-btn {
  padding: 10px 8px;
  border: 1px solid var(--brand);
  border-radius: var(--radius-control);
  background: var(--surface-1);
  color: var(--text-primary);
  cursor: pointer;
  font-size: 0.9rem;
  font-weight: 600;
}
.pago-btn.active {
  background: var(--brand);
  color: #fff;
}

/* Dashboard de Reportes (Fase D2, Tanda de Reportes ronda 1) -- reusa las variables/
   sombra/radio de .tarjeta (--surface-1/--border/--radius-card/--shadow-card), NINGUN
   sistema de diseno nuevo, solo layout en grid para las tarjetas KPI y los mini-graficos. */
.grilla-kpis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 0.75rem;
  margin-bottom: 0.5rem;
}

.tarjeta-kpi {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  padding: 0.9rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

.tarjeta-kpi-label {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-secondary);
}

.tarjeta-kpi-valor {
  font-size: 1.4rem;
  font-weight: 800;
  color: var(--text-primary);
}

.tarjeta-kpi-valor.tarjeta-kpi-good { color: var(--good); }
.tarjeta-kpi-valor.tarjeta-kpi-warning { color: var(--warning); }
.tarjeta-kpi-valor.tarjeta-kpi-critical { color: var(--critical); }

/* Pantalla Resumen -- diseño "estado colapsado" (confirmado con Alejandro, 2026-08-08).
   Hero de "Vendido hoy": mismo fondo/borde/radio/sombra que .tarjeta-kpi (ningun sistema
   de diseño paralelo), solo mas grande -- el numero mas importante de la pantalla, pensado
   para leerse de un vistazo en celular. */
.resumen-hero {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  padding: 1rem 1.1rem;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  margin-bottom: 0.75rem;
}

.resumen-hero-label {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-secondary);
}

.resumen-hero-valor {
  font-size: 2.6rem;
  font-weight: 600;
  color: var(--text-primary);
  line-height: 1.1;
}

.resumen-estado-titulo {
  margin: 1rem 0 0.4rem;
  font-size: 0.95rem;
  color: var(--text-secondary);
}

/* Franja de Estado: lista de filas, una por categoria que amerite reportarse (o una sola
   fila "Todo en orden" -- ver renderEstadoResumen/calcularResumenEstado). Reusa el mismo
   punto circular de color que .semaforo-punto (P&G/Control de Inventario, mas abajo) pero
   con clase propia -- esta franja es de una sola columna con detalle opcional, no encaja
   en el layout de grilla de .fila-catalogo. */
.lista-estado-resumen {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.fila-estado-resumen {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  padding: 0.65rem 0.75rem;
  border-radius: var(--radius-control);
}

.fila-estado-resumen-punto {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex: 0 0 auto;
  margin-top: 0.3rem;
}

.fila-estado-resumen-texto {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}

.fila-estado-resumen-titulo {
  font-weight: 700;
  color: var(--text-primary);
}

.fila-estado-resumen-detalle {
  font-size: 0.82rem;
  color: var(--text-secondary);
}

.fila-estado-resumen-good { background: var(--good-bg); }
.fila-estado-resumen-warning { background: var(--warning-bg); }
.fila-estado-resumen-serious { background: var(--serious-bg); }
.fila-estado-resumen-critical { background: var(--critical-bg); }

.fila-estado-resumen-punto-good { background: var(--good); }
.fila-estado-resumen-punto-warning { background: var(--warning); }
.fila-estado-resumen-punto-serious { background: var(--serious); }
.fila-estado-resumen-punto-critical { background: var(--critical); }

/* Semaforo de salud -- 3 checks (ver renderPyG, app.js), reusa .fila-catalogo como base
   (mismo lenguaje visual que el resto de listas de esta app) y solo agrega el punto de
   color. Selector calificado con "li.fila-catalogo span." (no solo ".semaforo-punto"): la
   regla base ".fila-sucursal span, .fila-catalogo span" (mas arriba) tiene mas
   especificidad que ".semaforo-punto" sola y estira el punto a una elipse horizontal
   (flex:1 1 auto/width:auto) si no se le gana en especificidad explicitamente. */
li.fila-catalogo span.semaforo-punto {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex: 0 0 auto;
}

.semaforo-punto.semaforo-ok { background: var(--good); }
.semaforo-punto.semaforo-warn { background: var(--warning); }
.semaforo-punto.semaforo-critico { background: var(--critical); }

/* Comparativa vs periodo anterior -- el signo/color de la variacion % se resuelve en
   app.js (favorable/desfavorable depende de la metrica: para ventas subir es bueno, para
   CMV/gastos subir es malo, ver calcularClaseComparativa). */
.comparativa-favorable { color: var(--good); font-weight: 700; }
.comparativa-desfavorable { color: var(--critical); font-weight: 700; }
.comparativa-neutral { color: var(--muted); font-weight: 700; }

/* Mini-graficos de tendencia (ultimos 6 meses) -- DOM/CSS puro, sin libreria (adaptado de
   un patron de referencia de LISTO ERP, ver miniBarChart en app.js). El contenedor solo
   pone el marco de tarjeta + el eje de barras; cada barra trae su propio alto inline
   (calculado en JS segun el valor). */
.grilla-mini-charts {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}

@media (min-width: 700px) {
  .grilla-mini-charts {
    grid-template-columns: 1fr 1fr;
  }
}

.mini-chart-contenedor {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  padding: 0.9rem;
}

.mini-chart-titulo {
  margin: 0 0 0.5rem;
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--text-secondary);
}

.mini-chart {
  display: flex;
  align-items: flex-end;
  gap: 6px;
  min-height: 100px;
}

/* Control de Inventario -- Maestro de Productos + Insumos (Fase D2, Tanda de Reportes
   ronda 2 REDEFINIDA). Sigue siendo <li class="fila-catalogo">, NUNCA <table> (ver nota
   extendida en index.html) -- selectores calificados "li.fila-catalogo.claseGrid" /
   "li.fila-catalogo span.grid-celda-label" (nunca la clase sola): la regla base
   ".fila-sucursal, .fila-catalogo { display:flex; ... }" y ".fila-catalogo span
   {font-weight:600}" (mas arriba) tienen la MISMA o mayor especificidad que una clase
   sola -- mismo error ya corregido con .semaforo-punto en la ronda 1, no se repite aca. */
li.fila-catalogo.fila-control-inventario {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
  gap: 0.5rem 1rem;
  align-items: start;
}

/* >>> FUENTE UNICA DE COLUMNAS DEL MAESTRO <<<
   --mp-grid-cols se declara UNA sola vez y la consumen el encabezado (.mp-grid-encabezado) y
   cada fila (.fila-maestro-producto). Cambiarla mueve las dos a la vez -- que es el punto.

   MEDIDO el 27/08 con el peor caso (nombre de 32 caracteres, ventana 1280): las 11 columnas
   necesitan 896px y hay 945px disponibles, sobran 49px. El GAP NO SE ACHICA: es lo que hace
   legible una tabla de 63 filas, y recortarlo pagaria 38px con la legibilidad de todas.
   "Codigo" salio de la vista (84px) y vive en el title de la celda del nombre. */
.mp-grid {
  /* +1 columna el 01/09/2026: "Te dura". 92px alcanza para "más de 30 días". */
  --mp-grid-cols: 150px minmax(86px, 1fr) 40px 56px 62px 58px 58px 52px 78px 62px 92px 74px;
  overflow-x: auto;
}

.mp-grid-encabezado {
  display: grid;
  grid-template-columns: var(--mp-grid-cols);
  gap: 0.5rem 0.9rem;
  align-items: center;
  min-width: 56rem;
}

/* Encabezado STICKY: con 63 renglones, en la fila 20 nadie se acuerda cual columna era cual. */
.mp-grid-encabezado {
  position: sticky;
  top: 0;
  z-index: 2;
  padding: 0.4rem 0.6rem;
  background: var(--page);
  border-bottom: 1px solid var(--border);
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-secondary);
}

/* El nombre, acotado con ellipsis. El codigo completo vive en el title de la celda. */
.mp-nombre {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ci-nota-alerta {
  display: inline-block;
  margin-left: 0.5rem;
  font-size: 0.8rem;
  color: var(--text-secondary);
}

li.fila-catalogo.fila-maestro-producto {
  display: grid;
  /* Consume la MISMA variable que el encabezado. Antes era repeat(auto-fit, minmax(84px,1fr)),
     que envolvia 13 celdas en 2 renglones -- y como esa regla tenia la misma especificidad que
     la del encabezado pero venia DESPUES, se imponia y la grilla salia desalineada. */
  grid-template-columns: var(--mp-grid-cols);
  gap: 0.5rem 0.9rem;
  align-items: center;
  /* El MISMO min-width que el encabezado: sin esto, en una ventana angosta las filas se
     comprimirian y el encabezado no, y las columnas dejarian de coincidir. 56rem = 896px, que
     es lo que necesitan las 11 columnas (medido). */
  min-width: 56rem;
  /* Y el MISMO padding lateral. .fila-catalogo trae 0.5rem y el encabezado usa 0.6rem: con esa
     diferencia, medida, las columnas quedaban corridas 1-2px entre el titulo y el dato. En una
     tabla de 11 columnas y 63 filas eso se ve. */
  padding-left: 0.6rem;
  padding-right: 0.6rem;
}

/* grid-celda: componente reutilizable "label chico arriba + valor abajo" -- MISMO
   lenguaje visual que .tarjeta-kpi-label/.tarjeta-kpi-valor (Dashboard, ronda 1), evita
   inventar un tercer sistema de etiquetas. Compartido por ambas grillas de arriba. */
.grid-celda {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
}

li.fila-catalogo span.grid-celda-label {
  font-size: 0.72rem;
  font-weight: 500;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

.grid-celda-valor {
  font-size: 0.95rem;
  color: var(--text-primary);
  overflow-wrap: break-word;
}

.fila-agotado .grid-celda-valor {
  color: var(--critical);
}

.fila-bajo-minimo .grid-celda-valor {
  color: var(--warning);
}

/* Columna "Estado" de Maestro de Productos -- mismo semantico rojo/amarillo/verde que el
   resto de la app (var(--critical)/var(--warning)/var(--good)). Solo color: NO se pelea
   por font-weight contra ".fila-catalogo span" (ya gana 600 por su cuenta, visualmente
   suficiente, no vale la pena calificar el selector para 1 punto mas de grosor). */
.mp-estado-agotado { color: var(--critical); }
.mp-estado-bajo { color: var(--warning); }
.mp-estado-ok { color: var(--good); }
/* "sin-datos" (producido/mezcla sin insumosPorUnidad/receta definidos -- Pieza 3, disponible
   estimado) -- ni rojo de agotado ni verde de ok, neutro (var(--muted), mismo gris ya usado
   para texto secundario en el resto de la app). */
.mp-estado-sin-datos { color: var(--muted); }
/* Columna "Precio" de Maestro de Productos cuando precioVenta es null (ronda "precio de
   venta fuera de creacion de producto") -- reusa var(--critical), mismo color que
   .mp-estado-agotado, sin inventar uno nuevo. */
.mp-precio-faltante { color: var(--critical); font-weight: 700; }

/* Barra de filtros de Maestro de Productos -- mismo patron flex-wrap que
   .compra-items-toolbar (Compras, Fase D2 Parte 2). */
.mp-filtros {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin: 0.75rem 0;
}

.mp-filtros input[type="text"] {
  flex: 2 1 200px;
}

.mp-filtros select {
  flex: 1 1 160px;
}

/* Toggles de estado (rojo/amarillo/verde) -- inactivos: borde/texto del color, fondo
   claro (mismo criterio que .pago-btn). Activos (.active, clickeado): fondo solido del
   color + texto blanco -- MISMA logica de toggle que .cat-tab.active/.pago-btn.active,
   ninguna clase nueva de interaccion, solo la paleta semantica ya usada en semaforos. */
.mp-filtro-estado {
  background: var(--surface-1);
  border: 1px solid var(--border);
  /* color EXPLICITO: sin esto el chip hereda el blanco del estilo base de <button> (pensado
     para el boton primario, que va sobre fondo verde) y queda blanco sobre blanco. Es el mismo
     bug que esta ronda vino a arreglar en .active -- encontrado al medir el contraste real de
     los chips no seleccionados despues del fix, no antes. */
  color: var(--text-primary);
  font-weight: 600;
  padding: 0.5rem 0.8rem;
  font-size: 0.85rem;
}

.mp-filtro-estado:hover {
  background: #f0efe9;
}

/* ===== Chips de filtro: LA SELECCION GANA SOBRE EL COLOR SEMANTICO (2026-08-25) =====

   BUG QUE ESTO ARREGLA, y estaba EN PRODUCCION: la regla era
       .mp-filtro-estado.active { color: #fff; }
   sin ningun background en la base. Los chips CON clase semantica recibian su fondo
   (-agotado/-bajo/-ok), pero "Todos" -- que no tiene clase semantica y ademas es el filtro POR
   DEFECTO de Control de Inventario -- quedaba con texto BLANCO sobre fondo claro: el unico
   chip ilegible de la fila era justamente el seleccionado. Se descubrio mirando una captura
   del modo conteo, donde parecia que "Con diferencia" estaba activo; en realidad el activo era
   "Todos" y habia desaparecido.

   PRINCIPIO: la SELECCION gana sobre el color semantico. El chip seleccionado se llena de
   color -- de acento por defecto, o del color de su significado si lo tiene -- y los NO
   seleccionados conservan su pista semantica en algo mas debil: un punto y el borde, nunca el
   texto entero coloreado compitiendo con el chip activo.

   Un solo cambio de CSS, sin tocar JS ni markup: arregla Control de Inventario (Maestro de
   Productos e Insumos) y el modo conteo de Ajuste de stock a la vez, porque las tres usan
   estas mismas clases. */

/* No seleccionados: el borde lleva la pista de color y el texto vuelve al color normal. */
.mp-filtro-estado-agotado { border-color: var(--critical); }
.mp-filtro-estado-bajo { border-color: var(--warning); }
.mp-filtro-estado-ok { border-color: var(--good); }

/* El punto reemplaza al texto coloreado como pista semantica: se lee sin gritar. Es
   ::before para no tocar el markup de las tres pantallas que usan estos chips. */
.mp-filtro-estado-agotado::before,
.mp-filtro-estado-bajo::before,
.mp-filtro-estado-ok::before {
  content: '';
  display: inline-block;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  margin-right: 0.4rem;
  vertical-align: 0.05em;
}

.mp-filtro-estado-agotado::before { background: var(--critical); }
.mp-filtro-estado-bajo::before { background: var(--warning); }
.mp-filtro-estado-ok::before { background: var(--good); }

/* Seleccionado: fondo de acento como BASE -- esta es la regla que faltaba y por la que
   "Todos" quedaba invisible. Cualquier chip, tenga o no clase semantica, se llena. */
.mp-filtro-estado.active {
  background: var(--brand);
  border-color: var(--brand);
  color: #fff;
}
.mp-filtro-estado.active:hover { filter: brightness(0.92); }

/* Y las variantes semanticas pisan esa base con su propio color. */
.mp-filtro-estado-agotado.active { background: var(--critical); border-color: var(--critical); }
.mp-filtro-estado-bajo.active { background: var(--warning); border-color: var(--warning); }
.mp-filtro-estado-ok.active { background: var(--good); border-color: var(--good); }

/* El punto del chip seleccionado se vuelve blanco: sobre el fondo lleno, el punto de color
   sobre su mismo color no se veria. */
.mp-filtro-estado.active::before { background: #fff; }

/* Historial de Ventas -- filas reorganizadas en grid (Fase D2, Tanda de Reportes ronda 3),
   MISMO patron que Maestro de Productos/Insumos (.grid-celda, mas arriba). Selector
   calificado "li.fila-catalogo.claseGrid" por el mismo motivo ya documentado arriba
   (".fila-catalogo { display:flex }" tiene la misma especificidad que una clase sola). */
li.fila-catalogo.fila-historial-venta {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(90px, 1fr));
  gap: 0.5rem 1rem;
  align-items: center;
}

li.fila-catalogo.fila-historial-periodo {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(90px, 1fr));
  gap: 0.5rem 1rem;
  align-items: start;
}

/* Columna "Estado" de Historial de Ventas -- mismo semantico verde/rojo que el resto de
   la app (var(--good)/var(--critical)), mismo criterio que .mp-estado-* (Maestro de
   Productos). */
.hv-estado-activa { color: var(--good); }
.hv-estado-anulada { color: var(--critical); }

/* Ventas por Vendedor (Reportes, admin-only) -- MISMO patron grid (.fila-catalogo +
   .grid-celda) que Maestro de Productos/Historial de Ventas, NUNCA <table> (ver nota en
   index.html). Un "card" por vendedor: nombre + Total vendido + Cantidad de ventas +
   Ticket promedio. Selector calificado "li.fila-catalogo.claseGrid" por el mismo motivo ya
   documentado arriba (".fila-catalogo { display:flex }" empata en especificidad con una
   clase sola). */
/* >>> FUENTE UNICA DE COLUMNAS DE VENTAS POR VENDEDOR <<<
   --vpv-grid-cols la ESCRIBE renderVentasPorVendedor (app.js), porque las columnas de medio
   se arman desde los datos y no se saben hasta consultar. La consumen el encabezado, cada
   fila, el desglose diario y el pie de totales: una sola variable mueve las cuatro.

   Antes era `repeat(auto-fit, minmax(120px, 1fr))`, y con el peor caso (9 columnas: nombre +
   5 medios + cuenta socio + total + ventas) NO ENTRABA -- 9 x 120 + 8 x 16 de gap = 1208px
   contra 1100px de contenedor, asi que auto-fit mandaba la novena a un segundo renglon.

   MEDIDO el 27/08 con montos de 7 cifras y el nombre de vendedor mas largo: las 9 columnas
   necesitan 660px de contenido + 128px de gaps = 788px, y hay 1100px. SOBRAN 312px. O sea que
   el problema NUNCA fue el contenido, era el piso de 120px del minmax. EL GAP NO SE ACHICA:
   con 312px de sobra, recortarlo seria pagar legibilidad por un problema inexistente. La
   columna mas ancha (BANCOLOMBIA, por su etiqueta) exige 92px. */
.vpv-grid {
  overflow-x: auto;
}

.vpv-grid-encabezado,
.vpv-grid-totales,
li.fila-catalogo.fila-ventas-vendedor,
.vpv-dia-fila {
  display: grid;
  grid-template-columns: var(--vpv-grid-cols);
  gap: 0.5rem 1rem;
  align-items: center;
  /* Mismo padding lateral en las cuatro: con .fila-catalogo trayendo 0.5rem y el encabezado
     otro valor, las columnas quedan corridas 1-2px entre el titulo y el dato (ya medido y
     corregido asi en .mp-grid). */
  padding-left: 0.6rem;
  padding-right: 0.6rem;
}

.vpv-grid-encabezado {
  position: sticky;
  top: 0;
  z-index: 2;
  padding-top: 0.4rem;
  padding-bottom: 0.4rem;
  background: var(--page);
  border-bottom: 1px solid var(--border);
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-secondary);
}

/* La celda numerica alineada a la derecha y con cifras de ancho fijo: una columna de montos
   que no alinea no se puede comparar de un vistazo, que es para lo que existe la tabla. */
.vpv-num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* CUENTA SOCIO: visualmente distinta de los medios que SI son plata. No es un color de
   error -- no esta mal que exista -- sino la misma familia "esto es otra cosa" que ya usa
   .caja-desglose-informativo. --text-secondary sobre el fondo real da 7,50:1 (medido). */
.vpv-cuenta-socio,
.vpv-grid-encabezado .vpv-cuenta-socio {
  color: var(--text-secondary);
  font-style: italic;
}

/* La fila "(sin vendedor)": tampoco es una persona. Se marca igual que cuenta socio para no
   inventar un tercer lenguaje visual, y va siempre ultima (lo decide la libreria). */
.vpv-sin-vendedor .vpv-nombre {
  color: var(--text-secondary);
  font-style: italic;
}

.vpv-grid-totales {
  border-top: 2px solid var(--border);
  padding-top: 0.5rem;
  margin-top: 0.25rem;
  font-weight: 700;
}

/* El boton de abrir y el desglose diario viven DENTRO de la fila-grilla, asi que sin esto
   caerian cada uno en una celda de medio y romperian la alineacion de las columnas. Ocupan
   la fila entera, debajo de los numeros. */
.vpv-fila-extra {
  grid-column: 1 / -1;
}

/* Desglose diario: mismas columnas, un escalon adentro para que se lea como hijo de su fila
   y no como otra fila de vendedor. */
.vpv-dia-fila {
  font-size: 0.9rem;
  color: var(--text-secondary);
  padding-top: 0.2rem;
  padding-bottom: 0.2rem;
}

.vpv-detalle {
  border-left: 2px solid var(--border);
  margin-left: 0.6rem;
}

/* Boton de exportar (2026-08-31): hereda .boton-secundario y solo separa del "Volver" que
   viene despues, para que no se toquen y alguien exporte queriendo salir. */
.boton-exportar {
  margin-right: 0.5rem;
}

/* Productos vendidos (2026-08-31). Columnas EXPLICITAS via --pv-grid-cols, escritas desde la
   MISMA lista que los titulos (COLUMNAS_PV en app.js) -- nunca auto-fit minmax(): con auto-fit
   dos filas pueden resolver anchos distintos y los numeros dejan de alinearse, que es lo unico
   que permite verificar una suma con la vista. */
li.fila-catalogo.fila-productos-vendidos {
  display: grid;
  grid-template-columns: var(--pv-grid-cols);
  gap: 0.35rem 1rem;
  align-items: baseline;
}

.pv-num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.pv-nombre {
  min-width: 0;
  overflow-wrap: anywhere;
}

.pv-encabezado {
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  color: var(--text-secondary);
}

.pv-totales {
  font-weight: 700;
  border-top: 2px solid var(--border);
  margin-top: 0.25rem;
}

.pv-orden {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin: 0.75rem 0 0;
}

.pv-orden-etiqueta {
  font-size: 0.85rem;
  color: var(--text-secondary);
}

/* El boton de orden activo, con el mismo verde de marca que .boton-nav.active. */
.pv-orden .boton-secundario[aria-pressed="true"] {
  background: var(--brand);
  color: #fff;
  border-color: var(--brand);
}

/* En celular: nombre en su propia fila y las tres cifras debajo, alineadas entre si. Tres
   columnas de numeros mas un nombre no entran en 390px. */
@media (max-width: 699.98px) {
  li.fila-catalogo.fila-productos-vendidos {
    grid-template-columns: 1fr auto auto;
    gap: 0.15rem 0.75rem;
  }
  .fila-productos-vendidos .pv-nombre,
  .fila-productos-vendidos > span:first-child {
    grid-column: 1 / -1;
  }
}

/* Detalle de una venta en el Historial (2026-08-31). MISMO patron visual que .vpv-detalle:
   escalon a la izquierda para que se lea como hijo de su fila y no como otra venta.
   `grid-column: 1 / -1` porque la fila del historial es una grilla y sin eso el detalle se
   metería en la primera columna. */
.hv-fila-extra {
  grid-column: 1 / -1;
}

.hv-detalle {
  grid-column: 1 / -1;
  border-left: 2px solid var(--border);
  margin-left: 0.6rem;
  padding-left: 0.75rem;
}

/* Cada item: nombre | cantidad × precio | subtotal. Columnas EXPLICITAS, nunca auto-fit:
   con varios items los subtotales tienen que quedar alineados entre si para poder sumarlos
   con la vista, que es lo unico que hace util un detalle. */
.hv-item {
  --hv-item-cols: 1fr auto 6.5rem;
  display: grid;
  grid-template-columns: var(--hv-item-cols);
  gap: 0.15rem 0.75rem;
  align-items: baseline;
  padding: 0.3rem 0;
  font-size: 0.9rem;
}

.hv-item + .hv-item {
  border-top: 1px solid var(--gridline);
}

/* min-width:0 para que un nombre largo se encoja en vez de desbordar: un item de grid no baja
   de su min-content sin esto (mismo motivo que en .caja-desglose-etiqueta). */
.hv-item-nombre {
  min-width: 0;
  overflow-wrap: anywhere;
}

/* Un id en vez de un nombre se muestra, pero se ve DISTINTO: en monoespaciado y atenuado, para
   que nadie lo lea como el nombre de un producto. --text-secondary sobre --surface-1 da 7,50:1
   MEDIDO, muy por encima del 4,5:1 de AA. */
.hv-item-sin-nombre {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.82rem;
  color: var(--text-secondary);
}

.hv-item-cantidad {
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}

.hv-item-subtotal {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* Los modificadores cuelgan del item, ocupando la fila entera y con sangria propia. */
.hv-item-modificador {
  grid-column: 1 / -1;
  padding-left: 1rem;
  font-size: 0.85rem;
  color: var(--text-secondary);
}

/* En celular el item se apila: tres columnas de numeros en 390px no se leen. */
@media (max-width: 699.98px) {
  .hv-item {
    grid-template-columns: 1fr auto;
  }
  .hv-item-nombre {
    grid-column: 1 / -1;
  }
}

/* Compras: lista de Compras registradas (Tanda B -- Trazabilidad) -- MISMO patron grid
   (.fila-catalogo + .grid-celda) que Maestro de Productos/Historial de Ventas, NUNCA
   <table> (ver nota en index.html). Selector calificado "li.fila-catalogo.claseGrid" por
   el mismo motivo ya documentado arriba (".fila-catalogo { display:flex }" empata en
   especificidad con una clase sola). */
li.fila-catalogo.fila-compra-registrada {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
  gap: 0.5rem 1rem;
  align-items: start;
}

/* Detalle de items (boton "Ver detalle"): ocupa el ANCHO COMPLETO de la fila resumen --
   grid-column:1/-1 evita que se vuelva una columna mas del grid de arriba. */
.compra-detalle-items {
  grid-column: 1 / -1;
  margin-top: 0.5rem;
  padding-top: 0.5rem;
  border-top: 1px solid var(--border);
}

.compra-detalle-items ul {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  padding: 0;
  margin: 0;
}

li.fila-catalogo.fila-compra-detalle-item {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(84px, 1fr));
  gap: 0.4rem 0.8rem;
  background: var(--surface-1);
  padding: 0.4rem 0.6rem;
}

/* Pantalla Pedidos -- sub-paso A3a (canal de pedidos IA). */
#pedidos-barra-estado {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin-bottom: 0.6rem;
}

/* 10/09/2026: --good -> --good-texto. --good como letra da 3,27:1 (ver :root). Esta clase la
   usan Pedidos IA (oculta para todos los tenants) y Pedidos externos, y "En vivo" es texto de
   0,85rem: no califica como texto grande. */
.pedidos-estado-conexion {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--good-texto);
}

/* .pedidos-estado-desconectado: snapshot.metadata.fromCache===true -- viendo datos
   locales/desactualizados, no un push real del servidor (ver iniciarListenerPedidos,
   app.js).
   >>> 10/09/2026: --warning -> --warning-texto. <<< Era --warning (#d98a00, 2,70:1), el token
   que esta en el backlog por NO pasar contraste como letra. La pantalla de Pedidos externos
   reusa esta clase, y un elemento nuevo no puede nacer heredando un token defectuoso
   (CLAUDE.md): se arregla aca, con el token medido que ya existe (#8f5400, 5,95:1). Hasta ese
   dia esta clase solo la usaba la pantalla de Pedidos IA, oculta para todos los tenants
   (modulosActivos.pedidosIA ausente en puerto-coco y mango-bike, leido en produccion el 09/09/2026). */
.pedidos-estado-desconectado {
  color: var(--warning-texto);
}

/* ===================== Pedidos externos (10/09/2026) ===================== */
/* Ambar = "esperando caja". SIEMPRE --warning-texto sobre --warning-bg, NUNCA --warning como
   letra (ver arriba). Contraste de esa pareja ya medido en .rp-porque: 5,45:1. */
.pext-barra-estado {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 0.75rem;
}

.pext-contador {
  padding: 0.2rem 0.6rem;
  border: 1px solid var(--warning-texto);
  border-radius: 999px;
  background: var(--warning-bg);
  color: var(--warning-texto);
  font-size: 0.85rem;
  font-weight: 700;
}

.pext-item {
  list-style: none;
  margin-bottom: 0.5rem;
}

/* La hora va en `auto` y NO en un ancho fijo: con 4rem, "14:11" (unos 40px) dejaba un hueco
   y en 390px el nombre se apilaba en 3-4 renglones (capturas del 10/09). */
.pext-item-boton {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  padding: 0.7rem 0.8rem;
  border: 1px solid var(--warning-texto);
  border-left-width: 4px;
  border-radius: var(--radius-card);
  background: var(--warning-bg);
  color: var(--text-primary);
  text-align: left;
  cursor: pointer;
}

.pext-item-boton[aria-pressed="true"] {
  box-shadow: 0 0 0 2px var(--warning-texto);
}

.pext-item-hora {
  font-weight: 700;
  color: var(--warning-texto);
}

.pext-item-total {
  font-weight: 700;
}

/* Un pedido de un dia anterior: el borde se engruesa Y lleva la fecha escrita. No depende de
   distinguir un tono -- un pedido de anteayer mezclado con los de hoy es peor que no verlo. */
.pext-item-otro-dia .pext-item-boton {
  border-left-width: 10px;
}

.pext-item-marca-dia {
  grid-column: 1 / -1;
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--warning-texto);
}

/* En celular (mismo corte que .rp-bolsillos): hora y total arriba, y el "vendedor · cliente"
   debajo con TODO el ancho. En tres columnas, a 390px el nombre quedaba con ~130px y se
   apilaba en 3-4 renglones al lado de la hora (capturas del 10/09). */
@media (max-width: 640px) {
  .pext-item-boton {
    grid-template-columns: 1fr auto;
    grid-template-areas: "hora total" "quien quien" "marca marca";
    row-gap: 0.2rem;
  }
  .pext-item-hora { grid-area: hora; }
  .pext-item-total { grid-area: total; }
  .pext-item-quien { grid-area: quien; }
  .pext-item-marca-dia { grid-area: marca; }
}

.pext-detalle {
  margin-top: 1rem;
  padding: 1rem;
  border: 1px solid var(--gridline);
  border-radius: var(--radius-card);
  background: var(--surface-1);
}

.pext-datos {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.25rem 0.75rem;
  margin: 0 0 0.75rem;
}

.pext-datos dt {
  color: var(--text-secondary);
  font-weight: 600;
}

.pext-datos dd {
  margin: 0;
}

.pext-item-linea {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.15rem 0.75rem;
  padding: 0.35rem 0;
  border-bottom: 1px solid var(--gridline);
}

.pext-item-modificador {
  grid-column: 1 / -1;
  padding-left: 1rem;
  font-size: 0.85rem;
  color: var(--text-secondary);
}

.pext-item-subtotal {
  font-weight: 600;
}

.pext-total {
  margin: 0.75rem 0;
  text-align: right;
}

.pext-aviso-precio {
  margin: 0.75rem 0;
  padding: 0.75rem 0.9rem;
  border: 1px solid var(--warning-texto);
  border-radius: var(--radius-card);
  background: var(--warning-bg);
  color: var(--warning-texto);
}

.pext-aviso-precio-titulo {
  margin: 0 0 0.4rem;
  font-weight: 700;
}

.pext-aviso-precio-linea {
  margin: 0.15rem 0;
}

.pext-acciones {
  display: flex;
  gap: 0.6rem;
  margin: 0.75rem 0;
}

/* El RESULTADO de un pedido resuelto, en el lugar donde estaban las acciones. Reusa
   .caja-veredicto (el veredicto del arqueo, mas abajo): texto en --text-primary y el acento en
   el borde y el fondo, que es donde un color de acento funciona. Aca solo los tres tonos. */
.caja-veredicto.pext-resultado-confirmado { background: var(--good-bg); border-left-color: var(--good); }
.caja-veredicto.pext-resultado-rechazado { background: var(--page); border-left-color: var(--muted); }
.caja-veredicto.pext-resultado-aviso { background: var(--warning-bg); border-left-color: var(--warning); }

.pedidos-alerta-visual {
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--brand);
  animation: pedidos-parpadeo 1s ease-in-out infinite;
}

@keyframes pedidos-parpadeo {
  50% { opacity: 0.35; }
}

.pedido-tarjeta {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  padding: 0.7rem 0.6rem;
  border-bottom: 1px solid var(--gridline);
  border-radius: 8px;
}

.pedido-tarjeta:hover {
  background: #f9f8f4;
}

.pedido-tarjeta-encabezado {
  font-weight: 700;
}

.pedido-tarjeta-items {
  list-style: none;
  margin: 0;
  padding: 0;
  color: var(--text-secondary);
  font-size: 0.9rem;
}

.pedido-tarjeta-acciones {
  display: flex;
  gap: 0.4rem;
  flex-wrap: wrap;
}

/* Aviso global persistente (sub-paso A3b-fix2, Hallazgo B) -- fixed, arriba de todo lo
   demas (z-index alto, por encima de .barra-lateral que usa 200) -- visible sea cual sea
   la pantalla activa, se cierra SOLO con el boton "x" (nunca por codigo). */
.aviso-global {
  position: fixed;
  left: 50%;
  bottom: 1rem;
  transform: translateX(-50%);
  z-index: 500;
  max-width: min(90vw, 480px);
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.75rem 1rem;
  background: var(--critical);
  color: #fff;
  border-radius: var(--radius-control);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
}

.aviso-global button {
  background: transparent;
  color: #fff;
  padding: 0 0.4rem;
  font-size: 1.2rem;
  font-weight: 700;
  line-height: 1;
}

.aviso-global button:hover {
  background: rgba(255, 255, 255, 0.15);
}

/* ===== Ingreso rapido de mercaderia en Compras (ronda 2026-08-24) =====
   El caso real: llega TODOS LOS DIAS la misma lista (~150 helados en ~17 sabores), el
   conjunto es fijo y solo cambian las cantidades. Todo lo de abajo existe para que se pueda
   teclear una columna de numeros sin soltar el teclado y cuadrar el total antes de guardar.
   Ningun token de color nuevo: --warning es el mismo ambar de "esto necesita tu atencion" que
   ya usan las alertas de stock, y --good el mismo verde que el resto de la app. */

.compra-aviso {
  margin: 0.5rem 0;
  padding: 0.6rem 0.75rem;
  border-radius: 10px;
  background: var(--page);
  font-size: 0.88rem;
}

.compra-aviso-atencion {
  border-left: 3px solid var(--warning);
}

/* Cantidad SUGERIDA: la puso el sistema, no una persona. Es la contraparte obligatoria de
   precargar cantidades -- sin esta marca, el borrador registraria mercaderia que nunca llego.
   Se le va sola apenas el usuario edita la celda. */
input.compra-cantidad-sugerida {
  background: var(--warning-bg);
  border-color: var(--warning);
}

/* Costo sugerido: MISMA familia visual, un escalon mas abajo de intensidad -- es una marca,
   no un freno. Los costos del proveedor casi no cambian, asi que no pide confirmacion. */
input.compra-costo-sugerido {
  border-color: var(--warning);
}

/* Precio de venta TOCADO por el usuario: queda resaltado hasta guardar, para que al terminar
   vea de un vistazo que precios movio. Cambiar un precio de venta sin darse cuenta es un
   error caro y silencioso: se vende barato hasta que alguien lo nota. */
input.compra-precio-cambiado {
  border-color: var(--good);
  font-weight: 700;
}



/* ===== Rediseño de la grilla de Compras: UNA LINEA POR ITEM (2026-08-25) =====
   Alejandro uso la pantalla del ingreso rapido y pidio el cambio: cada item ocupaba DOS
   renglones y el segundo envolvia, asi que 18 items eran 36 bloques.

   NO se usa <table>: la app no usa esa etiqueta en ningun lado y las dos notas que lo
   documentan (index.html, en Compras y en Maestro de Productos) siguen vigentes. La
   alineacion se resuelve con CSS grid.

   >>> FUENTE UNICA DE COLUMNAS <<<
   --compra-grid-cols se declara UNA SOLA VEZ, aca abajo, y la consumen los TRES bloques:
   el encabezado (.compra-grid-encabezado), cada fila de item (.fila-compra-item) y la fila
   de totales (.compra-grid-totales). CAMBIARLA MUEVE LAS TRES A LA VEZ -- que es el punto.
   Si ajustas el ancho de una columna en uno solo de los tres bloques, la grilla se
   desalinea y nadie se entera hasta que un numero queda bajo la columna equivocada. Una
   definicion, tres consumidores (mismo principio de fuente unica que L1/L4). */
.compra-grid {
  --compra-grid-cols: minmax(9rem, 2.2fr) 5.5rem 4.5rem 6.5rem 7rem 7rem 2rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  /* max-height + overflow-y: es lo que hace que el sticky del encabezado y el de los totales
     tengan contra que pegarse. Sin contenedor scrolleable, `position: sticky` no hace nada. */
  max-height: 26rem;
  overflow-y: auto;
  overflow-x: auto;
  margin: 0.75rem 0;
  background: var(--surface-1);
}

.compra-grid-encabezado,
.compra-grid-totales,
.compra-grid .fila-compra-item {
  display: grid;
  grid-template-columns: var(--compra-grid-cols);
  gap: 0.5rem;
  align-items: center;
  min-width: 44rem;
  padding: 0.4rem 0.6rem;
}

/* Encabezados STICKY: con 18 renglones, en la fila 14 nadie se acuerda cual columna era cual. */
.compra-grid-encabezado {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--page);
  border-bottom: 1px solid var(--border);
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-secondary);
}

.compra-grid-cuerpo {
  list-style: none;
  margin: 0;
  padding: 0;
}

.compra-grid .fila-compra-item {
  border-bottom: 1px solid var(--gridline);
}

/* Fila con cantidad 0: se atenua. El subtotal muestra "--", no "$0" -- "todavia no hay
   subtotal" es distinto de "el subtotal es cero". */
.compra-grid .fila-compra-item.fila-sin-cantidad {
  opacity: 0.55;
}

/* Totales STICKY al borde inferior, cada numero bajo SU columna. Con 40 renglones tiene que
   seguir visible sin scrollear -- es el numero contra el que se cuadra la factura. */
.compra-grid-totales {
  position: sticky;
  bottom: 0;
  z-index: 2;
  background: var(--page);
  border-top: 1px solid var(--border);
}

.compra-grid-totales .num strong {
  display: block;
  font-size: 1.15rem;
  font-variant-numeric: tabular-nums;
}

.compra-grid-totales .num small,
.compra-grid-totales small {
  display: block;
  font-size: 0.7rem;
  color: var(--text-secondary);
}

.compra-totales-etiqueta {
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-secondary);
}

.compra-grid .num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.compra-grid input {
  width: 100%;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* El NOMBRE deja de ser un campo: el item ya esta decidido, se muestra como texto. */
.compra-fila-nombre {
  font-weight: 600;
  overflow-wrap: anywhere;
}

/* El TIPO se marca SOLO cuando es insumo. La mayoria son productos: lo normal no necesita
   cartel, y 18 etiquetas que dicen todas lo mismo son 18 cosas que el ojo tiene que descartar. */
.compra-fila-tipo-insumo {
  margin-left: 0.35rem;
  padding: 0.05rem 0.35rem;
  border-radius: 999px;
  background: var(--page);
  border: 1px solid var(--border);
  font-size: 0.65rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-secondary);
  white-space: nowrap;
}

/* "antes $X": reemplaza a los textos estaticos "Costo anterior"/"Precio actual", que repetian
   el valor que el campo de al lado ya mostraba. Aparece SOLO en la fila que se toca, y solo
   mientras esta cambiada -- una fila crece un renglon, no las dieciocho. */
.compra-fila-antes {
  display: block;
  font-size: 0.68rem;
  color: var(--warning);
  text-align: right;
}

/* Avisos de NIVEL 2 ("comprando muy poco", "el costo cambio mucho").
   display:block y sin texto cuando no hay nada que decir: la fila solo crece de alto en el
   renglon que efectivamente tiene una advertencia, no en los otros trece.
   NO bloquean nada -- son ayudas, y por eso van en el mismo ambar de --warning que el resto
   de las advertencias de esta pantalla, no en el rojo de --critical, que aca significa
   "anulado". */
.compra-fila-aviso {
  display: block;
  font-size: 0.68rem;
  line-height: 1.25;
  color: var(--warning-texto);
  text-align: right;
}

/* ===================== Reporte del periodo (2026-08-28) ===================== */

.rp-frescura {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  margin: 0.75rem 0 0;
}

/* La hora de la ultima lectura. --text-secondary sobre --surface-1: 7,50:1 MEDIDO, muy por
   encima del 4,5:1 de AA. Es informacion de segundo plano, pero legible: si nadie la lee, el
   boton "Actualizar" de al lado no tiene sentido. */
.rp-leido-en {
  font-size: 0.85rem;
  color: var(--text-secondary);
}

.rp-bloque {
  margin-top: 1.5rem;
}

.rp-bloque h3 {
  margin-bottom: 0.35rem;
}

.rp-subtitulo {
  margin: 0 0 0.75rem;
  font-size: 0.9rem;
  color: var(--text-secondary);
  line-height: 1.35;
}

/* La cuenta del bloque 1: concepto a la izquierda, monto a la derecha. Columnas EXPLICITAS
   (--rp-cuenta-cols), no auto-fit minmax(): con auto-fit, dos filas de la misma cuenta pueden
   resolver anchos distintos y los montos dejan de alinearse -- que es lo unico que hace legible
   una resta encolumnada. */
.rp-cuenta {
  --rp-cuenta-cols: 1fr auto;
  display: grid;
  grid-template-columns: var(--rp-cuenta-cols);
  gap: 0.35rem 1rem;
  max-width: 30rem;
  align-items: baseline;
}

.rp-cuenta .rp-monto {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* La linea del resultado: separada por una regla, como una cuenta escrita a mano. */
.rp-cuenta .rp-total-etiqueta,
.rp-cuenta .rp-total-monto {
  border-top: 2px solid var(--border);
  padding-top: 0.35rem;
  margin-top: 0.15rem;
  font-weight: 700;
  font-size: 1.05rem;
}

.rp-bolsillos {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
}

/* Una sola columna en celular: dos tarjetas de numeros lado a lado en 360px no se leen. */
@media (max-width: 640px) {
  .rp-bolsillos {
    grid-template-columns: 1fr;
  }
}

.rp-bolsillo {
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: 0.85rem;
  background: var(--surface-1);
}

.rp-bolsillo h4 {
  margin: 0 0 0.15rem;
}

.rp-bolsillo-subtitulo {
  display: block;
  font-size: 0.78rem;
  color: var(--text-secondary);
  margin-bottom: 0.6rem;
}

/* Los renglones del bolsillo: MISMAS columnas explicitas para los tres (etiqueta, cantidad,
   monto), asi las cifras de las dos tarjetas quedan alineadas entre si. */
.rp-renglon {
  --rp-renglon-cols: 1fr auto 6.5rem;
  display: grid;
  grid-template-columns: var(--rp-renglon-cols);
  gap: 0.25rem 0.6rem;
  align-items: baseline;
  padding: 0.2rem 0;
  font-size: 0.9rem;
}

.rp-renglon .rp-cantidad {
  font-size: 0.78rem;
  color: var(--text-secondary);
}

.rp-renglon .rp-monto {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* El neto, EN PALABRAS. Sin rojo y sin signo negativo a proposito: "salio mas de lo que entro"
   es el mismo hecho dicho como lo diria una persona, y un menos rojo se lee como "error" cuando
   en realidad puede ser una carga de inventario perfectamente sana. */
.rp-neto {
  border-top: 1px solid var(--border);
  margin-top: 0.5rem;
  padding-top: 0.5rem;
}

.rp-neto-frase {
  display: block;
  font-size: 0.88rem;
  color: var(--text-secondary);
}

.rp-neto-monto {
  display: block;
  font-weight: 700;
  font-size: 1.1rem;
  font-variant-numeric: tabular-nums;
}

/* El aporte del dueño: linea aparte y punteada, para que NO se lea como un tercer bolsillo. */
.rp-aporte {
  margin-top: 1rem;
  padding: 0.6rem 0.75rem;
  border: 1px dashed var(--border);
  border-radius: var(--radius-card);
  font-size: 0.9rem;
  line-height: 1.35;
  color: var(--text-primary);
}

.rp-aporte .rp-monto {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* El recuadro que explica POR QUE ganar y tener plata no dan lo mismo.
   --warning-texto (#8f5400), NUNCA --warning (#d98a00, 2,70:1 -- sirve de borde, no de letra).
   Fondo: --warning-bg, el token que ya existe, no un ambar nuevo inventado aca (L4).
   CONTRASTE MEDIDO, no mirado: --warning-bg sobre --surface-1 compone rgb(249,241,226), y
   #8f5400 sobre eso da 5,45:1 -- pasa AA de texto normal (4,5:1) con margen. */
.rp-porque {
  margin-top: 1rem;
  padding: 0.75rem 0.9rem;
  border: 1px solid var(--warning-texto);
  border-radius: var(--radius-card);
  background: var(--warning-bg);
  color: var(--warning-texto);
}

.rp-porque h4 {
  margin: 0 0 0.5rem;
  font-size: 0.95rem;
}

.rp-porque ul {
  margin: 0;
  padding-left: 1.1rem;
  font-size: 0.9rem;
  line-height: 1.45;
}

.rp-porque li + li {
  margin-top: 0.4rem;
}

.rp-pie {
  margin-top: 1.5rem;
  font-size: 0.85rem;
  line-height: 1.4;
  color: var(--text-secondary);
}

/* Aviso de caja de otro dia (2026-08-28) -- en el POS y en Caja, PERSISTENTE.
   Usa `--warning-texto` (#8f5400, 5,95:1 medido sobre el fondo real), NUNCA `--warning`, que
   esta en el backlog por dar 2,70:1. El fondo es el mismo tono en version muy clara: el aviso
   tiene que verse como un aviso sin gritar -- no esta pasando nada malo, hay algo que ordenar.
   El boton va al lado del texto y no debajo: un aviso que dice que hagas algo tiene que dejarte
   hacerlo desde ahi. */
.aviso-caja-vieja {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.75rem;
  margin-bottom: 0.75rem;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--warning-texto);
  border-radius: var(--radius-card);
  background: #fdf6e9;
  color: var(--warning-texto);
  font-size: 0.9rem;
  line-height: 1.35;
}

.aviso-caja-vieja span {
  flex: 1 1 16rem;
}

.aviso-caja-vieja button {
  flex: 0 0 auto;
}

/* LA UNIDAD DEL ITEM, pegada al nombre (NIVEL 1, 2026-08-26).
   Va en la columna del nombre a proposito: es la UNICA flexible (2.2fr) de --compra-grid-cols,
   asi que puede crecer sin desalinear las otras seis. Las otras son de ancho fijo y meter
   texto ahi obligaria a mover la definicion compartida, que consumen encabezado, filas y pie. */
.compra-fila-unidad {
  margin-left: 0.35rem;
  font-size: 0.72rem;
  color: var(--text-secondary);
  white-space: nowrap;
}


/* >>> LOS BOTONES DEL SPINNER SE OCULTAN EN EL CAMPO DE CANTIDAD. <<<
   MEDIDO 2026-08-26: el input mide 88px y las flechitas nativas se comen ~20px, asi que
   "1000" entra pero "125000" queda cortado mientras se teclea. Y 125.000 es exactamente una
   cantidad en g o en ml -- el caso que NIVEL 1 existe para proteger. Un campo que esconde los
   digitos justo en ese escenario es el MISMO defecto que la unidad invisible, no uno aparte.

   Se ocultan los BOTONES, no se rompe el control: las flechas del teclado, la rueda del
   mouse, min/step y la validacion de type="number" siguen intactas -- esto es puramente
   visual. Nadie llega a 1.000 haciendo clic en una flechita, asi que no se pierde nada.

   Solo el campo de CANTIDAD: el de costo mide 104px y ahi "198462" ya entraba completo
   (medido). No se toca lo que no tiene el problema. */
.compra-grid .fila-compra-item .compra-campo-cantidad::-webkit-outer-spin-button,
.compra-grid .fila-compra-item .compra-campo-cantidad::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.compra-grid .fila-compra-item .compra-campo-cantidad {
  -moz-appearance: textfield;
  appearance: textfield;
}

/* >>> EL PLACEHOLDER DE ESTA GRILLA LLEVA INFORMACION, NO ES DECORATIVO. <<<
   Dice la unidad ("ml", "por ml") dentro del campo vacio, que es el estado en que esta justo
   antes de teclear -- y en un item que se compra por primera vez es la unica senal que hay.
   El gris por defecto del navegador (rgb(117,117,117)) da 4.49:1 MEDIDO contra el fondo de la
   fila: queda 0.01 por debajo del umbral AA de 4.5:1. Con --text-secondary da 7.50:1.
   opacity: 1 es necesario porque Firefox le aplica opacidad propia al placeholder. */
.compra-grid .fila-compra-item input::placeholder {
  color: var(--text-secondary);
  opacity: 1;
}

/* "Quitar" pasa a ser una x: repetido 18 veces, ese boton competia con los numeros, que es lo
   unico que importa en esta pantalla. */
.compra-boton-quitar {
  background: none;
  border: 0;
  color: var(--text-secondary);
  font-size: 1rem;
  line-height: 1;
  padding: 0.25rem;
  cursor: pointer;
  border-radius: var(--radius-control);
}

.compra-boton-quitar:hover {
  color: var(--critical);
  background: var(--critical-bg);
}

/* Columna "Queda" con marca de SALTO: si la cantidad es >= 5 veces la sugerida, el numero se
   pinta. Atrapa el "150 donde iban 15", que hoy recien aparece un mes despues cuando el
   inventario no cuadra. NO bloquea nada. */
.compra-queda-salto {
  color: var(--warning);
  font-weight: 700;
  cursor: help;
}

/* El total calculado del gasto (cantidad x valor unitario). Es INFORMACION, no una
   advertencia: usa --text-secondary (7,50:1 medido) y no el ambar, que en esta app significa
   "revisá esto". */
/* >>> UN CAMPO BLOQUEADO TIENE QUE VERSE BLOQUEADO. <<<
   Con valor unitario cargado, el Monto lo manda la cuenta y el input se deshabilita. Sin este
   estilo se veia IDENTICO a uno editable -- mismo fondo, mismo color, misma opacidad, medido --
   asi que alguien iba a intentar escribir ahi y no iba a pasar nada, sin ninguna pista de por que.

   ACOTADO AL FORMULARIO DE GASTOS a proposito. La app no tiene NINGUN estilo :disabled para
   inputs, asi que el problema existe tambien en Compras (medio de pago), Productos (stock minimo)
   y Conteo -- pero Compras esta congelada y una regla global le cambiaria el aspecto. Queda en el
   backlog para resolverlo de una vez cuando se descongele. */
#form-gasto input:disabled {
  background: var(--page);
  color: var(--text-secondary);
  cursor: not-allowed;
}

.gasto-monto-calculado {
  display: block;
  margin-top: 0.25rem;
  font-size: 0.85rem;
  color: var(--text-secondary);
}

/* Renglones del gasto (14/09/2026, gastos por renglones pieza 2). OPCIONALES: sin renglones la
   seccion es un recuadro con su titulo y el boton "Agregar renglón", nada mas. */
.gasto-renglones {
  margin: 0;
  padding: 0.6rem 0.75rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
}

/* El titulo con el MISMO aspecto que los <label> del formulario: es un campo mas. */
.gasto-renglones legend {
  padding: 0 0.25rem;
  font-weight: 600;
  font-size: 0.85rem;
  color: var(--text-secondary);
}

.gasto-renglones-lista {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.gasto-renglones-lista:not(:empty) {
  margin-bottom: 0.6rem;
}

/* Un renglon: Item | Cantidad | Costo unitario | Total | x, y el aviso abajo, de ancho completo.
   El Total va con ANCHO FIJO, no `auto`: cada renglon es su propia grilla, y con `auto` un renglon
   con "—" (incompleto) o con un total largo corria sus columnas respecto de los otros (se vio en la
   foto a 390). 6rem entra "$1.234.567". */
.gasto-renglon {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr) 6rem auto;
  gap: 0.4rem 0.6rem;
  align-items: end;
  padding-bottom: 0.6rem;
  border-bottom: 1px solid var(--gridline);
}

.gasto-renglon-campo {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  min-width: 0;
}

.gasto-renglon-campo > span {
  font-size: 0.72rem;
}

/* El total es texto, no un campo: se alinea con el texto de los inputs (su mismo alto de linea y
   padding vertical) para que la fila se lea de corrido. */
.gasto-renglon-total > span {
  font-weight: 600;
  color: var(--text-secondary);
}

.gasto-renglon-total output {
  padding: 0.7rem 0;
  font-size: 1rem;
  font-weight: 600;
  color: var(--text-primary);
  white-space: nowrap;
}

.gasto-renglon-quitar {
  align-self: center;
  font-size: 1.25rem;
  padding: 0.35rem 0.5rem;
}

.gasto-renglon-aviso {
  grid-column: 1 / -1;
  margin: 0;
}

/* A 390 px cinco columnas no entran: el item ocupa su renglon entero (con la x al lado) y abajo
   van cantidad, costo unitario y total. */
@media (max-width: 699.98px) {
  .gasto-renglon {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 6rem;
  }
  .gasto-renglon-item {
    grid-column: 1 / 3;
    grid-row: 1;
  }
  .gasto-renglon-quitar {
    grid-column: 3;
    grid-row: 1;
    justify-self: end;
  }
  .gasto-renglon-cantidad { grid-column: 1; grid-row: 2; }
  .gasto-renglon-unitario { grid-column: 2; grid-row: 2; }
  .gasto-renglon-total { grid-column: 3; grid-row: 2; }
  .gasto-renglon-aviso { grid-row: 3; }
  /* El detalle en la lista, 2 x 2 (Item | Cantidad / Costo unitario | Total): con el auto-fit del
     detalle de compra quedaban 3 + 1 y el Total solo, abajo a la izquierda (foto a 390). Acotado a
     gastos: el detalle de compra no se toca. */
  .gasto-detalle-renglones li.fila-catalogo.fila-compra-detalle-item {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
}

/* El detalle en la lista: el mecanismo del detalle de compra (.compra-detalle-items) dentro de
   una fila FLEX -- ahi el ancho completo lo da flex-basis; su grid-column no aplica. */
.gasto-detalle-renglones {
  flex-basis: 100%;
}

.gasto-detalle-total {
  margin: 0.5rem 0 0;
  text-align: right;
  font-weight: 600;
}

/* Rangos rapidos del Historial de Ventas: una fila de botones que se envuelve en pantallas
   angostas en vez de desbordar. No son pestañas (.cat-tab) a proposito -- una pestaña
   sugiere un estado que queda activo, y estos LLENAN los campos y se olvidan. */
.hv-rangos-rapidos {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
}

/* Cuadre contra la factura (punto P). */
.compra-cuadre {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin: 0.5rem 0 0.75rem;
}

.compra-cuadre input {
  max-width: 12rem;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.compra-cuadre-veredicto {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.compra-cuadre-veredicto.cuadra {
  color: var(--good);
}

.compra-cuadre-veredicto.difiere {
  color: var(--warning);
}

.compra-cuadre-veredicto.neutro {
  color: var(--text-secondary);
  font-weight: 400;
}

/* ===== Modo conteo sobre Ajuste de stock (2026-08-25) =====
   Reusa .compra-grid del redisenio de Compras -- misma familia visual, mismo sticky, misma
   idea de una definicion de columnas consumida por los tres bloques. Solo se redefine la
   custom property: 6 columnas en vez de 7. */
.conteo-grid {
  --compra-grid-cols: minmax(11rem, 2.8fr) 5.5rem 6rem 6rem 7rem;
}

.conteo-bloque {
  margin-top: 1.5rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border);
}

.conteo-barra,
.conteo-controles,
.conteo-pie {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin: 0.6rem 0;
}

.conteo-sello,
.conteo-progreso {
  font-size: 0.82rem;
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}

.conteo-progreso {
  margin-left: auto;
}

.conteo-filtros {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}

/* Diferencia: baja en rojo, sube en verde. Es lo primero que el ojo busca en la fila. */
.conteo-baja {
  color: var(--critical);
  font-weight: 700;
}

.conteo-sube {
  color: var(--good);
  font-weight: 700;
}

/* Fila sin contar: atenuada, para que las contadas destaquen sobre un fondo de 54 renglones. */
.conteo-fila-sin-contar {
  opacity: 0.6;
}

/* Cantidad tecleada que no es un entero >= 0. Un conteo fisico no puede ser negativo. */
.conteo-invalido {
  border-color: var(--critical);
}

/* Estado tras aplicar, en la FILA ENTERA -- no en una celda "Estado" que antes de aplicar
   esta vacia y solo consume ancho. Mismo vocabulario que .fila-agotado/.fila-bajo-minimo del
   resto de la app: la fila se colorea, no una celda suelta.
   La aplicacion NO es atomica (van uno por uno por ajustarStock*, cada uno con su movimiento),
   asi que la fila fallida tiene que quedar senalada hasta que alguien la resuelva, y ademas
   aparece en la lista de fallos de abajo con su error -- nunca un "listo" generico (L5). */
.fila-conteo-aplicada {
  color: var(--good);
}

.fila-conteo-fallo {
  color: var(--critical);
  font-weight: 700;
  background: var(--critical-bg);
}

/* Motivo arriba, junto a "Cargar items": ENMARCA la tarea. Escrito antes de empezar dice que
   se esta haciendo; pedido al final es un tramite que se apura, y asi es como termina un
   "asdf" grabado para siempre en cada movimiento del ledger. */
.conteo-motivo {
  flex: 1 1 18rem;
  min-width: 12rem;
}

.conteo-motivo-label {
  font-size: 0.82rem;
  color: var(--text-secondary);
}

.conteo-dialogo-motivo {
  font-size: 0.9rem;
  color: var(--text-secondary);
}

.conteo-dialogo-aviso {
  color: var(--warning);
  font-weight: 600;
}

/* Sello de hora de Control de Inventario: acompaña, no compite con los numeros del reporte. */
.ci-sello {
  color: var(--text-secondary);
  font-size: 0.85rem;
}

/* ===== Vuelto en el modal de cobro (2026-08-25) =====
   El vuelto va GRANDE y arriba del boton de cobrar: es lo que la cajera mira con el cliente
   enfrente y la mano en la caja. El campo "Recibe" es chico al lado; el numero que importa es
   el resultado, no lo que se tecleo. */
.pos-vuelto {
  margin: 0.75rem 0;
  padding: 0.75rem;
  border-radius: var(--radius-card);
  background: var(--page);
  text-align: left;
}

.pos-vuelto label {
  display: block;
  font-size: 0.8rem;
  color: var(--text-secondary);
  margin-bottom: 0.25rem;
}

.pos-vuelto input {
  width: 100%;
  text-align: right;
  font-size: 1.1rem;
  font-variant-numeric: tabular-nums;
}

.pos-vuelto-veredicto {
  margin-top: 0.5rem;
  text-align: center;
  font-size: 1.6rem;
  font-weight: 700;
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
  min-height: 1.9rem;
}

/* Vuelto a devolver: verde, es el caso normal y bueno. */
.pos-vuelto-veredicto.sobra { color: var(--good); }

/* Pago justo: mismo verde, texto en vez de un "$0" que se lee como si faltara algo. */
.pos-vuelto-veredicto.exacto {
  color: var(--good);
  font-size: 1.1rem;
}

/* No alcanza: ambar, NO rojo. No es un error del sistema ni algo roto -- es que el cliente
   todavia no termino de pagar, y el cobro sigue estando disponible (puede completar con otro
   medio). El rojo esta reservado para lo que impide seguir. */
.pos-vuelto-veredicto.falta { color: var(--warning); }

/* ===== Cuenta del socio, dentro del Historial de Ventas (2026-08-25) ===== */
.socio-bloque {
  margin: 0.75rem 0;
  padding: 0.9rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  background: var(--page);
}

.socio-bloque h3 { margin: 0 0 0.6rem; font-size: 1rem; }

.socio-cifras {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem;
}

.socio-cifras > div { display: flex; flex-direction: column; }

.socio-etiqueta {
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-secondary);
}

.socio-cifras strong {
  font-size: 1.5rem;
  font-variant-numeric: tabular-nums;
}

.socio-cifras small { font-size: 0.72rem; color: var(--text-secondary); }

/* El saldo es lo que el socio DEBE: ambar cuando hay deuda. Verde cuando esta en cero o a
   favor -- un saldo negativo es un estado real (pago de mas), no un error que esconder. */
.socio-saldo.debe { color: var(--warning); }
.socio-saldo.al-dia { color: var(--good); }

.socio-abono {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.9rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--border);
}

.socio-abono label { font-size: 0.82rem; color: var(--text-secondary); }
.socio-abono input { max-width: 10rem; text-align: right; font-variant-numeric: tabular-nums; }

/* .aviso-desborde: EXTIENDE .aviso -- misma severidad, con fondo y borde para que no pase
   inadvertida. Sin colores nuevos: --warning, --warning-bg y --text-primary ya
   existian. Marca el UNICO caso en que la lista NO es todo lo que hay: la pantalla muestra
   las 200 mas recientes y este cartel dice que quedaron mas afuera. */
.aviso-desborde {
  /* El TEXTO va en --text-primary, no en el ambar de .aviso: medido, el ambar sobre este
     fondo da 2.70:1 y no llega a AA (4.5:1); --text-primary da 16.78:1. El ambar se queda
     donde SI funciona -- el borde y el fondo --, que es lo que da la senal de severidad.
     Sin colores nuevos: las dos variables ya existian. */
  color: var(--text-primary);
  background: var(--warning-bg);
  border-left: 3px solid var(--warning);
  padding: 0.5rem 0.75rem;
  margin: 0 0 0.75rem 0;
  border-radius: 4px;
}

/* ===== EL CAJON (2026-08-26) =====
   Una sola columna con signos explicitos. Los montos van en una columna propia, alineados a
   la derecha y con cifras tabulares: una lista de plata que no alinea obliga a comparar de a
   un digito. Ningun color nuevo -- todo sale de la paleta de :root. */
/* ===== BARRA DE ACCION FIJA DE CAJA (02/09/2026) =====
   "Cerrar caja" vivia al final de la pantalla, despues de hasta 91 movimientos (medido en
   produccion el 30/08). Se queda pegada arriba mientras se hace scroll: la accion principal
   no puede depender de encontrarla.
   `position: sticky` y no `fixed`: sticky respeta el flujo del documento y no tapa contenido
   al final de la pagina, que es el defecto clasico de la barra fija. */
.caja-barra-accion {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  margin: 0 0 1rem;
  padding: 0.6rem 0.75rem;
  background: var(--surface-1);
  border-bottom: 1px solid var(--gridline);
}
.caja-barra-titulo {
  font-weight: 600;
  color: var(--text-secondary);
}

/* EL RESULTADO DEL CIERRE, EN LA BARRA (04/09/2026).
   Auditado sobre produccion: la confirmacion se escribia en el <p> del pie, a 1.074 px del
   tope, con el boton que la provoca a 67 px -- 1.007 px por debajo de donde la persona
   acababa de apretar. Un mensaje sobre una accion tiene que aparecer DONDE OCURRIO LA ACCION.
   El exito usa --good y el error --critical, los mismos tokens que el veredicto del arqueo:
   el mismo hecho no cambia de color segun quien lo diga. */
/* >>> ESTAR EN PANTALLA ES NECESARIO, NO SUFICIENTE. <<< La primera version de estas reglas
   solo movia el mensaje a la barra, con el mismo peso visual que el texto de alrededor. El
   defecto original NO era la distancia: era que la cajera NO LO VEIA. Un mensaje que hay que
   buscar no cumple, aunque este a 47 px del boton. Fondo propio y borde de color -- la misma
   paleta que ya usan el veredicto del arqueo y los avisos de la pantalla, para que el ojo lo
   encuentre sin buscarlo. Un renglon, no un banner. */
.caja-barra-mensaje,
#caja-cerrada-mensaje {
  font-weight: 700;
  font-size: 1rem;
  color: var(--good);
  /* --good-bg / --critical-bg, los tokens QUE YA EXISTEN (lineas 31 y 34) y que usa el
     veredicto del arqueo con este mismo patron fondo + borde izquierdo. No se inventan
     nombres nuevos: una variable CSS inexistente no falla, hereda la nada en silencio. */
  background: var(--good-bg);
  border-left: 4px solid var(--good);
  border-radius: 4px;
  padding: 0.5rem 0.75rem;
  /* La barra es flex con wrap: en angosto el mensaje baja a su propia linea en vez de
     competir con el titulo y el boton por el mismo ancho. */
  flex-basis: 100%;
}
.caja-barra-mensaje-error,
.caja-barra-titulo-error {
  color: var(--critical);
  background: var(--critical-bg);
  border-left-color: var(--critical);
}

/* ===== REPORTES -> CIERRES DE CAJA (05/09/2026) =====
   Una fila por turno, con li.fila-catalogo + crearCeldaGrid -- NUNCA <table>, misma convencion
   que Maestro de Productos, Insumos y Control de Inventario. Cada celda trae su etiqueta arriba
   del valor, asi que NO hay encabezado de columnas que ocultar en angosto: el mismo markup se
   lee igual a 1707 px que a 390 px.
   Columnas EXPLICITAS y la variable en el MISMO scope que la usa -- la regla de layout de la
   casa. NUNCA auto-fit minmax(). */
li.fila-catalogo.cc-fila {
  --cc-grid-cols: 6.5rem 7rem 1fr 1fr 6.5rem 6.5rem 11rem;
  display: grid;
  grid-template-columns: var(--cc-grid-cols);
  gap: 0.5rem 1rem;
  align-items: start;
}
.cc-valor-fecha { font-variant-numeric: tabular-nums; }
li.fila-catalogo.cc-fila .cc-valor-veredicto { font-weight: 700; }

/* >>> LO QUE CUADRO SE APAGA PARA QUE LO QUE NO CUADRO SALTE. <<< La diferencia es la
   protagonista de esta pantalla: si todas las filas pesan igual, no contesta su pregunta
   ("¿cuando NO cuadro?"). El exacto no se oculta -- se atenua. */
li.fila-catalogo.cc-fila-exacta .grid-celda-valor { color: var(--text-secondary); }
li.fila-catalogo.cc-fila-exacta .cc-valor-veredicto { font-weight: 400; }
/* Anterior al corte: se MUESTRA y se marca. El corte la saca de la SUMA, no de la lista --
   ocultarla dejaria el encabezado sin cerrar contra las filas. */
li.fila-catalogo.cc-fila-fuera-del-corte { opacity: 0.7; }
.cc-marca-corte { grid-column: 1 / -1; font-size: 0.78rem; margin: 0.2rem 0 0; }
.arqueo-abierta { color: var(--text-secondary); font-style: italic; }

/* El enlace desde Mis números al detalle turno por turno. */
.mn-enlace {
  background: none;
  border: 0;
  padding: 0;
  margin: 0.2rem 0 0;
  font: inherit;
  font-size: 0.85rem;
  color: var(--good);
  text-decoration: underline;
  cursor: pointer;
}

@media (max-width: 699.98px) {
  /* En angosto, DOS columnas: los siete datos entran de a dos por linea, cada uno con su
     etiqueta encima. La fecha y el veredicto toman la linea entera -- son lo que se busca al
     escanear la lista: cuando fue, y si cuadro. */
  li.fila-catalogo.cc-fila {
    --cc-grid-cols: 1fr 1fr;
    gap: 0.5rem 0.75rem;
  }
  li.fila-catalogo.cc-fila > .grid-celda:first-child,
  li.fila-catalogo.cc-fila > .grid-celda:last-child { grid-column: 1 / -1; }
}

/* DOS CAJAS ABIERTAS A LA VEZ./* DOS CAJAS ABIERTAS A LA VEZ. Va primero en la pantalla y pesa mas que cualquier otro aviso:
   es la unica condicion en que conviene frenar ANTES de vender. Mismos tokens que el resto
   (--critical / --critical-bg), ningun color nuevo. */
.caja-aviso-multiples {
  margin: 0 0 1rem;
  padding: 0.7rem 0.9rem;
  font-weight: 700;
  color: var(--critical);
  background: var(--critical-bg);
  border-left: 4px solid var(--critical);
  border-radius: 4px;
}

/* La fila de detalle por medio de pago, dentro del bloque de ventas: indentada para que se
   lea como parte de la linea de arriba y no como un renglon mas del total. */
.caja-fila-detalle {
  padding-left: 1.5rem;
  font-size: 15px;
  color: var(--text-secondary);
}

/* Un boton apagado tiene que PARECER apagado. Sin esto queda identico al habilitado y el
   unico sintoma es que no pasa nada al apretarlo, que se lee como "la app esta rota". */
.boton-apagado,
button.boton-apagado:hover {
  opacity: 0.45;
  cursor: not-allowed;
}

.caja-desglose {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.9rem 1rem;
  margin-bottom: 1.25rem;
  background: var(--surface-1);
}

.caja-desglose h3 { margin: 0 0 0.6rem 0; }

.caja-desglose-lista { list-style: none; margin: 0; padding: 0; }

.caja-desglose-renglon { border-bottom: 1px solid var(--border); }
.caja-desglose-renglon:last-child { border-bottom: none; }

/* La grilla se define UNA sola vez y la comparten la cabecera de cada renglon y los
   informativos, para que las columnas de monto queden alineadas entre bloques. */
.caja-desglose-cabecera {
  display: grid;
  grid-template-columns: 1fr auto auto auto;
  align-items: center;
  gap: 0.75rem;
  padding: 0.45rem 0;
}

.caja-desglose-etiqueta { color: var(--text-primary); }

/* >>> UN TOTAL SIN CANTIDAD NO SE PUEDE VERIFICAR. <<< La cantidad va en tono secundario:
   acompana al monto, no compite con el. */
.caja-desglose-cantidad {
  color: var(--text-secondary);
  font-size: 0.85rem;
  white-space: nowrap;
}

.caja-desglose-monto {
  font-variant-numeric: tabular-nums;
  text-align: right;
  min-width: 7.5rem;
  color: var(--text-primary);
}

.caja-desglose-abrir,
.caja-desglose-enlace {
  background: none;
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--brand-dark);
  font-size: 0.8rem;
  padding: 0.2rem 0.55rem;
  cursor: pointer;
}
.caja-desglose-abrir:hover,
.caja-desglose-enlace:hover { background: var(--page); }

/* Fila del bloque informativo (2026-08-31): el medio a la izquierda y una linea por direccion.
   NO usa la grilla de .caja-desglose-cabecera -- esa tiene columnas de cantidad y monto pensadas
   para un renglon con UN monto, y aca hay dos frases completas. En celular se apila solo. */
.caja-informativo-renglon {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.2rem 1rem;
  padding: 0.35rem 0;
}

.caja-informativo-detalle {
  font-size: 0.9rem;
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}

.caja-desglose-detalle {
  padding: 0.25rem 0 0.7rem 1.25rem;
  border-left: 2px solid var(--gridline);
  margin-left: 0.25rem;
}
.caja-desglose-detalle-lista {
  list-style: none;
  margin: 0 0 0.5rem 0;
  padding: 0;
  max-height: 14rem;   /* con 63 ventas en un dia, el detalle scrollea adentro y no empuja
                          el total fuera de la pantalla */
  overflow-y: auto;
  font-variant-numeric: tabular-nums;
  font-size: 0.88rem;
  color: var(--text-secondary);
}
.caja-desglose-detalle-lista li { padding: 0.15rem 0; }

/* El total: la linea que la cajera busca. Separada por una regla mas marcada que las de los
   renglones, porque es el resultado, no un item mas de la lista. */
.caja-desglose-total {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  gap: 0.75rem;
  margin: 0.6rem 0 0 0;
  padding-top: 0.6rem;
  border-top: 2px solid var(--text-primary);
  font-size: 1.05rem;
}
.caja-desglose-total strong {
  font-variant-numeric: tabular-nums;
  font-size: 1.25rem;
}

/* NOTA (04/09/2026): aca vivian .caja-desglose-informativo y su -titulo. El markup que las
   usaba se elimino en f983e96 y estas dos reglas quedaron sin nadie que las aplicara.
   Verificado antes de sacarlas: cero ocurrencias en public/ y en test/ fuera de este archivo
   -- la unica restante es la mencion en el comentario de .vpv-cuenta-socio, que sigue siendo
   cierta como referencia historica al criterio de color y no depende de la regla. */

/* ===== EL VEREDICTO DEL ARQUEO =====
   En palabras y con fondo, no un numero con signo. Los tres estados usan el par
   color-de-texto / color-de-fondo que ya existe en la paleta.

   >>> EL TEXTO NO VA EN EL COLOR DE ACENTO. <<< Medido: --warning (#d98a00) sobre el fondo
   de la app da 2.70:1, por debajo de AA. El acento vive en el BORDE y el FONDO, que es
   donde funciona; el texto va en --text-primary (16+:1). Es la misma correccion que ya se
   hizo en .aviso-desborde del historial. */
.caja-veredicto {
  margin: 0.5rem 0;
  padding: 0.5rem 0.8rem;
  border-radius: 6px;
  border-left: 4px solid transparent;
  font-weight: 600;
  color: var(--text-primary);
}
.caja-veredicto.arqueo-exacto { background: var(--good-bg); border-left-color: var(--good); }
.caja-veredicto.arqueo-sobra { background: var(--warning-bg); border-left-color: var(--warning); }
.caja-veredicto.arqueo-falta { background: var(--critical-bg); border-left-color: var(--critical); }

/* No es una advertencia de peligro: es un dato. Va en tono secundario, sin color de alarma,
   porque cerrar la caja es lo normal al final del dia. */
.caja-aviso-irreversible {
  margin: 0.35rem 0 0.6rem 0;
  font-size: 0.82rem;
  color: var(--text-secondary);
}

/* ===== ORIGEN DEL PAGO (2026-08-26) =====
   Tres opciones apiladas, cada una con SU CONSECUENCIA debajo del nombre. El nombre solo no
   alcanza para elegir bien la primera vez: "de fondos propios" no le dice a nadie que el
   cajon no se toca. Sin colores nuevos: --border, --brand y --text-secondary ya existen. */
.compra-origen-grupo {
  display: grid;
  gap: 0.5rem;
  margin-bottom: 1rem;
}
.compra-origen-opcion {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0 0.6rem;
  align-items: start;
  padding: 0.6rem 0.8rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  cursor: pointer;
}
.compra-origen-opcion:hover { background: var(--page); }
/* :has() marca la opcion elegida sin JS. Si el navegador no lo soporta, el radio sigue
   siendo el indicador -- se degrada a la apariencia de siempre, nunca a algo ambiguo. */
.compra-origen-opcion:has(input:checked) {
  border-color: var(--brand);
  background: var(--page);
}
.compra-origen-opcion input { grid-row: 1 / 3; margin-top: 0.2rem; }
.compra-origen-titulo { font-weight: 600; color: var(--text-primary); }
.compra-origen-detalle { font-size: 0.85rem; color: var(--text-secondary); }

/* ============ MÓVIL: la barra deja de compartir el ancho y pasa a ser un panel ============
   (2026-08-30)

   >>> EL PROBLEMA MEDIDO. <<< La barra siempre fue `position: fixed`, pero el `body` llevaba
   `padding-left: 230px` para dejarle lugar. En un celular de 390 px eso es el 59% del ancho, y
   con la preferencia "Fijar barra" en `true` la barra NO se cerraba al navegar: medido el
   30/08/2026, el contenido de Historial de Ventas quedaba en **108 px**. Nunca existio una media
   query que lo corrigiera -- verificado con `git log -S`: no es una regresion, siempre fue asi.

   >>> QUE CAMBIA. <<< Por debajo de 700 px el contenido ocupa SIEMPRE el 100%: la barra no lo
   empuja ni lo encoge. La barra se desliza ENCIMA, con un fondo oscuro detras que la cierra al
   tocarlo. Por encima de 700 px no cambia absolutamente nada.

   El 700 vive en public/js/lib/layout.js (BREAKPOINT_MOVIL_PX) y un test lee ESTE archivo para
   exigir que coincidan. 699.98 es el complemento exacto de las `min-width: 700px` que ya usan
   .pos-layout / .cart-panel / .grilla-mini-charts. */
/* El boton ☰ de movil NO existe en escritorio: ahi sigue mandando el boton original de la
   tarjeta de inicio, que no se toca. Va ANTES de la media query, no despues: con la misma
   especificidad gana la ultima regla, y puesta al final pisaba el display:flex de movil --
   medido el 30/08/2026, el boton quedaba en 0x0 en el celular. */
#boton-menu-movil {
  display: none;
}

@media (max-width: 699.98px) {
  /* 1. El contenido al 100%. Esta regla anula el padding que le hace lugar a la barra. */
  body:has(#pantalla-login[hidden]) {
    padding-left: 0;
  }

  /* 2. La barra, como panel. Sigue siendo fixed y sigue usando la MISMA clase `colapsada` que
     en escritorio -- no hay un segundo mecanismo de apertura que mantener sincronizado. */
  .barra-lateral {
    width: min(82vw, 300px);
    box-shadow: 4px 0 24px rgba(10, 20, 15, 0.35);
  }

  /* 3. Mientras el panel esta abierto, la pagina de atras NO scrollea. Sin esto, arrastrar
     sobre el fondo mueve el contenido que no se puede ver, y al cerrar aparece otra parte de la
     pantalla sin que nadie lo haya pedido. */
  body:has(#pantalla-login[hidden]):has(#barra-lateral:not(.colapsada)) {
    overflow: hidden;
  }

  /* 4. "Fijar barra" NO EXISTE en movil: fijar un panel que tapa el 82% de la pantalla no
     significa nada. Se OCULTA el boton; el valor guardado en localStorage NO se borra ni se
     toca, para que el mismo usuario en computadora conserve su preferencia (app.js simplemente
     no lo consulta cuando el ancho es de movil). */
  .boton-fijar-sidebar {
    display: none;
  }

  /* 5. El boton ☰ de movil, SIEMPRE alcanzable: fijo en pantalla, no se va con el scroll.
     44x44 es el minimo tocable de las guias de accesibilidad de Apple y Google -- por debajo de
     eso el dedo falla y el usuario toca otra cosa.
     z-index 250: por ENCIMA de la barra (200) para poder cerrarla tocandolo, y por encima del
     fondo (150). Lleva fondo y borde propios porque cuando el panel esta abierto queda sobre el
     verde oscuro de la barra. */
  #boton-menu-movil {
    display: flex;
    align-items: center;
    justify-content: center;
    position: fixed;
    top: 0.6rem;
    left: 0.6rem;
    width: 44px;
    height: 44px;
    padding: 0;
    font-size: 1.35rem;
    line-height: 1;
    color: var(--text-primary);
    background: var(--surface-1);
    border: 1px solid var(--border);
    border-radius: var(--radius-control);
    box-shadow: var(--shadow-card);
    z-index: 250;
  }

  /* La cabecera le deja lugar al boton fijo para que no lo tape ni quede encima del titulo. */
  .encabezado {
    padding-left: calc(0.6rem + 44px + 0.6rem);
  }

  /* 7. El desglose de Caja, en DOS filas.
     >>> ES EL UNICO DESBORDE HORIZONTAL QUE APARECIO EN EL BARRIDO. <<< Medido el 30/08/2026:
     de las 11 pantallas en 390 px, 10 daban `scrollWidth - clientWidth = 0` y Caja daba **37**,
     con `BUTTON.caja-desglose-abrir` llegando a right=407 sobre un viewport de 387.
     La causa es preexistente y no la trajo el panel: `.caja-desglose-cabecera` es una grilla de
     CUATRO columnas (etiqueta | cantidad | monto | "Ver detalle") con gap de 0.75rem, y las tres
     `auto` mas la etiqueta no entran en el ancho de un celular. En movil la etiqueta se lleva la
     primera fila entera y las otras tres quedan abajo, que es como se leeria en papel. */
  .caja-desglose-cabecera {
    grid-template-columns: 1fr auto auto;
    gap: 0.15rem 0.5rem;
  }

  .caja-desglose-etiqueta {
    grid-column: 1 / -1;
    /* min-width:0 para que un nombre largo pueda encogerse: un item de grid NO baja de su
       min-content sin esto, y ahi vuelve el desborde por otra puerta. */
    min-width: 0;
    overflow-wrap: anywhere;
  }

  /* 6. El fondo oscuro detras del panel abierto. Tocarlo cierra (app.js). */
  #sidebar-fondo {
    position: fixed;
    inset: 0;
    background: rgba(10, 20, 15, 0.55);
    z-index: 150;
  }
}

/* ==================== MIS NUMEROS (2026-09-01) ====================
   Escrita para una persona mayor. Las tres decisiones que mandan sobre el resto:
     - montos a 33px y botones a 19px (el resto de la app usa 15-16px),
     - area tocable minima de 48px en TODO lo que se pueda clickear,
     - contraste >= 4.5:1, y --warning-texto (#8f5400, 5,95:1 medido) nunca --warning
       (#d98a00, 2,70:1), que esta en el backlog justamente por eso. */

.mis-numeros {
  font-size: 17px;
}

.mn-sucursal {
  margin-bottom: 1rem;
}

/* ---- Los tres periodos ---- */
.mn-periodos {
  display: flex;
  gap: 0.5rem;
  margin-bottom: 1rem;
  flex-wrap: wrap;
}
.mn-periodo {
  flex: 1 1 0;
  min-width: 96px;
  min-height: 48px;
  font-size: 19px;
  font-weight: 600;
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  background: var(--surface-1);
  color: var(--text-primary);
  cursor: pointer;
}
.mn-periodo-activo {
  background: var(--brand);
  border-color: var(--brand);
  color: #fff;
}

/* ---- Las flechas y el rotulo del periodo ---- */
.mn-navegacion {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 1.25rem;
}
.mn-flecha {
  min-width: 48px;
  min-height: 48px;
  font-size: 22px;
  line-height: 1;
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  background: var(--surface-1);
  color: var(--text-primary);
  cursor: pointer;
  flex: 0 0 auto;
}
.mn-rotulo {
  flex: 1 1 auto;
  text-align: center;
}
.mn-titulo-periodo {
  margin: 0;
  font-size: 19px;
  font-weight: 600;
  color: var(--text-primary);
}
.mn-subtitulo-periodo {
  margin: 0.15rem 0 0;
  font-size: 15px;
  color: var(--text-secondary);
}
.mn-cargando {
  margin: 0 0 1rem;
  color: var(--text-secondary);
  font-size: 16px;
}

/* ---- Los dos bloques ---- */
.mn-bloque {
  margin-bottom: 1.75rem;
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--gridline);
}
.mn-bloque h3 {
  margin: 0 0 0.75rem;
  font-size: 21px;
  color: var(--text-primary);
}
.mn-cuenta {
  list-style: none;
  margin: 0;
  padding: 0;
}
/* El signo va en su propia columna, alineado, para que la cuenta se lea como una cuenta:
   una columna de simbolos, una de conceptos, una de numeros. */
.mn-renglon {
  display: grid;
  grid-template-columns: 1.5rem 1fr auto;
  gap: 0.5rem;
  align-items: baseline;
  padding: 0.4rem 0;
}
.mn-renglon-signo {
  font-size: 19px;
  color: var(--text-secondary);
  text-align: center;
}
.mn-renglon-etiqueta {
  font-size: 17px;
  color: var(--text-primary);
}
.mn-renglon-monto {
  font-size: 19px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--text-primary);
  white-space: nowrap;
}
/* El total: la linea que separa, y el numero grande. 33px. */
.mn-total {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  margin: 0.5rem 0 0;
  padding-top: 0.75rem;
  border-top: 2px solid var(--text-primary);
  flex-wrap: wrap;
}
.mn-total > span {
  font-size: 19px;
  font-weight: 700;
  color: var(--text-primary);
}
.mn-total > strong {
  font-size: 33px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--text-primary);
  white-space: nowrap;
}
/* Rojo SOLO en la perdida del bloque 1. El neto del bloque 2 NUNCA va en rojo: salir mas plata
   de la que entro suele ser sano (una carga de inventario), y pintarlo de alarma enseña a
   asustarse de un dato normal. */
.mn-total > strong.mn-negativo {
  color: var(--critical);
}
.mn-nota {
  margin: 0.75rem 0 0;
  padding: 0.75rem;
  background: var(--warning-bg);
  border-left: 4px solid var(--warning-texto);
  border-radius: var(--radius-control);
  font-size: 16px;
  line-height: 1.45;
  color: var(--text-primary);
}
.mn-medios {
  margin: 0 0 1.5rem;
  font-size: 17px;
  color: var(--text-secondary);
}

/* ---- Las tres listas ---- */
.mn-listas-botones {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
}
.mn-boton-lista {
  flex: 1 1 auto;
  min-width: 150px;
  min-height: 48px;
  font-size: 19px;
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  background: var(--surface-1);
  color: var(--text-primary);
  cursor: pointer;
}
.mn-boton-lista-abierto {
  background: var(--brand);
  border-color: var(--brand);
  color: #fff;
}
/* Un boton apagado se ve apagado Y no responde: el disabled del HTML hace lo segundo, esto
   hace lo primero. Sin las dos cosas, o parece roto o engaña. */
.mn-boton-lista-apagado,
.mn-boton-lista:disabled {
  opacity: 0.45;
  cursor: default;
}
.mn-lista-contenedor {
  margin-bottom: 1.5rem;
  --mn-lista-cols: 1fr auto auto;
}
.mn-lista-contenedor h4 {
  margin: 0 0 0.5rem;
  font-size: 19px;
  color: var(--text-primary);
}
.mn-lista {
  list-style: none;
  margin: 0;
  padding: 0;
}
.mn-lista-fila {
  display: grid;
  grid-template-columns: var(--mn-lista-cols);
  gap: 0.5rem 0.75rem;
  align-items: baseline;
  padding: 0.55rem 0;
  border-bottom: 1px solid var(--gridline);
  font-size: 17px;
}
.mn-celda-fecha {
  color: var(--text-secondary);
  white-space: nowrap;
}
.mn-celda-nombre {
  color: var(--text-primary);
}
.mn-celda-num,
.mn-celda-monto {
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}
.mn-celda-monto {
  font-weight: 600;
}
/* La marca ocupa toda la fila siguiente: es una aclaracion, no una columna. */
.mn-marca-credito {
  grid-column: 1 / -1;
  font-size: 15px;
  color: var(--warning-texto);
}

/* ===== UN AJUSTE ANTERIOR AL REGISTRO DE RAZONES BAJA DE TONO (08/09/2026) =====
   Mismo mecanismo que .caja-renglon-vacio --bajar el tono, no esconder-- porque el problema
   es el mismo: un dato que es CONTEXTO compitiendo con los que son SENAL.
   Estas filas son de antes de que existiera el menu de razones (ver RAZONES_DESDE en
   schemas.js). Se muestran enteras: son movimientos reales del ledger y su plata cuenta.
   Lo que no pueden hacer es leerse como una falla de una funcion que todavia no existia --
   y su marca en ambar (--warning-texto) hacia exactamente eso: pintaba de alerta 13 ajustes
   que nadie pudo clasificar. La marca de estas filas va en gris, no en ambar. */
.mn-lista-fila.mn-fila-anterior-al-corte .mn-celda-fecha,
.mn-lista-fila.mn-fila-anterior-al-corte .mn-celda-nombre,
.mn-lista-fila.mn-fila-anterior-al-corte .mn-celda-num,
.mn-lista-fila.mn-fila-anterior-al-corte .mn-celda-monto,
.mn-lista-fila.mn-fila-anterior-al-corte .mn-marca-credito {
  color: var(--texto-atenuado);
}

/* ===== LOS TRES PASOS DEL CIERRE DE CAJA (03/09/2026) =====
   El número del paso, delante del título, para que se lean como una secuencia y no como tres
   cuadros sueltos: el orden importa -- contar antes de ver el esperado es la única forma de
   que el arqueo signifique algo. */
.caja-paso > h3 {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
}
.caja-paso-numero {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.6rem;
  height: 1.6rem;
  border-radius: 50%;
  background: var(--brand);
  color: #fff;
  font-size: 14px;
  font-weight: 700;
  flex: 0 0 auto;
}
.caja-paso-ayuda {
  margin: 0.15rem 0 0.75rem;
  color: var(--text-secondary);
  font-size: 15px;
}
/* Las líneas puente: explican la relación entre el bloque 1 y el 2. Van en el tono de una
   aclaración, no de un dato -- si compitieran con los montos, agregarían el ruido que vinieron
   a sacar. */
.caja-paso-puente {
  margin: 0.6rem 0 0;
  padding-left: 0.65rem;
  border-left: 3px solid var(--gridline);
  color: var(--text-secondary);
  font-size: 15px;
}
/* Un renglón sin movimientos ("ninguna") baja de tono: lo que no ocurrió no compite con lo
   que sí. Se sigue mostrando -- que el cero esté ahí es información. */
.caja-renglon-vacio .caja-desglose-etiqueta,
.caja-renglon-vacio .caja-desglose-cantidad,
.caja-renglon-vacio .caja-desglose-monto {
  color: var(--muted);
}

/* NOTA (04/09/2026): acá hubo un bloque de columnas para #caja-ventas-lista que se
   ELIMINO por ser codigo muerto. Apuntaba a , pero
   crearFilaSimple no pone tres hijos en el <li>: pone UNO, un div.caja-desglose-cabecera que
   los contiene. Los selectores no llegaban a las celdas.
   La grilla real de esas filas es .caja-desglose-cabecera, que YA tiene su regla movil desde
   el 30/08 (la etiqueta toma la fila entera con grid-column:1/-1 y las otras celdas bajan).
   Medido a 390px con las reglas REALES: nombre en 1 linea, fila de 74,7px, sin desborde.
   >>> LA LECCION NO ES DEL CSS SINO DE LA MEDICION: el clon de verificacion forzaba MIS
   selectores en vez de los que el navegador aplica, y por eso "midio" un defecto que no
   existia. Un preview que no usa las reglas publicadas no verifica las reglas publicadas. */

/* Las filas del agrupado y del detalle de préstamo: tres columnas explícitas.
   `.caja-desglose-renglon` sólo pone el borde inferior -- no es un grid -- así que los tres
   spans salían pegados sin un espacio: "sin razón registrada8 ajustes$13.457". Medido en la
   captura del dueño, 02/09/2026. */
.mv-grupo-fila {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: 0.5rem 1rem;
  align-items: baseline;
  padding: 0.45rem 0;
}
.mv-grupo-fila .caja-desglose-cantidad {
  color: var(--text-secondary);
  font-size: 15px;
  white-space: nowrap;
}
.mv-grupo-fila .caja-desglose-monto {
  text-align: right;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* ===== LAS DOS LISTAS DE AJUSTES REUSAN .mn-lista-fila, Y NECESITAN SUS PROPIAS COLUMNAS
   (02/09/2026) =====
   `.mn-lista-fila` hace `grid-template-columns: var(--mn-lista-cols)`, y esa variable estaba
   definida SOLO en `.mn-lista-contenedor` (Mis números). En estas dos listas la variable NO
   existía, así que la declaración caía a nada, el grid quedaba de UNA columna y las celdas se
   apilaban: cada fila ocupaba ~190px de alto en vez de una línea. Medido en la captura del
   dueño el 02/09/2026.
   Se definen EXPLÍCITAS, nunca `auto-fit minmax()`: con auto-fit el número de columnas
   depende del ancho disponible, así que dos filas de la misma lista pueden partirse distinto
   y dejar de alinearse entre sí -- el defecto que este proyecto ya midió en Ventas por
   Vendedor y en el Reporte del período.
   La razón (.mn-marca-credito) ya ocupa la fila entera con `grid-column: 1 / -1`, así que
   cae sola debajo y en letra chica. */
#ajuste-stock-ultimos-lista {
  /* fecha · ítem · cantidad · valor */
  --mn-lista-cols: auto 1fr auto auto;
}
#mv-lista {
  /* fecha · ítem · cantidad · saldo · valor */
  --mn-lista-cols: auto 1fr auto auto auto;
}
/* Los números a la derecha y sin cortarse: una columna de plata que envuelve deja de poder
   leerse en vertical, que es justamente para lo que sirve. */
#ajuste-stock-ultimos-lista .mn-celda-monto,
#mv-lista .mn-celda-monto {
  text-align: right;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
#ajuste-stock-ultimos-lista .mn-celda-nombre,
#mv-lista .mn-celda-nombre {
  /* Un nombre largo se recorta con puntos suspensivos en vez de empujar las columnas de la
     derecha fuera de la pantalla. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* A pantalla angosta, la fila pasa a DOS líneas a propósito: fecha + nombre arriba, los
   números abajo alineados a la derecha. Apilar cinco columnas en 390px no se lee; dos
   líneas ordenadas, sí. */
/* A pantalla angosta la fila pasa a TRES líneas, con posiciones EXPLÍCITAS por celda.
   Con `--mn-lista-cols: auto 1fr` (2 columnas) y 5 celdas, las tres últimas caían cada una a
   su propia fila: el -3, el "queda 76" y el $5.625 apilados en vertical. Medido a 390px el
   02/09/2026, DESPUÉS de arreglar el escritorio -- el mismo defecto, sólo que a otro ancho.
   Se colocan por `grid-area` en vez de dejar que el auto-placement decida, por la misma razón
   por la que no se usa auto-fit: la ubicación no puede depender del ancho sobrante.
     línea 1: fecha  nombre
     línea 2: cantidad  saldo  valor   (los tres juntos, que es como se leen)
     línea 3: la razón */
@media (max-width: 699.98px) {
  #ajuste-stock-ultimos-lista .mn-lista-fila,
  #mv-lista .mn-lista-fila {
    grid-template-columns: auto auto 1fr;
    gap: 0.15rem 0.6rem;
  }
  #mv-lista .mn-lista-fila > :nth-child(1) { grid-area: 1 / 1 / 2 / 2; }
  #mv-lista .mn-lista-fila > :nth-child(2) { grid-area: 1 / 2 / 2 / 4; }
  #mv-lista .mn-lista-fila > :nth-child(3) { grid-area: 2 / 1 / 3 / 2; justify-self: start; }
  #mv-lista .mn-lista-fila > :nth-child(4) { grid-area: 2 / 2 / 3 / 3; justify-self: start; }
  #mv-lista .mn-lista-fila > :nth-child(5) { grid-area: 2 / 3 / 3 / 4; justify-self: end; }
  #mv-lista .mn-marca-credito { grid-area: 3 / 1 / 4 / 4; }
  /* La lista de "últimos 10" tiene UNA celda menos (no muestra saldo): fecha, nombre,
     cantidad, valor. Mismo criterio, un tramo más corto. */
  #ajuste-stock-ultimos-lista .mn-lista-fila > :nth-child(1) { grid-area: 1 / 1 / 2 / 2; }
  #ajuste-stock-ultimos-lista .mn-lista-fila > :nth-child(2) { grid-area: 1 / 2 / 2 / 4; }
  #ajuste-stock-ultimos-lista .mn-lista-fila > :nth-child(3) { grid-area: 2 / 1 / 3 / 2; justify-self: start; }
  #ajuste-stock-ultimos-lista .mn-lista-fila > :nth-child(4) { grid-area: 2 / 3 / 3 / 4; justify-self: end; }
  #ajuste-stock-ultimos-lista .mn-marca-credito { grid-area: 3 / 1 / 4 / 4; }
  #ajuste-stock-ultimos-lista .mn-celda-monto,
  #mv-lista .mn-celda-monto {
    font-size: 15px;
    color: var(--text-secondary);
  }
}
/* El desglose del pie de "en qué gasté": los renglones que suman al total. Van ARRIBA del
   total y en un peso menor, para que se lean como sus partes y no como tres totales que
   compiten. El borde superior separa la lista del pie; el total se queda con el borde grueso
   porque es el que cierra. */
.mn-lista-desglose {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  font-size: 15px;
  color: var(--text-secondary);
  /* El borde separador lo lleva el PRIMERO QUE SE VE, no el primero del DOM: cualquiera de los
     tres puede estar oculto (aparecen solo si son > 0), y `:first-of-type` cuenta igual a los
     [hidden] -- el borde habria quedado invisible justo cuando el primer renglon es cero.
     Se le da a todos y se lo quita a los que tienen un hermano VISIBLE antes: el primero
     visible es el unico sin ese hermano, asi que es el unico que lo conserva. */
  margin: 0.75rem 0 0;
  padding-top: 0.5rem;
  border-top: 1px solid var(--gridline);
}
.mn-lista-desglose:not([hidden]) ~ .mn-lista-desglose {
  margin-top: 0;
  padding-top: 0.15rem;
  border-top: none;
}
.mn-lista-desglose > strong {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--text-primary);
}

.mn-lista-total {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  margin: 0.75rem 0 0;
  padding-top: 0.5rem;
  border-top: 2px solid var(--text-primary);
  font-size: 19px;
  font-weight: 700;
  color: var(--text-primary);
}
.mn-lista-total > strong {
  font-size: 21px;
  font-variant-numeric: tabular-nums;
}

/* ---- Enlace directo: un modulo de UNA sola pantalla (2026-09-01) ---- */
/* Se ve igual que un encabezado de grupo -- misma tipografia, mismo peso, mismo fondo -- para
   que el menu se lea parejo, pero SIN flecha: no hay nada que desplegar. Un modulo con una
   sola pantalla es un enlace, no un desplegable. */
.nav-grupo-enlace .boton-nav-directo {
  display: flex;
  align-items: center;
  width: 100%;
  text-align: left;
  background: rgba(255, 255, 255, 0.06);
  border: none;
  border-radius: 8px;
  padding: 0.55rem 0.6rem;
  margin: 0.15rem 0 0.3rem;
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--sidebar-text);
  cursor: pointer;
}
.nav-grupo-enlace .boton-nav-directo:hover {
  background: rgba(255, 255, 255, 0.11);
}

/* ---- Movil ---- */
@media (max-width: 699.98px) {
  /* Los montos bajan a 27px: a 33px un "$1.887.100" se sale de 390px de ancho. Sigue siendo
     casi el doble que el texto normal de la app. */
  .mn-total > strong {
    font-size: 27px;
  }
  .mn-periodo,
  .mn-boton-lista {
    min-width: 0;
    font-size: 17px;
  }
  /* La cuenta se sigue leyendo como cuenta, pero el concepto puede envolver. */
  .mn-renglon {
    grid-template-columns: 1.25rem 1fr auto;
  }
  .mn-renglon-etiqueta {
    font-size: 16px;
  }
}

/* ---- KPI de inventario en dos lineas + "Te dura" (2026-09-01) ---- */
.tarjeta-kpi-inventario {
  align-items: stretch;
  text-align: left;
}
.kpi-desglose {
  list-style: none;
  margin: 0.35rem 0 0;
  padding: 0;
}
.kpi-desglose li {
  display: flex;
  justify-content: space-between;
  gap: 0.75rem;
  align-items: baseline;
  padding: 0.15rem 0;
  font-size: 0.82rem;
  color: var(--text-secondary);
}
.kpi-desglose strong {
  font-variant-numeric: tabular-nums;
  color: var(--text-primary);
  white-space: nowrap;
}
.kpi-total-inventario {
  display: flex;
  justify-content: space-between;
  gap: 0.75rem;
  align-items: baseline;
  margin: 0.35rem 0 0;
  padding-top: 0.35rem;
  border-top: 1px solid var(--gridline);
  font-size: 0.86rem;
  font-weight: 700;
  color: var(--text-primary);
}
.kpi-total-inventario strong {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* --warning-texto (#8f5400, 5,95:1 medido), NUNCA --warning (2,70:1). */
.kpi-aviso {
  margin: 0.35rem 0 0;
  font-size: 0.76rem;
  line-height: 1.35;
  color: var(--warning-texto);
}

/* Los dos niveles de la columna "Te dura". Se pintan como TEXTO con color y peso, no como
   badge: la columna Estado de al lado ya es el badge, y dos badges contiguos compiten. */
.mp-cobertura-agotado {
  color: var(--critical);
  font-weight: 700;
}
.mp-cobertura-bajo {
  color: var(--warning-texto);
  font-weight: 600;
}

/* Impresion de la remision (11/09/2026) -- bloques que SOLO existen con
   _nichoCfg.imprimeRemision === true (Puerto Coco no los ve). */
/* El nombre del pedido en el modal de cobro: mismo aire que el bloque del vuelto (.pos-vuelto),
   sin su fondo -- es un dato del pedido, no una cuenta. */
.pos-nombre-pedido {
  margin: 0.75rem 0;
  text-align: left;
}
.nicho-bloque-maquina {
  margin-top: 1.5rem;
}
/* El mensaje de la casilla NACE con --good-texto (4,86:1), no con el --good de .exito (3,27:1,
   en el backlog): un elemento nuevo no nace fallando (CLAUDE.md). Acotado a este bloque: los usos
   viejos de .exito no se tocan aca. */
.nicho-bloque-maquina .exito {
  color: var(--good-texto);
}
