/* ==========================================================================
   SIVAL — components.css
   Catalogo unico de componentes de interfaz. Depende de tokens.css y base.css.
   Aqui no se declara ningun color ni ningun tamano literal de diseno: todo
   sale de var(--token). El contrato de uso esta en docs/CONTRATO_UI.md.

   Regla que ordena este archivo: manda el HTML que emiten los componentes de
   assets/js/components/. Cuando el CSS antiguo llamaba distinto a la misma
   pieza, el nombre del JS es el canonico y el viejo queda como alias marcado
   con "ALIAS" (se retira cuando ninguna vista lo use).

   Indice
     0.  Reajustes sobre base.css
     1.  Botones
     2.  Campos de formulario
     3.  Tabla densa (Table.js)
     4.  Paginacion de tabla (Table.js) y paginacion suelta
     5.  Menu de acciones (Table.js)
     6.  Tarjeta
     7.  KPI (KpiCard.js)
     8.  Badge
     9.  Chip
     10. Filtros
     11. Tabs
     12. Breadcrumb, cabecera de pantalla y barra de acciones
     13. Ficha de datos (etiqueta + valor)
     14. Drawer (Drawer.js)
     15. Modal y confirmacion (Modal.js, Confirm.js)
     16. Toast (Toast.js)
     17. Buscador y sugerencias (Search.js)
     18. Rango de fechas (DateRange.js)
     19. Teclado numerico (NumPad.js)
     20. Escaner por camara (core/Escaner.php::visor + EscanerCamara.js)
     21. Grafico (Chart.js)
     22. Estados: vacio, cargando, error, sin permiso, esqueleto
     23. Campana de alertas
     24. Hojas imprimibles (etiquetas de precio, guia de dos partes)
     25. Animaciones
     26. Tactil (< 768 px), panel completo (< 560 px) y movil pequeno (< 360 px)
   ========================================================================== */


/* ==========================================================================
   0. REAJUSTES SOBRE BASE.CSS
   Varios bloques de este archivo declaran display propio (flex o grid) y eso
   los volveria visibles aunque llevaran el atributo hidden. La regla se
   reafirma aqui, despues de todos ellos no: antes, porque lleva !important y
   gana igual; se deja arriba para que se lea junto al resto de resets.
   ========================================================================== */

[hidden] { display: none !important; }

/* Texto solo para lectores de pantalla. Table.js lo llama .sr-solo;
   base.css declara .solo-lectores. Las dos son la misma pieza. */
.sr-solo,
.solo-lectores {
  position: absolute;
  width: var(--linea);
  height: var(--linea);
  margin: calc(var(--linea) * -1);
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Icono SVG en linea (Drawer.js, Modal.js, Toast.js, Table.js). El tamano lo
   fija el atributo width/height del propio <svg>: aqui solo se evita que el
   flex lo estire o lo encoja. */
.icono {
  flex: none;
  vertical-align: middle;
}


/* ==========================================================================
   1. BOTONES
   Tres niveles (primario, secundario, terciario) + peligro.
   Altura unica de 36 px (44 px en POS y movil), radio 4 px, sin mayusculas.
   ========================================================================== */

.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--gap-icono);
  height: var(--alto-control);
  padding: 0 var(--pad-control);
  border: var(--linea) solid var(--borde-fuerte);
  border-radius: var(--r);
  background: var(--superficie);
  color: var(--texto);
  font-size: var(--t-boton);
  font-weight: var(--peso-fuerte);
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--transicion), border-color var(--transicion), color var(--transicion);
}

.btn:hover { text-decoration: none; }

.btn:focus-visible {
  outline: none;
  box-shadow: var(--anillo-foco);
}

/* 1.1 Niveles --------------------------------------------------------------- */

/* Primario: relleno azul. Uno solo por vista o panel. */
.btn--primario {
  background: var(--primario);
  border-color: var(--primario);
  color: var(--texto-invertido);
}
.btn--primario:hover {
  background: var(--primario-hover);
  border-color: var(--primario-hover);
}

/* Secundario: borde, fondo blanco. */
.btn--secundario {
  background: var(--superficie);
  border-color: var(--borde-fuerte);
  color: var(--texto);
}
.btn--secundario:hover { background: var(--superficie-2); }

/* Terciario: solo texto. */
.btn--terciario {
  background: none;
  border-color: transparent;
  color: var(--primario);
  padding: 0 var(--e2);
}
.btn--terciario:hover { background: var(--primario-suave); }

/* Peligro: solo anular o eliminar, siempre con confirmacion. */
.btn--peligro {
  background: var(--peligro);
  border-color: var(--peligro);
  color: var(--texto-invertido);
}
.btn--peligro:hover {
  background: var(--peligro-hover);
  border-color: var(--peligro-hover);
}
.btn--peligro:focus-visible { box-shadow: var(--anillo-foco-peligro); }

/* Terciario en rojo: "Anular ticket" al final de la botonera del POS. */
.btn--terciario-peligro {
  background: none;
  border-color: transparent;
  color: var(--peligro);
  padding: 0 var(--e2);
}
.btn--terciario-peligro:hover { background: var(--peligro-suave); }
.btn--terciario-peligro:focus-visible { box-shadow: var(--anillo-foco-peligro); }

/* 1.2 Tamanos --------------------------------------------------------------- */

.btn--sm {
  height: var(--alto-control-sm);
  padding: 0 var(--e2);
  font-size: var(--t-mini);
}

/* POS y pantallas tactiles */
.btn--pos {
  height: var(--alto-control-pos);
  font-size: var(--t-cuerpo);
}

/* Solo icono: cuadrado, sin texto */
.btn--icono {
  width: var(--alto-control);
  padding: 0;
  border-color: transparent;
  background: none;
  color: var(--texto-2);
}
.btn--icono:hover {
  background: var(--superficie-2);
  color: var(--texto);
}
.btn--icono.btn--sm { width: var(--alto-control-sm); }

.btn--bloque {
  display: flex;
  width: 100%;
}

.btn__icono {
  width: var(--icono);
  height: var(--icono);
  flex: none;
}

/* Atajo de teclado anunciado dentro del boton: "Cobrar  F9" */
.btn__atajo {
  font-family: var(--mono);
  font-size: var(--t-micro);
  font-weight: normal;
  color: var(--texto-3);
}
.btn--primario .btn__atajo,
.btn--peligro .btn__atajo { color: var(--primario-suave); }

/* 1.3 Estados --------------------------------------------------------------- */

.btn:disabled,
.btn.esta-deshabilitado,
.btn[aria-disabled="true"] {
  opacity: var(--opacidad-deshabilitada);
  cursor: default;
}
.btn.esta-deshabilitado,
.btn[aria-disabled="true"] { pointer-events: none; }

/*
   Cargando. Form.js reemplaza el contenido del boton por
   <span class="btn__girador"></span><span class="btn__texto">Guardando…</span>
   y le pone la clase .btn--cargando, despues de haber reservado como min-width
   el mayor de los dos anchos: el boton NO cambia de tamano al pasar a cargando.
*/
.btn--cargando {
  position: relative;
  pointer-events: none;
  cursor: default;
}

.btn__girador {
  display: inline-block;
  width: var(--spinner);
  height: var(--spinner);
  flex: none;
  border: var(--grosor-spinner) solid currentColor;
  border-top-color: transparent;
  border-radius: var(--r-circulo);
  animation: sival-girar 700ms linear infinite;
}

.btn__texto {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/*
   ALIAS. Variante sin marcado propio: el texto se vuelve transparente y el
   spinner se dibuja con ::after. Se conserva para el codigo que ya la usa.
*/
.btn.esta-cargando {
  color: transparent;
  pointer-events: none;
  cursor: default;
}
.btn.esta-cargando > * { visibility: hidden; }

.btn.esta-cargando::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: var(--spinner);
  height: var(--spinner);
  margin: calc(var(--spinner) / -2) 0 0 calc(var(--spinner) / -2);
  border: var(--grosor-spinner) solid var(--texto-2);
  border-top-color: transparent;
  border-radius: var(--r-circulo);
  animation: sival-girar 700ms linear infinite;
}
.btn--primario.esta-cargando::after,
.btn--peligro.esta-cargando::after { border-color: var(--texto-invertido); border-top-color: transparent; }

/* 1.4 Grupos: botones hermanos del mismo ancho, orden [Cancelar] [Guardar] --- */

.grupo-botones {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--e2);
}
.grupo-botones .btn { min-width: var(--ancho-min-boton); }
.grupo-botones .btn--icono,
.grupo-botones .btn--terciario { min-width: 0; }


/* ==========================================================================
   2. CAMPOS DE FORMULARIO
   Etiqueta encima, ayuda debajo, error en rojo bajo el campo.
   Anchos obligatorios del PROMPT §9.3.
   ========================================================================== */

.campo {
  display: flex;
  flex-direction: column;
  gap: var(--e1);
  margin-bottom: var(--e3);
  min-width: 0;
}

.campo__etiqueta {
  font-size: var(--t-etiqueta);
  font-weight: var(--peso-medio);
  text-transform: uppercase;
  letter-spacing: var(--espaciado-etiqueta);
  color: var(--texto-2);
}

.campo--requerido > .campo__etiqueta::after {
  content: " *";
  color: var(--texto-3);
}

/* 2.1 Control --------------------------------------------------------------- */

.campo__control,
.campo input:not([type="checkbox"]):not([type="radio"]),
.campo select,
.campo textarea {
  width: 100%;
  height: var(--alto-control);
  padding: 0 var(--pad-campo);
  border: var(--linea) solid var(--borde-fuerte);
  border-radius: var(--r);
  background: var(--superficie);
  color: var(--texto);
  font-family: var(--fuente);
  font-size: var(--t-cuerpo);
  transition: border-color var(--transicion), box-shadow var(--transicion);
}

.campo textarea {
  height: auto;
  min-height: calc(var(--alto-control) * 2);
  padding: var(--e2) var(--pad-campo);
  line-height: var(--lh);
}

.campo__control:focus,
.campo input:focus,
.campo select:focus,
.campo textarea:focus {
  outline: none;
  border-color: var(--primario);
  box-shadow: var(--anillo-foco);
}

.campo__control::placeholder,
.campo input::placeholder,
.campo textarea::placeholder { color: var(--texto-3); }

.campo__control:disabled,
.campo input:disabled,
.campo select:disabled,
.campo textarea:disabled,
.campo__control[readonly],
.campo input[readonly] {
  background: var(--superficie-2);
  color: var(--texto-3);
  cursor: default;
}

/* 2.2 Ayuda y error --------------------------------------------------------- */

.campo__ayuda {
  font-size: var(--t-mini);
  color: var(--texto-3);
}

.campo__error {
  display: none;
  font-size: var(--t-mini);
  color: var(--peligro);
}

.campo--error .campo__error { display: block; }

.campo--error .campo__control,
.campo--error input,
.campo--error select,
.campo--error textarea { border-color: var(--peligro); }

.campo--error .campo__control:focus,
.campo--error input:focus,
.campo--error select:focus,
.campo--error textarea:focus { box-shadow: var(--anillo-foco-peligro); }

/* 2.3 Prefijo y sufijo de unidad ("$", "ml", "%") ---------------------------- */

.campo__grupo {
  display: flex;
  align-items: stretch;
  min-width: 0;
}
.campo__grupo > .campo__control,
.campo__grupo > input { border-radius: var(--r) 0 0 var(--r); }

.campo__sufijo,
.campo__prefijo {
  display: inline-flex;
  align-items: center;
  height: var(--alto-control);
  padding: 0 var(--e2);
  border: var(--linea) solid var(--borde-fuerte);
  background: var(--superficie-2);
  color: var(--texto-2);
  font-size: var(--t-mini);
  white-space: nowrap;
}
.campo__sufijo {
  border-left: 0;
  border-radius: 0 var(--r) var(--r) 0;
}
.campo__prefijo {
  border-right: 0;
  border-radius: var(--r) 0 0 var(--r);
}
.campo__prefijo + .campo__control,
.campo__prefijo + input { border-radius: 0 var(--r) var(--r) 0; }

/* 2.4 Anchos obligatorios (PROMPT §9.3) ------------------------------------- */

.campo--codigo   { max-width: var(--ancho-codigo); }    /* codigo, SKU, cedula, telefono */
.campo--numero   { max-width: var(--ancho-numero); }    /* cantidad, precio, monto, % */
.campo--fecha    { max-width: var(--ancho-fecha); }
.campo--select   { max-width: var(--ancho-select); }
.campo--nombre   { max-width: var(--ancho-nombre); }
.campo--busqueda { max-width: var(--ancho-busqueda); }
.campo--notas    { max-width: var(--ancho-notas); }

/* Cantidades y montos: monoespaciada y alineados a la derecha */
.campo--numero .campo__control,
.campo--numero input {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  text-align: right;
}

/* 2.5 Casillas y radios ----------------------------------------------------- */

.campo--casilla {
  flex-direction: row;
  align-items: center;
  gap: var(--e2);
}
.campo--casilla .campo__etiqueta {
  text-transform: none;
  letter-spacing: normal;
  font-size: var(--t-cuerpo);
  color: var(--texto);
}
.campo--casilla input[type="checkbox"],
.campo--casilla input[type="radio"] {
  width: var(--icono);
  height: var(--icono);
  flex: none;
  accent-color: var(--primario);
  cursor: pointer;
}

/* 2.6 Formulario ------------------------------------------------------------ */

/* Bloque del formulario. No pinta nada: existe para que la vista tenga un
   gancho estable al que enganchar new Form(). */
.formulario { min-width: 0; }

/*
   Rejilla del formulario. Es la del §9.3: 12 columnas y filas de 2 a 4 campos
   relacionados. Se apoya en .grid-12 / .col-N de base.css y solo ajusta lo
   que un formulario necesita: los campos no arrastran su margen inferior
   porque la separacion la pone el gap de la rejilla.
*/
.formulario__rejilla {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--e3);
  align-items: start;
}
.formulario__rejilla > .campo,
.grid-12 > .campo { margin-bottom: 0; }

/* Un campo dentro de la rejilla ocupa su columna entera: el ancho del §9.3 lo
   sigue poniendo .campo--codigo, .campo--numero, etc. sobre el control. */
.formulario__rejilla > .campo > .campo__control,
.formulario__rejilla > .campo > .campo__grupo { max-width: 100%; }

.formulario__seccion + .formulario__seccion { margin-top: var(--e5); }

.formulario__titulo {
  margin-bottom: var(--e3);
  padding-bottom: var(--e2);
  border-bottom: var(--linea) solid var(--borde);
  font-size: var(--t-etiqueta);
  font-weight: var(--peso-fuerte);
  text-transform: uppercase;
  letter-spacing: var(--espaciado-etiqueta);
  color: var(--texto-2);
}

.formulario__pie {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--e2);
  margin-top: var(--e4);
  padding-top: var(--e3);
  border-top: var(--linea) solid var(--borde);
}
.formulario__pie .btn { min-width: var(--ancho-min-boton); }


/* ==========================================================================
   3. TABLA DENSA (Table.js)
   Estructura que dibuja el componente:

     .tabla-envoltura                 <- el contenedor que recibe new Table()
       .tabla-desplazable             <- la caja que desplaza (cabecera fija)
         table.tabla.tabla--cabecera-fija
           thead.tabla__cabecera > tr > th.tabla__th
           tbody.tabla__cuerpo   > tr.tabla__fila > td.tabla__celda
           tfoot.tabla__pie      > tr.tabla__total
       .tabla-paginacion

   Fila de 36 px, cabecera fija, sin cebra, numeros a la derecha en
   monoespaciada, fila de total en negrita, hover sutil.
   ========================================================================== */

.tabla-envoltura {
  display: block;
  min-width: 0;
}

/*
   Zona desplazable. Sin ella una tabla ancha desborda la pantalla en lugar de
   desplazarse dentro de su caja, y la cabecera fija no tiene contra que fijarse.

   El alto maximo es lo que hace que la cabecera se quede quieta: la lista se
   desplaza DENTRO de la caja y el th sticky se ancla a ella. Una pantalla que
   necesite otro alto (o ninguno) reescribe --tabla-alto en su contenedor.
*/
.tabla-desplazable {
  position: relative;
  overflow: auto;
  max-height: var(--tabla-alto, 70vh);
  background: var(--superficie);
  border: var(--linea) solid var(--borde);
  border-radius: var(--r-2);
}

/* ALIAS del mismo marco, para las vistas que lo escriben a mano. */
.tabla-contenedor {
  position: relative;
  overflow: auto;
  background: var(--superficie);
  border: var(--linea) solid var(--borde);
  border-radius: var(--r-2);
}

.tabla {
  width: 100%;
  font-size: var(--t-tabla);
}

/* Cuando la columna de nombre debe truncar con elipsis */
.tabla--fija { table-layout: fixed; }

/* Grupos de la tabla. No cambian nada por si mismos: existen para que una
   vista pueda apuntar a la cabecera, al cuerpo o al pie sin depender de la
   etiqueta HTML. */
.tabla__cabecera,
.tabla__cuerpo,
.tabla__pie,
.tabla__fila { min-width: 0; }

.tabla th,
.tabla__th {
  position: sticky;
  top: 0;
  z-index: var(--z-cabecera-tabla);
  height: var(--alto-fila);
  padding: var(--e2) var(--e3);
  background: var(--fondo-cabecera-tabla);
  border-bottom: var(--linea) solid var(--borde-fuerte);
  color: var(--texto-2);
  font-size: var(--t-etiqueta);
  font-weight: var(--peso-fuerte);
  text-transform: uppercase;
  letter-spacing: var(--espaciado-etiqueta);
  text-align: left;
  white-space: nowrap;
}

/* La cabecera solo se queda fija si algo desplaza por debajo de ella. */
.tabla--cabecera-fija th,
.tabla--cabecera-fija .tabla__th { position: sticky; top: 0; }

.tabla td,
.tabla__celda {
  height: var(--alto-fila);
  padding: var(--e2) var(--e3);
  border-bottom: var(--linea) solid var(--borde);
  vertical-align: middle;
}

.tabla tbody tr:last-child td { border-bottom: 0; }

/* Sin cebra: solo hover */
.tabla tbody tr:hover { background: var(--fila-hover); }
.tabla tbody tr.esta-seleccionada { background: var(--fila-seleccionada); }
.tabla--clicable tbody tr,
.tabla__fila--clicable { cursor: pointer; }

/* 3.1 Alineacion ------------------------------------------------------------ */

/* Numeros a la derecha, monoespaciada */
.tabla__num,
.tabla th.tabla__num {
  text-align: right;
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.tabla th.tabla__num { font-family: var(--fuente); }

.tabla__centro,
.tabla th.tabla__centro { text-align: center; }

/* Columna flexible (nombre) y columnas fijas */
.tabla__flexible {
  width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tabla__estrecha { width: 1%; white-space: nowrap; }

/* .truncar solo recorta si la celda le da caja propia */
.tabla__celda > .truncar { display: block; max-width: 100%; }

/* 3.2 Fila de total --------------------------------------------------------- */

.tabla tfoot td {
  position: sticky;
  bottom: 0;
  background: var(--superficie);
  border-top: var(--linea) solid var(--borde-fuerte);
  border-bottom: 0;
}
.tabla__total {
  font-weight: var(--peso-total);
  color: var(--acento);
}
.tabla tfoot .tabla__num { font-weight: var(--peso-total); }

/* 3.3 Acciones por fila ----------------------------------------------------- */

/* Visibles al hover en escritorio, siempre en movil (§9.5).
   width:1% = "tan estrecha como permita el contenido". */
.tabla__acciones,
.tabla__th--acciones,
.tabla__col-acciones {
  width: 1%;
  white-space: nowrap;
  text-align: right;
}

/* La columna de acciones se pega al borde: el relleno normal de celda le roba
   ancho a la columna de nombre y a 1366 px acababa cortando el ultimo boton.
   Hace falta el selector con la etiqueta para ganarle a `.tabla td`. */
.tabla td.tabla__acciones,
.tabla th.tabla__th--acciones {
  padding-right: var(--e2);
  padding-left: var(--e1);
}
.tabla__acciones .btn--icono {
  opacity: 0;
  transition: opacity var(--transicion);
}
.tabla tbody tr:hover .tabla__acciones .btn--icono,
.tabla__acciones .btn--icono:focus-visible { opacity: 1; }

/* Boton de accion de fila que dibuja Table.js: nunca parte en dos lineas. */
.tabla__accion { white-space: nowrap; }
.tabla__accion--peligro { color: var(--peligro); }
.tabla__accion--peligro:hover { background: var(--peligro-suave); }

/* 3.4 Cabecera ordenable ---------------------------------------------------- */

.tabla__ordenable { cursor: pointer; }

.tabla__boton-orden {
  display: inline-flex;
  align-items: center;
  gap: var(--e1);
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  cursor: pointer;
  user-select: none;
}
.tabla__boton-orden:focus-visible {
  outline: none;
  box-shadow: var(--anillo-foco);
}

.tabla__icono-orden { color: var(--texto-3); }
.tabla__ordenable--activo .tabla__boton-orden { color: var(--texto); }
.tabla__ordenable--activo .tabla__icono-orden { color: var(--primario); }

/* La flecha contraria a la direccion vigente se apaga. */
.tabla__ordenable--activo[aria-sort="ascending"] .tabla__flecha-abajo,
.tabla__ordenable--activo[aria-sort="descending"] .tabla__flecha-arriba { opacity: .25; }

/* ALIAS del ordenamiento escrito a mano en una vista. */
.tabla__orden {
  cursor: pointer;
  user-select: none;
}
.tabla__orden:hover { color: var(--texto); }
.tabla__orden--asc::after  { content: " \2191"; }
.tabla__orden--desc::after { content: " \2193"; }

/* 3.5 Filas de estado: cargando, vacio y error ------------------------------ */

.tabla__fila--esqueleto,
.tabla__fila--estado { cursor: default; }

.tabla__fila--esqueleto:hover,
.tabla__fila--estado:hover { background: transparent; }

.tabla__fila--estado td,
.tabla__fila--estado .tabla__celda { height: auto; }

/* 3.6 Tabla dentro de un drawer o una tarjeta: sin marco propio -------------- */

.tarjeta > .tabla-contenedor,
.tarjeta > .tabla-desplazable,
.drawer__cuerpo > .tabla-contenedor,
.drawer__cuerpo > .tabla-desplazable {
  border: 0;
  border-radius: 0;
}


/* ==========================================================================
   4. PAGINACION DE TABLA (Table.js) Y PAGINACION SUELTA
   "Filas 50 · 1–50 de 312 · ‹ ›" abajo, con la cuenta pegada a la derecha.
   ========================================================================== */

.tabla-paginacion {
  display: flex;
  align-items: center;
  gap: var(--e3);
  padding: var(--e2) 0 0;
  font-size: var(--t-mini);
  color: var(--texto-2);
}

.tabla-paginacion__tamano {
  display: inline-flex;
  align-items: center;
  gap: var(--e2);
}

.tabla-paginacion__select {
  width: auto;
  height: var(--alto-control-sm);
  padding: 0 var(--e2);
  border: var(--linea) solid var(--borde-fuerte);
  border-radius: var(--r);
  background: var(--superficie);
  color: var(--texto-2);
  font-family: var(--fuente);
  font-size: var(--t-mini);
}
.tabla-paginacion__select:focus-visible {
  outline: none;
  box-shadow: var(--anillo-foco);
}

.tabla-paginacion__info {
  margin-left: auto;
  font-family: var(--mono);
  white-space: nowrap;
}

.tabla-paginacion__botones {
  display: inline-flex;
  gap: var(--e1);
}
.tabla-paginacion__botones .btn,
.tabla-paginacion__anterior,
.tabla-paginacion__siguiente { min-width: var(--alto-control-sm); }

/* Paginacion escrita a mano en una vista (mismo aspecto). */
.paginacion {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--e3);
  margin-top: var(--e3);
  font-size: var(--t-mini);
  color: var(--texto-2);
}

.paginacion__info {
  font-family: var(--mono);
  white-space: nowrap;
}

.paginacion__botones {
  display: flex;
  gap: var(--e1);
}

.paginacion__boton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--alto-control-sm);
  height: var(--alto-control-sm);
  border: var(--linea) solid var(--borde-fuerte);
  border-radius: var(--r);
  background: var(--superficie);
  color: var(--texto-2);
}
.paginacion__boton:hover { background: var(--superficie-2); }
.paginacion__boton:disabled {
  opacity: var(--opacidad-deshabilitada);
  cursor: default;
}
.paginacion__boton:focus-visible {
  outline: none;
  box-shadow: var(--anillo-foco);
}

.paginacion__tamano {
  height: var(--alto-control-sm);
  padding: 0 var(--e1);
  border: var(--linea) solid var(--borde-fuerte);
  border-radius: var(--r);
  background: var(--superficie);
  color: var(--texto-2);
  font-size: var(--t-mini);
}


/* ==========================================================================
   5. MENU DE ACCIONES (Table.js: el "⋯" cuando hay mas de 3 acciones)
   ========================================================================== */

.menu-envoltura {
  position: relative;
  display: inline-block;
}

/* Con el menu desplegado el "⋯" se queda visible aunque el puntero se vaya. */
.menu-envoltura--abierta .menu__disparador {
  opacity: 1;
  background: var(--superficie-2);
}

.menu {
  position: absolute;
  top: 100%;
  right: 0;
  z-index: var(--z-menu);
  display: flex;
  flex-direction: column;
  min-width: var(--ancho-codigo);
  padding: var(--e1);
  background: var(--superficie);
  border: var(--linea) solid var(--borde-fuerte);
  border-radius: var(--r);
  box-shadow: var(--sombra-2);
}

.menu__disparador { font-family: var(--fuente); }

.menu__opcion {
  padding: var(--e2);
  border-radius: var(--r);
  color: var(--texto);
  font-size: var(--t-mini);
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
}
.menu__opcion:hover { background: var(--superficie-2); }
.menu__opcion:focus-visible {
  outline: none;
  box-shadow: var(--anillo-foco);
}
.menu__opcion--peligro { color: var(--peligro); }
.menu__opcion--peligro:hover { background: var(--peligro-suave); }


/* ==========================================================================
   6. TARJETA
   ========================================================================== */

.tarjeta {
  background: var(--superficie);
  border: var(--linea) solid var(--borde);
  border-radius: var(--r-2);
  box-shadow: var(--sombra);
  padding: var(--e4);
}

/* Variante con cabecera / cuerpo / pie propios */
.tarjeta--secciones { padding: 0; }

.tarjeta__cabecera {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e2);
  padding: var(--e3) var(--e4);
  border-bottom: var(--linea) solid var(--borde);
}

.tarjeta__titulo {
  font-size: var(--t-etiqueta);
  font-weight: var(--peso-fuerte);
  text-transform: uppercase;
  letter-spacing: var(--espaciado-etiqueta);
  color: var(--texto-2);
}

.tarjeta__cuerpo { padding: var(--e4); }

.tarjeta__pie {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--e2);
  padding: var(--e3) var(--e4);
  border-top: var(--linea) solid var(--borde);
  background: var(--superficie-2);
}

/* Tarjetas hermanas con el mismo alto */
.tarjetas {
  display: grid;
  gap: var(--e3);
  align-items: stretch;
}
.tarjetas > .tarjeta { height: 100%; }


/* ==========================================================================
   7. KPI (KpiCard.js)
   Maximo 5 por pantalla, todos del mismo alto.
   ========================================================================== */

.kpis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--ancho-kpi-min), 1fr));
  gap: var(--e3);
  align-items: stretch;
  margin-bottom: var(--e4);
}

.kpi {
  display: flex;
  flex-direction: column;
  gap: var(--e1);
  height: 100%;
  padding: var(--e3) var(--e4);
  background: var(--superficie);
  border: var(--linea) solid var(--borde);
  border-left: var(--linea-2) solid var(--borde-fuerte);
  border-radius: var(--r-2);
  box-shadow: var(--sombra);
}

.kpi__etiqueta {
  font-size: var(--t-etiqueta);
  font-weight: var(--peso-medio);
  text-transform: uppercase;
  letter-spacing: var(--espaciado-etiqueta);
  color: var(--texto-2);
}

.kpi__valor {
  font-family: var(--mono);
  font-size: var(--t-kpi);
  font-weight: var(--peso-fuerte);
  line-height: var(--lh-apretada);
  color: var(--acento);
}

.kpi__sub {
  font-size: var(--t-mini);
  color: var(--texto-2);
}

/* Pie de la tarjeta: se pega abajo para que las hermanas cuadren. */
.kpi__pie {
  display: flex;
  align-items: baseline;
  gap: var(--e1);
  flex-wrap: wrap;
  margin-top: auto;
}

.kpi__delta {
  font-family: var(--mono);
  font-size: var(--t-mini);
}
.kpi__delta--sube   { color: var(--exito); }
.kpi__delta--baja   { color: var(--peligro); }
.kpi__delta--neutro { color: var(--texto-3); }

.kpi__delta-nota {
  margin-left: var(--e1);
  font-family: var(--fuente);
  color: var(--texto-3);
}

/* Tono: el color significa, no decora */
.kpi--ok      { border-left-color: var(--exito); }
.kpi--aviso   { border-left-color: var(--aviso); }
.kpi--peligro { border-left-color: var(--peligro); }


/* ==========================================================================
   8. BADGE (estados dentro de tablas y fichas)
   ========================================================================== */

.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--e1);
  padding: var(--e1) var(--e2);
  border: var(--linea) solid transparent;
  border-radius: var(--r);
  background: var(--neutro-suave);
  color: var(--texto-2);
  font-size: var(--t-badge);
  font-weight: var(--peso-medio);
  line-height: 1;
  white-space: nowrap;
}

.badge--ok      { background: var(--exito-suave);   color: var(--exito); }
.badge--aviso   { background: var(--aviso-suave);   color: var(--aviso); }
.badge--peligro { background: var(--peligro-suave); color: var(--peligro); }
.badge--neutro  { background: var(--neutro-suave);  color: var(--texto-2); }


/* ==========================================================================
   9. CHIP (filtros rapidos)
   ========================================================================== */

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--e1);
  height: var(--alto-control-sm);
  padding: 0 var(--e2);
  border: var(--linea) solid var(--borde-fuerte);
  border-radius: var(--r);
  background: var(--superficie);
  color: var(--texto-2);
  font-size: var(--t-mini);
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--transicion), border-color var(--transicion), color var(--transicion);
}
.chip:hover {
  background: var(--superficie-2);
  text-decoration: none;
}
.chip:focus-visible {
  outline: none;
  box-shadow: var(--anillo-foco);
}

.chip--activo {
  background: var(--primario-suave);
  border-color: var(--primario);
  color: var(--primario);
  font-weight: var(--peso-medio);
}

.chip__contador {
  font-family: var(--mono);
  color: var(--texto-3);
}
.chip--activo .chip__contador { color: var(--primario); }

.chip__quitar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--icono);
  height: var(--icono);
  border-radius: var(--r);
  color: var(--texto-3);
}
.chip__quitar:hover { color: var(--peligro); }

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e1);
}


/* ==========================================================================
   10. FILTROS — una sola fila encima de la tabla
   ========================================================================== */

.filtros {
  display: flex;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: var(--e2);
  margin-bottom: var(--e3);
  padding: var(--e3);
  background: var(--superficie);
  border: var(--linea) solid var(--borde);
  border-radius: var(--r-2);
}

/*
   Dentro de la fila de filtros los campos son elementos flex: sin una base
   explicita se encogerian hasta el ancho intrinseco del input. La base es el
   ancho del §9.3, y cuando ya no cabe la fila se envuelve en lugar de apretar.
*/
.filtros .campo {
  margin-bottom: 0;
  flex: 0 1 auto;
}
.filtros .campo--busqueda { flex: 1 1 var(--ancho-busqueda); }
.filtros .campo--nombre   { flex: 0 1 var(--ancho-nombre); }
.filtros .campo--select   { flex: 0 1 var(--ancho-select); }
.filtros .campo--codigo   { flex: 0 1 var(--ancho-codigo); }
.filtros .campo--fecha    { flex: 0 0 var(--ancho-fecha); }
.filtros .campo--numero   { flex: 0 0 var(--ancho-numero); }

.filtros__grupo {
  display: flex;
  align-items: flex-end;
  gap: var(--e2);
  min-width: 0;
}

/* "Limpiar" siempre al final de la fila */
.filtros__limpiar { margin-left: auto; }


/* ==========================================================================
   11. TABS
   ========================================================================== */

.tabs {
  display: flex;
  align-items: center;
  gap: var(--e4);
  margin-bottom: var(--e4);
  /* La linea inferior va como sombra interior para que el subrayado de la
     pestana activa la tape sin margenes negativos (que provocan un scroll
     vertical de 1 px dentro del contenedor). */
  box-shadow: inset 0 calc(var(--linea) * -1) 0 var(--borde);
  overflow-x: auto;
  overflow-y: hidden;
}

.tabs__enlace {
  display: inline-flex;
  align-items: center;
  gap: var(--e1);
  height: var(--alto-control);
  padding: 0 var(--e1);
  border-bottom: var(--linea-2) solid transparent;
  color: var(--texto-2);
  font-size: var(--t-cuerpo);
  white-space: nowrap;
  cursor: pointer;
}
.tabs__enlace:hover {
  color: var(--texto);
  text-decoration: none;
}

.tabs__enlace--activo {
  color: var(--primario);
  border-bottom-color: var(--primario);
  font-weight: var(--peso-medio);
}

.tabs__contador {
  padding: 0 var(--e1);
  border-radius: var(--r);
  background: var(--neutro-suave);
  color: var(--texto-2);
  font-family: var(--mono);
  font-size: var(--t-badge);
}
.tabs__enlace--activo .tabs__contador {
  background: var(--primario-suave);
  color: var(--primario);
}

/* Panel de contenido de una pestana. El atributo hidden lo apaga (bloque 0). */
.tabs__panel { min-width: 0; }


/* ==========================================================================
   12. BREADCRUMB, CABECERA DE PANTALLA Y BARRA DE ACCIONES
   .cabecera-pantalla y .cabecera-pantalla__acciones viven en base.css porque
   son layout; aqui van las piezas de contenido que las acompanan.
   ========================================================================== */

.breadcrumb {
  display: flex;
  align-items: center;
  gap: var(--e1);
  margin-bottom: var(--e1);
  font-size: var(--t-mini);
  color: var(--texto-2);
}

.breadcrumb a { color: var(--texto-2); }
.breadcrumb a:hover { color: var(--primario); }

.breadcrumb__sep::before {
  content: "/";
  color: var(--texto-3);
}

.breadcrumb__actual {
  color: var(--texto);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Parrafo de ayuda de una pantalla: una linea, ancho de lectura, gris. */
.ayuda-pantalla {
  margin: 0;
  max-width: var(--ancho-lectura);
  color: var(--texto-2);
  font-size: var(--t-mini);
}

/*
   Barra de acciones sobre una lista: a la izquierda el texto que explica lo
   que se ve, a la derecha los botones. Envuelve en lugar de apretar.
*/
.barra-acciones {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--e3);
  margin-bottom: var(--e3);
}

.barra-acciones__texto {
  margin: 0;
  min-width: 0;
  max-width: var(--ancho-lectura);
  color: var(--texto-2);
  font-size: var(--t-mini);
}

.barra-acciones__botones {
  display: flex;
  align-items: center;
  gap: var(--e2);
  flex: 0 1 auto;
  /* Envuelve: tres botones en una fila sin envolver empujaban la pantalla a
     418 px de ancho en un movil de 360 y el navegador respondia alejando el
     zoom (todo mas pequeno). */
  flex-wrap: wrap;
  justify-content: flex-end;
  min-width: 0;
}


/* ==========================================================================
   13. FICHA DE DATOS — pares etiqueta / valor en una rejilla
   Es el bloque de resumen que abre casi toda ficha en drawer.
   ========================================================================== */

.ficha-datos {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--ancho-numero), 1fr));
  gap: var(--e2) var(--e3);
  margin: 0 0 var(--e3);
  padding: var(--e3);
  background: var(--superficie-2);
  border: var(--linea) solid var(--borde);
  border-radius: var(--r-2);
}

/* Variante sin marco, para meterla dentro de una tarjeta que ya lo tiene. */
.ficha-datos--desnuda {
  padding: 0;
  background: none;
  border: 0;
}

.ficha-dato {
  display: flex;
  flex-direction: column;
  gap: var(--e0);
  min-width: 0;
}

/* Ocupa la fila entera: notas, direcciones, motivos. */
.ficha-dato--ancho { grid-column: 1 / -1; }

.ficha-dato__etiqueta {
  display: block;
  font-size: var(--t-etiqueta);
  font-weight: var(--peso-medio);
  text-transform: uppercase;
  letter-spacing: var(--espaciado-etiqueta);
  color: var(--texto-2);
}

.ficha-dato__valor {
  display: block;
  min-width: 0;
  color: var(--texto);
  overflow: hidden;
  text-overflow: ellipsis;
}

.ficha-dato__valor--num {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
}

.ficha-dato__valor--total {
  font-family: var(--mono);
  font-weight: var(--peso-total);
  color: var(--acento);
}

/* Aviso corto dentro de una ficha: franja lateral, nunca un modal. */
.ficha-aviso {
  margin: var(--e3) 0 0;
  padding: var(--e2) var(--e3);
  border-left: var(--linea-2) solid var(--aviso);
  background: var(--aviso-suave);
  color: var(--texto);
  font-size: var(--t-mini);
}
.ficha-aviso--peligro {
  border-left-color: var(--peligro);
  background: var(--peligro-suave);
}
.ficha-aviso--ok {
  border-left-color: var(--exito);
  background: var(--exito-suave);
}


/* ==========================================================================
   14. DRAWER (Drawer.js) — panel derecho de 420 a 560 px
   Estructura:
     .drawer-fondo                 <- velo a pantalla completa
       .drawer                     <- panel fijo a la derecha
         .drawer__cabecera > .drawer__titulo + .drawer__cerrar
         .drawer__cuerpo
         .drawer__pie              <- lo pone la vista, con [Cancelar][Guardar]
   ========================================================================== */

.drawer-fondo {
  position: fixed;
  inset: 0;
  z-index: var(--z-velo);
  background: var(--velo);
  /* Sin animacion de entrada, por lo mismo que el panel (ver mas abajo): el
     primer fotograma de un fundido es opacidad 0, y un reloj de animaciones
     que no avance deja el velo invisible y el panel flotando sobre la lista.
     El §9.1 tampoco quiere animaciones decorativas. */
}

/* Con un panel abierto, el fondo no se desplaza. */
body.sin-desplazamiento { overflow: hidden; }

.drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: var(--z-drawer);
  display: flex;
  flex-direction: column;
  /*
     Drawer.js publica el ancho pedido como --drawer-ancho (no como width en
     linea), para que las consultas de medios puedan pasarlo a 100 % sin
     recurrir a !important.
  */
  width: var(--drawer-ancho, var(--ancho-drawer));
  min-width: var(--ancho-drawer-min);
  max-width: var(--ancho-drawer-max);
  /* Relleno del cuerpo. Se declara aqui porque el pie pegajoso lo necesita
     como margen negativo para sangrar hasta el borde del panel. */
  --drawer-pad: var(--e4);
  background: var(--superficie);
  border-left: var(--linea) solid var(--borde);
  box-shadow: var(--sombra-2);
  transition: transform var(--transicion-panel);
}

/*
   El panel NO tiene animacion de entrada, y es a proposito.

   Cualquier forma de animarlo (translateX desde 100 %, opacidad desde 0) tiene
   el mismo defecto: el primer fotograma deja el panel fuera de la pantalla o
   invisible, y basta con que el reloj de animaciones no avance —pestana en
   segundo plano, ahorro de energia, un navegador que reduce movimiento— para
   que el usuario abra el panel y no vea nada. Ese fue exactamente el fallo que
   arrastraba la fase 1. El velo si se funde, porque su primer fotograma
   transparente no esconde nada.

   El §9.1 del PROMPT ademas pide "sin animaciones decorativas": el panel
   aparece, y aparece dentro de la pantalla.

   .esta-abierto la sigue poniendo Drawer.js porque varias vistas la usan como
   gancho; aqui solo fija el reposo por si alguna hoja futura lo desplazara.
*/
.drawer.esta-abierto { transform: translateX(0); }

.drawer--ancho { --drawer-ancho: var(--ancho-drawer-max); }

.drawer__cabecera {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e2);
  flex: none;
  height: var(--alto-barra-superior);
  padding: 0 var(--e4);
  border-bottom: var(--linea) solid var(--borde);
}

.drawer__titulo {
  font-size: var(--t-cuerpo);
  font-weight: var(--peso-fuerte);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.drawer__cerrar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--alto-control);
  height: var(--alto-control);
  flex: none;
  border-radius: var(--r);
  color: var(--texto-2);
}
.drawer__cerrar:hover { background: var(--superficie-2); }

.drawer__cuerpo {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: var(--drawer-pad);
}

/*
   El contenido del panel lo trae Response::parcial, asi que el pie de botones
   de la vista llega DENTRO de .drawer__cuerpo. Se pega abajo para que
   [Cancelar] [Guardar] no se pierdan al desplazar un formulario largo, que es
   justo lo que hace falta cuando el panel ocupa toda la pantalla.
*/
.drawer__cuerpo .formulario__pie,
.drawer__cuerpo .drawer__pie {
  position: sticky;
  bottom: calc(var(--drawer-pad) * -1);
  z-index: var(--z-cabecera-tabla);
  margin: var(--e4) calc(var(--drawer-pad) * -1) calc(var(--drawer-pad) * -1);
  padding: var(--e3) var(--drawer-pad);
  border-top: var(--linea) solid var(--borde);
  background: var(--superficie-2);
}

/* Pie con los botones a la derecha, orden [Cancelar] [Guardar].
   flex:none + el cuerpo desplazable lo dejan siempre a la vista. */
.drawer__pie {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--e2);
  flex: none;
  padding: var(--e3) var(--e4);
  border-top: var(--linea) solid var(--borde);
  background: var(--superficie-2);
}
.drawer__pie .btn { min-width: var(--ancho-min-boton); }


/* ==========================================================================
   15. MODAL Y CONFIRMACION (Modal.js, Confirm.js)
   Estructura canonica (la que emite el JS):
     .modal-fondo | .confirmar-fondo   <- velo, centra
       .modal                          <- la caja
         .modal__cabecera > .modal__titulo + .modal__cerrar
         .modal__cuerpo
         .modal__pie

   OJO: .modal es la CAJA, no el velo. La version antigua de este archivo
   usaba .modal como velo y .modal__caja como caja; ninguna vista escribia
   .modal__caja, asi que gana el JS. El alias queda mas abajo.
   ========================================================================== */

.modal-fondo,
.confirmar-fondo {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--e4);
  background: var(--velo);
  /* Sin animacion de entrada: ver §14. */
}

/* La confirmacion puede aparecer encima de un modal abierto. */
.confirmar-fondo { z-index: calc(var(--z-modal) + 1); }

.modal {
  position: static;
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: var(--ancho-modal);
  max-height: 100%;
  padding: 0;
  overflow: hidden;
  background: var(--superficie);
  border-radius: var(--r-2);
  box-shadow: var(--sombra-2);
}

/* La confirmacion es mas estrecha: titulo, una frase con la cifra y dos botones. */
.modal--confirmar { max-width: var(--ancho-acceso); }

/* ALIAS: caja escrita a mano dentro de un velo propio. */
.modal__caja {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: var(--ancho-modal);
  max-height: 100%;
  background: var(--superficie);
  border-radius: var(--r-2);
  box-shadow: var(--sombra-2);
  overflow: hidden;
}

.modal__cabecera {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e2);
  flex: none;
  padding: var(--e3) var(--e4);
  border-bottom: var(--linea) solid var(--borde);
}

.modal__titulo {
  font-size: var(--t-cuerpo);
  font-weight: var(--peso-fuerte);
}
.modal--peligro .modal__titulo { color: var(--peligro); }

.modal__cerrar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--alto-control);
  height: var(--alto-control);
  flex: none;
  border-radius: var(--r);
  color: var(--texto-2);
}
.modal__cerrar:hover { background: var(--superficie-2); }

.modal__cuerpo {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: var(--e4);
}

/* La frase con la cifra en juego: "Vas a anular la venta #1043 por $27,50". */
.modal__detalle {
  margin: 0;
  color: var(--texto-2);
  font-size: var(--t-cuerpo);
}

.modal__pie {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--e2);
  flex: none;
  padding: var(--e3) var(--e4);
  border-top: var(--linea) solid var(--borde);
  background: var(--superficie-2);
}
.modal__pie .btn { min-width: var(--ancho-min-boton); }


/* ==========================================================================
   16. TOAST (Toast.js) — abajo a la derecha, 3 s, con "Deshacer" cuando aplica
   ========================================================================== */

.toast-pila,
.toasts {                                   /* .toasts es ALIAS del nombre viejo */
  position: fixed;
  right: var(--e5);
  bottom: var(--e5);
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  gap: var(--e2);
  width: var(--ancho-toast);
  max-width: calc(100vw - var(--e6));
}

.toast {
  display: flex;
  align-items: flex-start;
  gap: var(--e2);
  padding: var(--e3);
  background: var(--superficie);
  border: var(--linea) solid var(--borde);
  border-left: var(--linea-2) solid var(--texto-2);
  border-radius: var(--r);
  box-shadow: var(--sombra-2);
  font-size: var(--t-mini);
  animation: sival-entrar var(--transicion-panel);
}

.toast--ok      { border-left-color: var(--exito); }
.toast--error   { border-left-color: var(--peligro); }
.toast--info    { border-left-color: var(--primario); }
.toast--aviso   { border-left-color: var(--aviso); }

.toast--saliendo {
  opacity: 0;
  transition: opacity var(--transicion);
}

.toast__texto,
.toast__mensaje {                           /* .toast__mensaje es ALIAS */
  flex: 1;
  min-width: 0;
  color: var(--texto);
}

.toast__accion {
  flex: none;
  color: var(--primario);
  font-weight: var(--peso-fuerte);
  font-size: var(--t-mini);
  cursor: pointer;
}
.toast__accion:hover { text-decoration: underline; }

.toast__cerrar {
  flex: none;
  color: var(--texto-3);
  cursor: pointer;
}
.toast__cerrar:hover { color: var(--texto); }


/* ==========================================================================
   17. BUSCADOR Y SUGERENCIAS (Search.js, buscador global Ctrl+K)
   Search.js envuelve el input en .buscador y cuelga .buscador__resultados.
   ========================================================================== */

.buscador {
  position: relative;
  width: 100%;
  max-width: var(--ancho-busqueda);
}

.buscador__entrada {
  width: 100%;
  height: var(--alto-control);
  padding: 0 var(--pad-campo);
  border: var(--linea) solid var(--borde-fuerte);
  border-radius: var(--r);
  background: var(--superficie);
  color: var(--texto);
  font-size: var(--t-cuerpo);
}
.buscador__entrada:focus {
  outline: none;
  border-color: var(--primario);
  box-shadow: var(--anillo-foco);
}

/* Mientras espera la respuesta, el campo se apaga un poco. */
.buscador--cargando .campo__control,
.buscador--cargando .buscador__entrada { opacity: .85; }

/* Con la lista desplegada el campo pierde el redondeo de abajo. */
.buscador--abierto .campo__control,
.buscador--abierto .buscador__entrada { border-bottom-color: var(--primario); }

.buscador__resultados {
  position: absolute;
  top: calc(100% + var(--e1));
  left: 0;
  right: 0;
  z-index: var(--z-menu);
  max-height: var(--alto-sugerencias);
  overflow-y: auto;
  background: var(--superficie);
  border: var(--linea) solid var(--borde);
  border-radius: var(--r-2);
  box-shadow: var(--sombra-2);
}

.buscador__item {
  display: flex;
  align-items: center;
  gap: var(--e2);
  padding: var(--e2) var(--e3);
  border-bottom: var(--linea) solid var(--borde);
  font-size: var(--t-tabla);
  cursor: pointer;
}
.buscador__item:last-child { border-bottom: 0; }
.buscador__item:hover,
.buscador__item--activo { background: var(--fila-hover); }

/* Segunda linea de un resultado: SKU, stock, saldo. */
.buscador__item-sub {
  font-family: var(--mono);
  font-size: var(--t-badge);
  color: var(--texto-3);
}

.buscador__vacio {
  padding: var(--e3);
  color: var(--texto-2);
  font-size: var(--t-mini);
  text-align: center;
}

/* ALIAS: lista de sugerencias escrita a mano en una vista. */
.sugerencias {
  position: absolute;
  top: calc(100% + var(--e1));
  left: 0;
  right: 0;
  z-index: var(--z-menu);
  max-height: var(--alto-sugerencias);
  overflow-y: auto;
  background: var(--superficie);
  border: var(--linea) solid var(--borde);
  border-radius: var(--r-2);
  box-shadow: var(--sombra-2);
}

.sugerencias__item {
  display: flex;
  align-items: center;
  gap: var(--e2);
  padding: var(--e2) var(--e3);
  border-bottom: var(--linea) solid var(--borde);
  font-size: var(--t-tabla);
  cursor: pointer;
}
.sugerencias__item:last-child { border-bottom: 0; }
.sugerencias__item:hover,
.sugerencias__item--activo { background: var(--fila-hover); }

.sugerencias__principal {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.sugerencias__secundario {
  flex: none;
  font-family: var(--mono);
  color: var(--texto-2);
}

.sugerencias__vacio {
  padding: var(--e3);
  color: var(--texto-2);
  font-size: var(--t-mini);
  text-align: center;
}


/* ==========================================================================
   18. RANGO DE FECHAS (DateRange.js)
   Atajos Hoy / Semana / Fin de semana / Mes … + comparacion.
   ========================================================================== */

.rango-fechas {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--e3);
}

.rango-fechas__campo {
  display: flex;
  flex-direction: column;
  gap: var(--e1);
}

.rango-fechas__etiqueta {
  font-size: var(--t-etiqueta);
  text-transform: uppercase;
  letter-spacing: var(--espaciado-etiqueta);
  color: var(--texto-2);
}

.rango-fechas__fecha {
  width: var(--ancho-fecha);
  height: var(--alto-control);
  padding: 0 var(--pad-campo);
  border: var(--linea) solid var(--borde-fuerte);
  border-radius: var(--r);
  background: var(--superficie);
  color: var(--texto);
  font-family: var(--fuente);
  font-size: var(--t-cuerpo);
}
.rango-fechas__fecha:focus {
  outline: none;
  border-color: var(--primario);
  box-shadow: var(--anillo-foco);
}

.rango-fechas__atajos {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e1);
}

.rango-fechas__atajo {
  height: var(--alto-control);
  padding: 0 var(--e3);
  border: var(--linea) solid var(--borde-fuerte);
  border-radius: var(--r);
  background: var(--superficie);
  color: var(--texto);
  font-family: var(--fuente);
  font-size: var(--t-boton);
  text-align: center;
  white-space: nowrap;
  cursor: pointer;
}
.rango-fechas__atajo:hover { background: var(--fila-hover); }
.rango-fechas__atajo:focus-visible {
  outline: none;
  box-shadow: var(--anillo-foco);
}
.rango-fechas__atajo[aria-pressed="true"] {
  background: var(--primario-suave);
  border-color: var(--primario);
  color: var(--primario);
  font-weight: var(--peso-fuerte);
}

.rango-fechas__comparar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e2);
}

.rango-fechas__casilla {
  display: inline-flex;
  align-items: center;
  gap: var(--e1);
  height: var(--alto-control);
  color: var(--texto);
  font-size: var(--t-cuerpo);
  cursor: pointer;
}

.rango-fechas__modo {
  width: var(--ancho-select);
  max-width: 100%;
  height: var(--alto-control);
  padding: 0 var(--pad-campo);
  border: var(--linea) solid var(--borde-fuerte);
  border-radius: var(--r);
  background: var(--superficie);
  color: var(--texto);
  font-family: var(--fuente);
  font-size: var(--t-cuerpo);
}

.rango-fechas__resumen {
  align-self: center;
  min-height: 1em;
  color: var(--texto-2);
  font-size: var(--t-mini);
}


/* ==========================================================================
   19. TECLADO NUMERICO (NumPad.js) — cantidades y montos en tactil
   ========================================================================== */

.numpad-velo {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--e4);
  background: var(--velo);
}

.numpad {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: var(--ancho-acceso);
  max-height: 100%;
  overflow: auto;
  background: var(--superficie);
  border: var(--linea) solid var(--borde);
  border-radius: var(--r-2);
  box-shadow: var(--sombra-2);
}

.numpad__cabecera {
  padding: var(--e3) var(--e4);
  border-bottom: var(--linea) solid var(--borde);
}

.numpad__titulo {
  margin: 0;
  font-size: var(--t-etiqueta);
  font-weight: var(--peso-medio);
  text-transform: uppercase;
  letter-spacing: var(--espaciado-etiqueta);
  color: var(--texto-2);
}

.numpad__sub {
  margin: var(--e1) 0 0;
  font-size: var(--t-mini);
  color: var(--texto-3);
}

.numpad__valor {
  padding: var(--e3) var(--e4);
  border-bottom: var(--linea) solid var(--borde);
  color: var(--acento);
  font-family: var(--mono);
  font-size: var(--t-pos-total);
  font-weight: var(--peso-fuerte);
  line-height: var(--lh-apretada);
  text-align: right;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.numpad__unidad {
  margin-left: var(--e2);
  font-size: var(--t-cuerpo);
  color: var(--texto-2);
}

.numpad__error {
  margin: 0;
  padding: var(--e2) var(--e4);
  background: var(--peligro-suave);
  color: var(--peligro);
  font-size: var(--t-mini);
}

.numpad__teclas {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--e2);
  padding: var(--e3);
}

.numpad__tecla {
  height: var(--pos-alto-cobrar);
  border: var(--linea) solid var(--borde-fuerte);
  border-radius: var(--r);
  background: var(--superficie);
  color: var(--texto);
  font-family: var(--mono);
  font-size: var(--t-titulo);
  font-weight: var(--peso-fuerte);
  /* base.css pone text-align:inherit en <button>: la cifra hay que centrarla. */
  text-align: center;
  cursor: pointer;
  user-select: none;
  touch-action: manipulation;
}
.numpad__tecla:hover  { background: var(--fila-hover); }
.numpad__tecla:active { background: var(--primario-suave); }
.numpad__tecla:focus-visible {
  outline: none;
  box-shadow: var(--anillo-foco);
}
.numpad__tecla:disabled {
  opacity: var(--opacidad-deshabilitada);
  cursor: default;
}

.numpad__tecla--accion {
  font-family: var(--fuente);
  font-size: var(--t-boton);
  color: var(--texto-2);
}

.numpad__pie {
  display: flex;
  gap: var(--e2);
  padding: 0 var(--e3) var(--e3);
}

.numpad__boton {
  flex: 1 1 0;
  height: var(--alto-control-pos);
  min-width: var(--ancho-min-boton);
  border: var(--linea) solid var(--borde-fuerte);
  border-radius: var(--r);
  background: var(--superficie);
  color: var(--texto);
  font-size: var(--t-boton);
  font-weight: var(--peso-fuerte);
  text-align: center;
  cursor: pointer;
}
.numpad__boton:hover { background: var(--fila-hover); }
.numpad__boton:disabled {
  opacity: var(--opacidad-deshabilitada);
  cursor: default;
}

.numpad__boton--primario {
  border-color: var(--primario);
  background: var(--primario);
  color: var(--texto-invertido);
}
.numpad__boton--primario:hover { background: var(--primario-hover); }


/* ==========================================================================
   20. ESCANER POR CAMARA
   Marcado: core/Escaner.php::visor(), que core/Layout.php pinta UNA vez por
   pagina, justo antes de cerrar el <body>, en TODAS las pantallas.
   Comportamiento: assets/js/components/EscanerCamara.js.

   El visor es una CAPA FIJA A PANTALLA COMPLETA, no una caja centrada dentro
   de un velo: el video tiene que ocupar la pantalla entera para que un EAN-13
   se lea a 15-25 cm. Nace con el atributo hidden y §0 lo apaga con
   display:none, asi que mientras nadie pulse "Escanear" no pinta nada.

   TRES COSAS QUE NO SE TOCAN:
     · .escaner__mira lleva pointer-events:none. Es DECORACION, no un recorte
       (el decodificador lee todo el encuadre). Si capturase toques, taparia el
       boton de cerrar y el usuario se quedaria encerrado en la camara.
     · La mira es GRANDE a proposito. Si es pequena la gente acerca demasiado
       el telefono, deja de enfocar y el escaner "no lee".
     · Los botones de la barra miden --alto-control-pos (44 px): se pulsan con
       el pulgar, con el telefono en una mano y el producto en la otra.

   Las medidas de la mira son ergonomia fisica —la distancia a la que enfoca la
   camara de un telefono—, no una decision de diseno, y por eso son literales
   aqui y no tokens: CONTRATO_UI.md §13 las declara como excepcion viva.
   ========================================================================== */

.escaner {
  /* Medidas propias del visor, reunidas arriba para no repetirlas.
     78vw/340px x 52vw/230px es el recuadro que deja el codigo a la distancia
     de enfoque; -55 % lo sube un poco sobre el centro, que es donde cae la
     mano al apuntar; 38 px de esquina con 4 px de trazo se ven sobre
     cualquier fondo sin tapar el codigo. */
  --escaner-mira-ancho: min(78vw, 340px);
  --escaner-mira-alto: min(52vw, 230px);
  --escaner-mira-esquina: 38px;
  --escaner-mira-trazo: 4px;
  --escaner-mira-alzado: -55%;

  position: fixed;
  inset: 0;
  /* Por encima del drawer (--z-drawer) y del modal: se escanea DESDE dentro de
     una ficha o de un formulario en panel. Queda por debajo de --z-toast a
     proposito, porque los avisos del escaner ("no se dio permiso a la camara")
     salen por Toast y tienen que verse aunque el visor siga en pantalla. */
  z-index: var(--z-modal);
  overflow: hidden;
  background: var(--acento);
  color: var(--texto-invertido);
  /* El gesto de arrastrar no debe desplazar la pantalla de detras. */
  touch-action: none;
  overscroll-behavior: contain;
}

/* El video ocupa la capa entera. object-fit:cover recorta lo que sobra en vez
   de deformar: un codigo de barras estirado no lo lee ningun decodificador. */
.escaner__video {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  background: var(--acento);
  object-fit: cover;
}

/* La mira: cuatro esquinas sueltas, centradas un poco por encima del centro. */
.escaner__mira {
  position: absolute;
  top: 50%;
  left: 50%;
  width: var(--escaner-mira-ancho);
  height: var(--escaner-mira-alto);
  transform: translate(-50%, var(--escaner-mira-alzado));
  /* OBLIGATORIO: sin esto la mira se come los toques y no se puede cerrar. */
  pointer-events: none;
}

.escaner__mira i {
  position: absolute;
  width: var(--escaner-mira-esquina);
  height: var(--escaner-mira-esquina);
  border: var(--escaner-mira-trazo) solid var(--texto-invertido);
  /* Halo oscuro de 1 px: la esquina blanca tiene que verse tambien sobre una
     etiqueta blanca, que es el fondo mas comun de un codigo de barras. */
  box-shadow: 0 0 0 var(--linea) var(--velo);
}

.escaner__mira i:nth-child(1) {
  top: 0;
  left: 0;
  border-right: 0;
  border-bottom: 0;
  border-top-left-radius: var(--r-2);
}
.escaner__mira i:nth-child(2) {
  top: 0;
  right: 0;
  border-left: 0;
  border-bottom: 0;
  border-top-right-radius: var(--r-2);
}
.escaner__mira i:nth-child(3) {
  bottom: 0;
  left: 0;
  border-right: 0;
  border-top: 0;
  border-bottom-left-radius: var(--r-2);
}
.escaner__mira i:nth-child(4) {
  bottom: 0;
  right: 0;
  border-left: 0;
  border-top: 0;
  border-bottom-right-radius: var(--r-2);
}

/* Barra superior: titulo, linterna y cerrar. Va sobre el video, con velo
   detras para que el texto blanco se lea aunque apunte a una pared clara.
   El calc() con env() aparta la barra de la muesca del telefono; si el
   navegador no conoce env(), esa declaracion se cae y manda el padding de la
   linea de arriba (el mismo patron que ya usa pos.css en su barra inferior). */
.escaner__barra {
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  display: flex;
  align-items: center;
  gap: var(--e2);
  padding: var(--e2) var(--e3);
  padding-top: calc(var(--e2) + env(safe-area-inset-top, 0px));
  background: var(--velo);
}

.escaner__titulo {
  flex: 1;
  min-width: 0;
  margin: 0;
  overflow: hidden;
  color: var(--texto-invertido);
  font-size: var(--t-cuerpo);
  font-weight: var(--peso-fuerte);
  line-height: var(--lh);
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Boton de la barra (linterna y cerrar): cuadrado de 44 px, area de toque
   completa. Solo lleva un <svg> dentro, asi que necesita su aria-label o su
   title: lo pone core/Escaner.php. */
.escaner__boton {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--alto-control-pos);
  height: var(--alto-control-pos);
  min-width: var(--alto-control-pos);
  padding: 0;
  border: var(--linea) solid var(--texto-invertido);
  border-radius: var(--r);
  background: transparent;
  color: var(--texto-invertido);
  cursor: pointer;
}
.escaner__boton:hover { background: var(--velo); }
.escaner__boton:focus-visible {
  outline: none;
  box-shadow: var(--anillo-foco);
}

/* Linterna encendida: se invierte, para que se vea de un vistazo que esta
   consumiendo bateria. El estado lo lleva aria-pressed, no una clase, porque
   asi lo canta tambien el lector de pantalla. */
.escaner__boton[aria-pressed="true"] {
  background: var(--texto-invertido);
  color: var(--acento);
}

/* Rotulo de estado, abajo y centrado. Es role="status" aria-live="polite":
   por aqui salen "Apunta al codigo de barras", la sugerencia de los 4 s y el
   aviso de los 9 s. Dos capas del mismo velo, porque una sola no basta para
   leer texto blanco sobre un video claro. Vacio no ocupa sitio. */
.escaner__estado {
  position: absolute;
  bottom: calc(var(--e5) + env(safe-area-inset-bottom, 0px));
  left: 50%;
  max-width: calc(100% - var(--e6));
  margin: 0;
  padding: var(--e2) var(--e3);
  transform: translateX(-50%);
  border-radius: var(--r-2);
  background-color: var(--velo);
  background-image: linear-gradient(var(--velo), var(--velo));
  color: var(--texto-invertido);
  font-size: var(--t-cuerpo);
  line-height: var(--lh);
  text-align: center;
}
.escaner__estado:empty { display: none; }

/* Horizontal en un telefono: la altura util se queda en nada. La mira vuelve
   al centro exacto y el rotulo sube, para que ni la barra de arriba ni el
   estado de abajo se le monten encima. */
@media (orientation: landscape) and (max-height: 560px) {

  .escaner { --escaner-mira-alzado: -50%; }

  .escaner__barra {
    padding: var(--e1) var(--e3);
    padding-top: calc(var(--e1) + env(safe-area-inset-top, 0px));
  }

  .escaner__estado { bottom: calc(var(--e2) + env(safe-area-inset-bottom, 0px)); }
}


/* ==========================================================================
   21. GRAFICO (Chart.js) — SVG propio, sin librerias
   ========================================================================== */

.grafico { width: 100%; }

.grafico svg {
  display: block;
  width: 100%;
  height: auto;
}

.grafico__vacio {
  margin: 0;
  padding: var(--e5) var(--e3);
  border: var(--linea) solid var(--borde);
  border-radius: var(--r);
  background: var(--superficie-2);
  color: var(--texto-2);
  font-size: var(--t-cuerpo);
  text-align: center;
}


/* ==========================================================================
   22. ESTADOS OBLIGATORIOS: vacio, cargando, error, sin permiso
   Toda pantalla los tiene (§21 del contrato).
   ========================================================================== */

.vacio,
.estado-cargando,
.estado-error,
.estado-sin-permiso {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--e3);
  padding: var(--e6) var(--e4);
  text-align: center;
  color: var(--texto-2);
}

.vacio__texto,
.estado-error__texto,
.estado-sin-permiso__texto { font-size: var(--t-cuerpo); }

.estado-error__texto       { color: var(--peligro); }
.estado-sin-permiso__texto { color: var(--texto-2); }

/* Variante de error del estado vacio (Table.js y Drawer.js la usan). */
.vacio--error .vacio__texto { color: var(--peligro); }

.estado-cargando {
  flex-direction: row;
  font-size: var(--t-mini);
}

/* "Cargando…" mientras el drawer trae su contenido. */
.cargando {
  padding: var(--e5);
  color: var(--texto-2);
  font-size: var(--t-mini);
  text-align: center;
}

.spinner {
  display: inline-block;
  width: var(--spinner);
  height: var(--spinner);
  flex: none;
  border: var(--grosor-spinner) solid var(--borde-fuerte);
  border-top-color: var(--primario);
  border-radius: var(--r-circulo);
  animation: sival-girar 700ms linear infinite;
}

/* Barra gris que ocupa el sitio de un dato mientras carga (Table.js). */
.esqueleto {
  display: block;
  height: var(--e2);
  border-radius: var(--r);
  background: var(--neutro-suave);
}
.tabla__fila--esqueleto .esqueleto { display: block; }


/* ==========================================================================
   23. CAMPANA DE ALERTAS (barra superior)
   ========================================================================== */

.campana { position: relative; }

.campana__boton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--alto-control);
  height: var(--alto-control);
  border-radius: var(--r);
  color: var(--texto-2);
}
.campana__boton:hover { background: var(--superficie-2); }
.campana__boton:focus-visible {
  outline: none;
  box-shadow: var(--anillo-foco);
}
.campana__icono {
  width: var(--icono-2);
  height: var(--icono-2);
}

.campana__contador {
  position: absolute;
  top: var(--e1);
  right: var(--e1);
  min-width: var(--e4);
  height: var(--e4);
  padding: 0 var(--e1);
  border-radius: var(--r);
  background: var(--peligro);
  color: var(--texto-invertido);
  font-family: var(--mono);
  font-size: var(--t-badge);
  line-height: var(--e4);
  text-align: center;
  pointer-events: none;
}
.campana__contador:empty { display: none; }

.campana__panel {
  display: none;
  position: absolute;
  top: calc(100% + var(--e1));
  right: 0;
  z-index: var(--z-menu);
  width: var(--ancho-panel-alertas);
  max-width: calc(100vw - var(--e5));
  max-height: var(--alto-panel-alertas);
  overflow-y: auto;
  background: var(--superficie);
  border: var(--linea) solid var(--borde);
  border-radius: var(--r-2);
  box-shadow: var(--sombra-2);
}
.campana.esta-abierta .campana__panel { display: block; }

.campana__cabecera {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e2);
  position: sticky;
  top: 0;
  padding: var(--e2) var(--e3);
  background: var(--fondo-cabecera-tabla);
  border-bottom: var(--linea) solid var(--borde);
  font-size: var(--t-etiqueta);
  font-weight: var(--peso-fuerte);
  text-transform: uppercase;
  letter-spacing: var(--espaciado-etiqueta);
  color: var(--texto-2);
}

.campana__item {
  display: flex;
  align-items: flex-start;
  gap: var(--e2);
  padding: var(--e2) var(--e3);
  border-bottom: var(--linea) solid var(--borde);
  color: var(--texto);
  font-size: var(--t-mini);
}
.campana__item:last-child { border-bottom: 0; }
.campana__item:hover {
  background: var(--fila-hover);
  text-decoration: none;
}
.campana__item--no-leida { background: var(--primario-suave); }

.campana__punto {
  width: var(--e2);
  height: var(--e2);
  flex: none;
  margin-top: var(--e1);
  border-radius: var(--r-circulo);
  background: var(--aviso);
}
.campana__punto--peligro { background: var(--peligro); }

.campana__texto {
  flex: 1;
  min-width: 0;
}

.campana__fecha {
  flex: none;
  font-family: var(--mono);
  font-size: var(--t-micro);
  color: var(--texto-3);
}

.campana__vacio {
  padding: var(--e4);
  text-align: center;
  color: var(--texto-2);
  font-size: var(--t-mini);
}


/* ==========================================================================
   24. HOJAS IMPRIMIBLES — estructura y vista previa en pantalla
   assets/css/print.css se enlaza con media="print", asi que solo existe al
   imprimir. La forma de una hoja se declara aqui (para que la vista previa se
   vea igual que el papel) y print.css se queda con lo propio del papel:
   @page, saltos, quitar el marco y pasar todo a tinta.

   Las medidas del papel A4 (210 x 297 mm) son las unicas literales de este
   archivo: no son una decision de diseno, son el tamano fisico de la hoja.
   Con el margen de tokens.css (--margen-a4: 12 mm) queda un area util de
   186 x 273 mm, que se reparte exacta:
     a4-24  3 x 8  ->  62,0 x 34,125 mm por etiqueta
     a4-10  2 x 5  ->  93,0 x 54,600 mm por etiqueta
     tira   1 x 10 -> 186,0 x 27,300 mm por etiqueta
   ========================================================================== */

.hoja-etiquetas {
  --hoja-ancho: 210mm;
  --hoja-alto: 297mm;
  --et-columnas: 3;
  --et-filas: 8;
  --et-precio: var(--t-kpi);
  --et-nombre: var(--t-tabla);
}

.hoja-etiquetas--a4-24 {
  --et-columnas: 3;
  --et-filas: 8;
  --et-precio: var(--t-kpi);
  --et-nombre: var(--t-tabla);
}

.hoja-etiquetas--a4-10 {
  --et-columnas: 2;
  --et-filas: 5;
  --et-precio: var(--t-pos-cambio);
  --et-nombre: var(--t-cuerpo);
}

.hoja-etiquetas--tira {
  --et-columnas: 1;
  --et-filas: 10;
  --et-precio: var(--t-kpi);
  --et-nombre: var(--t-cuerpo);
}

/* Una hoja de papel. En pantalla lleva marco para que se entienda el corte. */
.hoja {
  width: var(--hoja-ancho, 210mm);
  height: var(--hoja-alto, 297mm);
  max-width: 100%;
  margin: 0 auto var(--e5);
  padding: var(--margen-a4);
  overflow: hidden;
  background: var(--imp-fondo);
  border: var(--linea) solid var(--borde);
  box-shadow: var(--sombra);
}

.hoja__rejilla {
  display: grid;
  grid-template-columns: repeat(var(--et-columnas), 1fr);
  grid-template-rows: repeat(var(--et-filas), 1fr);
  height: 100%;
  border-top: var(--linea) dashed var(--imp-borde-suave);
  border-left: var(--linea) dashed var(--imp-borde-suave);
}

.etiqueta {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--e1);
  padding: var(--e2);
  border-right: var(--linea) dashed var(--imp-borde-suave);
  border-bottom: var(--linea) dashed var(--imp-borde-suave);
  color: var(--imp-texto);
  overflow: hidden;
}

.etiqueta__datos { min-width: 0; }

.etiqueta__producto {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  font-size: var(--et-nombre);
  font-weight: var(--peso-fuerte);
  line-height: var(--lh);
}

.etiqueta__presentacion {
  font-size: var(--t-badge);
  text-transform: uppercase;
  letter-spacing: var(--espaciado-etiqueta);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.etiqueta__cifra {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--e2);
  min-width: 0;
}

.etiqueta__precio {
  font-family: var(--mono);
  font-size: var(--et-precio);
  font-weight: var(--peso-total);
  line-height: var(--lh-apretada);
  white-space: nowrap;
}

.etiqueta__codigo {
  font-family: var(--mono);
  font-size: var(--t-badge);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Tira de estanteria: el nombre a la izquierda y el precio a la derecha. */
.hoja-etiquetas--tira .etiqueta {
  flex-direction: row;
  align-items: center;
  gap: var(--e4);
}
.hoja-etiquetas--tira .etiqueta__datos { flex: 1; }
.hoja-etiquetas--tira .etiqueta__cifra { flex: none; align-items: baseline; }
.hoja-etiquetas--tira .etiqueta__producto { -webkit-line-clamp: 1; }

/* Celdas de relleno de la ultima hoja: guardan la cuadricula para cortar derecho. */
.etiqueta--vacia { background: var(--imp-fondo); }

/* Barra de herramientas sobre una hoja (no se imprime).
   .etiquetas-barra es ALIAS del nombre que uso primero la vista de etiquetas. */
.hoja-barra,
.etiquetas-barra {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--e3);
  margin-bottom: var(--e4);
  padding: var(--e3) var(--e4);
  background: var(--superficie);
  border: var(--linea) solid var(--borde);
  border-radius: var(--r-2);
}

.hoja-barra__datos,
.etiquetas-barra__datos {
  font-size: var(--t-mini);
  color: var(--texto-2);
}
.hoja-barra__datos b,
.etiquetas-barra__datos b { color: var(--texto); }

/* 24.1 Guia imprimible de dos partes (transferencias, entregas) ------------- */

.guia-partes {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e4);
  margin-bottom: var(--e4);
}

.guia-parte {
  flex: 1 1 var(--ancho-nombre);
  min-width: 0;
  padding: var(--e3);
  border: var(--linea) solid var(--borde);
  border-radius: var(--r-2);
  background: var(--superficie);
}

.guia-parte__titulo {
  margin: 0 0 var(--e1);
  font-size: var(--t-etiqueta);
  text-transform: uppercase;
  letter-spacing: var(--espaciado-etiqueta);
  color: var(--texto-2);
}

.guia-parte__nombre {
  margin: 0 0 var(--e1);
  font-weight: var(--peso-fuerte);
}

.guia-parte__dato {
  margin: 0;
  font-size: var(--t-mini);
  color: var(--texto-2);
}

.guia-notas {
  margin: 0 0 var(--e3);
  font-size: var(--t-mini);
  color: var(--texto-2);
}

.guia-firmas {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e5);
  margin-top: var(--e6);
}

.guia-firma {
  flex: 1 1 var(--ancho-select);
  min-width: 0;
}

.guia-firma__linea {
  height: var(--e5);
  margin: 0 0 var(--e1);
  border-bottom: var(--linea) solid var(--texto-2);
}

.guia-firma__texto {
  margin: 0;
  font-size: var(--t-mini);
  color: var(--texto-2);
  text-align: center;
}


/* ==========================================================================
   25. ANIMACIONES (solo indicadores de estado y entrada de panel)
   OJO: --transicion-panel ya trae la curva ("160ms ease"), asi que en el
   atajo `animation` NO se vuelve a escribir la curva: repetirla invalida la
   declaracion entera y el panel se quedaria sin entrada.
   ========================================================================== */

@keyframes sival-girar {
  to { transform: rotate(360deg); }
}

@keyframes sival-entrar {
  from { opacity: 0; transform: translateY(var(--e2)); }
  to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .btn,
  .chip,
  .nav-enlace,
  .drawer,
  .drawer-fondo,
  .drawer-fondo > .drawer,
  .modal,
  .modal-fondo,
  .confirmar-fondo,
  .toast,
  .tabla__acciones .btn--icono {
    transition: none;
    animation: none;
  }
  /* El panel no se anima nunca (ver §14): aqui solo se deja explicito que su
     sitio es dentro de la pantalla. */
  .drawer-fondo > .drawer { transform: translateX(0); }
}


/* ==========================================================================
   26. TACTIL, PANEL COMPLETO Y MOVIL PEQUENO
   ========================================================================== */

/* 26.1 Areas de 44 px por debajo de 768 px ---------------------------------- */

@media (max-width: 768px) {

  .btn {
    height: var(--alto-control-pos);
    font-size: var(--t-cuerpo);
  }
  /* En tactil no hay boton pequeno: el §9.8 pide 44 px de area de toque, y eso
     manda sobre la densidad. La fila de tabla ya mide --alto-fila-tactil. */
  .btn--sm {
    height: var(--alto-control-pos);
    font-size: var(--t-mini);
  }
  .tabla__acciones .menu__disparador { height: var(--alto-control-pos); }

  /* La cabecera ordenable se toca entera, no solo el texto. */
  .tabla__boton-orden {
    height: 100%;
    min-height: var(--alto-control-pos);
  }
  .btn--icono { width: var(--alto-control-pos); }
  .btn--icono.btn--sm { width: var(--alto-control); }

  .campo__control,
  .campo input:not([type="checkbox"]):not([type="radio"]),
  .campo select,
  .buscador__entrada { height: var(--alto-control-pos); }

  .campo textarea { min-height: calc(var(--alto-control-pos) * 2); }

  /* La casilla crece hasta el maximo que permite el §9.8 (20 px). El area de
     toque de 44 px la da el bloque .campo--casilla, que es la etiqueta
     entera. En la matriz de permisos la casilla va suelta dentro de una celda
     de 44 px: ahi el alto lo pone la fila. */
  .campo--casilla input[type="checkbox"],
  .campo--casilla input[type="radio"],
  .tabla__celda input[type="checkbox"],
  .tabla__celda input[type="radio"] {
    width: var(--icono-tactil);
    height: var(--icono-tactil);
  }

  /* Filas mas altas y acciones siempre visibles (no hay hover) */
  .tabla td,
  .tabla th,
  .tabla__celda,
  .tabla__th { height: var(--alto-fila-tactil); }
  .tabla__acciones .btn--icono { opacity: 1; }

  /* La paginacion envuelve y la cuenta deja de estar pegada a la derecha */
  .tabla-paginacion { flex-wrap: wrap; }
  .tabla-paginacion__info { margin-left: 0; }
  .tabla-paginacion__select { height: var(--alto-control-pos); }
  .tabla-paginacion__anterior,
  .tabla-paginacion__siguiente {
    height: var(--alto-control-pos);
    min-width: var(--alto-control-pos);
  }

  .chip { height: var(--alto-control-pos); font-size: var(--t-cuerpo); }

  /* Pestanas, casillas y enlaces dentro de una celda: 44 px de toque (§9.8). */
  .tabs__enlace { height: var(--alto-control-pos); }

  .campo--casilla { min-height: var(--alto-control-pos); }

  /* Cabecera de un desplegable (<details> de /ayuda): se abre con el dedo. */
  summary {
    min-height: var(--alto-control-pos);
    padding-top: var(--e2);
    padding-bottom: var(--e2);
  }

  .tabla td button,
  .tabla__celda button { min-height: var(--alto-control-pos); }

  .paginacion__boton,
  .paginacion__tamano { height: var(--alto-control-pos); }
  .paginacion__boton { width: var(--alto-control-pos); }

  .campana__boton,
  .drawer__cerrar,
  .modal__cerrar {
    width: var(--alto-control-pos);
    height: var(--alto-control-pos);
  }

  .buscador__item,
  .sugerencias__item { padding: var(--e3); }

  .rango-fechas { gap: var(--e2); }
  .rango-fechas__fecha,
  .rango-fechas__atajo,
  .rango-fechas__modo,
  .rango-fechas__casilla { height: var(--alto-control-pos); }

  .toast-pila,
  .toasts {
    left: var(--e3);
    right: var(--e3);
    bottom: var(--e3);
    width: auto;
    max-width: none;
  }

  .filtros { padding: var(--e2); }
  .filtros__limpiar { margin-left: 0; }

  .grupo-botones,
  .formulario__pie { flex-wrap: wrap; }

  .barra-acciones__botones { flex: 1 1 100%; justify-content: stretch; }
  .barra-acciones__botones .btn { flex: 1 1 auto; }

  /* La rejilla de formulario pasa a 4 columnas, igual que .grid-12 */
  .formulario__rejilla { grid-template-columns: repeat(4, 1fr); gap: var(--e2); }
}

/* 26.2 Panel a pantalla completa por debajo de 560 px ------------------------ */

@media (max-width: 560px) {

  /*
     El drawer ocupa todo el ancho. Como el ancho pedido llega por
     --drawer-ancho (no como style="width:…"), basta con reescribirlo:
     no hace falta !important y el pie de botones sigue visible porque el
     cuerpo es lo unico que se desplaza.
  */
  .drawer,
  .drawer--ancho {
    --drawer-ancho: 100%;
    width: 100%;
    min-width: 0;
    max-width: none;
    border-left: 0;
  }

  .drawer          { --drawer-pad: var(--e3); }
  .drawer__cabecera { padding: 0 var(--e3); }
  .drawer__pie      { padding: var(--e2) var(--e3); }

  .modal-fondo,
  .confirmar-fondo { padding: var(--e2); }

  .modal,
  .modal--confirmar,
  .modal__caja { max-width: 100%; }

  .modal__cuerpo { padding: var(--e3); }
  .modal__pie    { padding: var(--e2) var(--e3); }

  /* .escaner NO entra aqui: ya no es una caja centrada con ancho maximo, sino
     una capa fija a pantalla completa (§20). */
  .numpad { max-width: 100%; }
}

/* 26.3 Movil pequeno (360 px) ---------------------------------------------- */

@media (max-width: 360px) {

  /* A este ancho los botones hermanos ocupan la fila completa */
  .grupo-botones .btn,
  .formulario__pie .btn,
  .drawer__pie .btn,
  .modal__pie .btn {
    flex: 1;
    min-width: 0;
  }

  .kpis { grid-template-columns: 1fr; }

  .campana__panel { width: calc(100vw - var(--e4)); }

  .paginacion,
  .tabla-paginacion {
    flex-wrap: wrap;
    justify-content: space-between;
    gap: var(--e2);
  }

  .filtros .campo,
  .filtros .campo--busqueda,
  .filtros .campo--nombre,
  .filtros .campo--select,
  .filtros .campo--codigo { flex: 1 1 100%; max-width: none; }

  .formulario__rejilla { grid-template-columns: 1fr; }

  .rango-fechas__campo,
  .rango-fechas__fecha { width: 100%; }

  .ficha-datos { grid-template-columns: 1fr; }
}


/* ==========================================================================
   27. PIEZAS DE PANTALLA — lo que emiten los modulos

   Aqui viven las clases que dibujan las vistas y los .js de modules/. Estan en
   esta hoja, y no en un <style> dentro de la vista, por las dos reglas del
   CONTRATO_UI: una vista no lleva <style> y solo tokens.css declara colores y
   tamanos. Cada bloque dice que pantalla lo escribe.

   Cuando una de estas piezas se repita en otra fase, sube a su seccion
   canonica (tabla, ficha, kpi...) y aqui queda solo el alias.
   ========================================================================== */

/* 27.1 Catalogo — lista de productos (catalogo/js/productos.js) ------------- */

/* Miniatura de 32 px en la primera columna. Sin foto se pinta la inicial. */
.prod-foto {
  display: block;
  width: var(--e6);
  height: var(--e6);
  border: var(--linea) solid var(--borde);
  border-radius: var(--r);
  object-fit: cover;
  background: var(--superficie-2);
}

.prod-foto--vacia {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--texto-3);
  font-family: var(--mono);
  font-size: var(--t-mini);
  text-transform: uppercase;
}

/* SKU y marcas del producto, en una segunda linea bajo el nombre. */
.prod-marcas {
  display: block;
  color: var(--texto-3);
  font-family: var(--mono);
  font-size: var(--t-badge);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 27.2 Catalogo — arbol de categorias (catalogo/js/clasificacion.js) -------- */

/* Marca de sangria de una subcategoria: un trazo, no un icono. */
.rama {
  display: inline-block;
  width: var(--e4);
  height: var(--e0);
  margin-right: var(--e1);
  border-top: var(--linea) solid var(--borde-fuerte);
  vertical-align: middle;
}

/* La categoria madre delante del nombre. */
.contexto { color: var(--texto-3); }

/* 27.3 Catalogo — ficha completa del producto ------------------------------ */

/* El titulo lleva los badges de estado en la misma linea. */
.ficha-titulo { align-items: center; gap: var(--e2); }

/* Caja de una pestana que se trae por red (presentaciones, precios, alias). */
.tab-remoto { min-width: 0; }

.tab-remoto__estado {
  margin: 0;
  padding: var(--e4) 0;
  color: var(--texto-2);
  font-size: var(--t-mini);
}

/* 27.4 Catalogo — presentaciones editables en linea ------------------------ */

/* Fila de una presentacion. Es el gancho que busca presentaciones.js; el
   aspecto lo pone .tabla. */
.pres__fila { min-width: 0; }

.pres__fila--inactiva .campo__control { color: var(--texto-3); }

/* Fila con cambios sin guardar: franja lateral, sin repintar la fila entera. */
.pres__fila--sucia > td:first-child {
  box-shadow: inset var(--linea-2) 0 0 var(--aviso);
}

.pres__campo--error {
  border-color: var(--peligro);
  box-shadow: var(--anillo-foco-peligro);
}

.pres__margen {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
}
.pres__margen--bajo    { color: var(--aviso); }
.pres__margen--perdida { color: var(--peligro); font-weight: var(--peso-fuerte); }

/* 27.4b Catalogo — resumen de la importacion CSV (catalogo/js/importar.js) -- */

/* El recuento de lo que se aplico ("Productos creados: 12"). Es una lista de
   datos, no una vinieta: sin punto ni sangria, y las cifras en monoespaciada
   como en cualquier otra tabla. */
.previa-notas {
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--t-tabla);
  color: var(--texto-2);
}
.previa-notas > li {
  padding: var(--e1) 0;
  font-variant-numeric: tabular-nums;
}
.previa-notas > li + li { border-top: var(--linea) solid var(--borde); }

/* 27.5 Existencias — stock, kardex y ajuste (modules/inventario) ----------- */

.inv-stock,
.inv-kardex,
.inv-ajuste { display: block; min-width: 0; }

/* Barra sobre la tabla: a la izquierda de que local se habla, a la derecha
   las acciones de la pantalla. */
.inv-barra {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--e3);
  flex-wrap: wrap;
  margin-bottom: var(--e3);
}
.inv-barra__texto { margin: 0; font-size: var(--t-mini); color: var(--texto-2); }
.inv-barra__acciones { display: flex; align-items: center; gap: var(--e2); }

.inv-semaforo { display: flex; align-items: flex-end; gap: var(--e1); flex-wrap: wrap; }
.inv-estados  { display: inline-flex; gap: var(--e1); flex-wrap: wrap; }

/* Celda de producto: nombre arriba, SKU debajo en monoespaciada. */
.inv-producto { display: flex; flex-direction: column; min-width: 0; }
.inv-producto__nombre {
  color: var(--texto);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.inv-producto__sku {
  color: var(--texto-3);
  font-family: var(--mono);
  font-size: var(--t-badge);
}

/* La cantidad es un enlace al kardex de ese producto. */
.inv-enlace-stock {
  font-family: var(--mono);
  color: var(--primario);
  text-decoration: underline;
  text-underline-offset: var(--e0);
  cursor: pointer;
}
.inv-enlace-stock:hover { color: var(--primario-hover); }
.inv-enlace-stock--peligro { color: var(--peligro); }

/* Cuadre del kardex contra el stock actual. */
.inv-cuadre {
  display: flex;
  align-items: flex-start;
  gap: var(--e3);
  margin-bottom: var(--e3);
  padding: var(--e3);
  border: var(--linea) solid var(--borde);
  border-left: var(--linea-2) solid var(--exito);
  border-radius: var(--r-2);
  background: var(--superficie);
  color: var(--texto-2);
  font-size: var(--t-mini);
}
.inv-cuadre--descuadre { border-left-color: var(--peligro); color: var(--peligro); }
.inv-cuadre__cifra { font-family: var(--mono); font-weight: var(--peso-fuerte); }

/* Resumen de cabecera del kardex y del ajuste. Misma forma que .ficha-datos. */
.inv-ficha {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--ancho-numero), 1fr));
  gap: var(--e2) var(--e3);
  margin-bottom: var(--e3);
  padding: var(--e3);
  background: var(--superficie-2);
  border: var(--linea) solid var(--borde);
  border-radius: var(--r-2);
}
.inv-ficha__dato { display: flex; flex-direction: column; gap: var(--e0); min-width: 0; }
.inv-ficha__dato--ancho { grid-column: 1 / -1; }
.inv-ficha__etiqueta {
  color: var(--texto-2);
  font-size: var(--t-etiqueta);
  letter-spacing: var(--espaciado-etiqueta);
  text-transform: uppercase;
}
.inv-ficha__valor {
  color: var(--texto);
  font-family: var(--mono);
  font-size: var(--t-cuerpo);
}

.inv-rango { margin-bottom: var(--e3); }

/* La diferencia del ajuste: la cifra que el usuario tiene que mirar. */
.inv-ajuste__diferencia {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--e3);
  margin: var(--e3) 0;
  padding: var(--e3);
  border: var(--linea) solid var(--borde);
  border-left: var(--linea-2) solid var(--borde-fuerte);
  border-radius: var(--r-2);
  background: var(--superficie-2);
}
.inv-ajuste__diferencia--sube  { border-left-color: var(--exito); }
.inv-ajuste__diferencia--baja  { border-left-color: var(--aviso); }
.inv-ajuste__diferencia--grave { border-left-color: var(--peligro); }
.inv-ajuste__cifra {
  color: var(--acento);
  font-family: var(--mono);
  font-size: var(--t-titulo);
  font-weight: var(--peso-fuerte);
}
.inv-ajuste__pie   { color: var(--texto-2); font-size: var(--t-mini); }
.inv-ajuste__modos { display: flex; gap: var(--e3); }
.inv-ajuste__aviso { margin: var(--e2) 0 0; color: var(--aviso); font-size: var(--t-mini); }

/* 27.6 Existencias — mermas (inventario/js/mermas.js) ---------------------- */

/* KPI cuyo valor es un texto (la causa que mas pesa), no una cifra. */
.kpi--texto .kpi__valor {
  font-family: var(--fuente);
  font-size: var(--t-titulo);
  line-height: var(--lh);
}

.mermas-intro { margin: 0 0 var(--e3); color: var(--texto-2); font-size: var(--t-mini); }

/* Ficha de la merma en el panel lateral: pares clave / valor en filas. */
.merma-ficha { display: flex; flex-direction: column; gap: var(--e2); }
.merma-ficha__fila {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--e3);
  padding-bottom: var(--e2);
  border-bottom: var(--linea) solid var(--borde);
}
.merma-ficha__fila:last-child { border-bottom: 0; }
.merma-ficha__clave {
  flex: none;
  color: var(--texto-2);
  font-size: var(--t-etiqueta);
  letter-spacing: var(--espaciado-etiqueta);
  text-transform: uppercase;
}
.merma-ficha__valor { min-width: 0; text-align: right; }
.merma-ficha__valor--num {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
}
.merma-ficha__valor--total {
  color: var(--acento);
  font-family: var(--mono);
  font-weight: var(--peso-total);
}
.merma-ficha__motivo { margin: var(--e2) 0 0; color: var(--texto-2); font-size: var(--t-mini); }
.merma-ficha__foto {
  display: block;
  width: 100%;
  margin-top: var(--e2);
  border: var(--linea) solid var(--borde);
  border-radius: var(--r-2);
}

/* Sugerencia del buscador de producto: nombre arriba, stock a la derecha. */
.merma-form__sug-nombre { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.merma-form__sug-stock {
  margin-left: auto;
  color: var(--texto-2);
  font-family: var(--mono);
  font-size: var(--t-mini);
}

/* 27.7 Existencias — transferencias y recepcion --------------------------- */

.transf-sugerencia { display: block; min-width: 0; }
.transf-linea__sku {
  display: block;
  color: var(--texto-3);
  font-family: var(--mono);
  font-size: var(--t-badge);
}
.transf-presentacion { max-width: var(--ancho-select); }
.transf-cantidad {
  max-width: var(--ancho-numero);
  font-family: var(--mono);
  text-align: right;
}
.transf-quitar { color: var(--peligro); }
.transf-quitar:hover { background: var(--peligro-suave); }

/* Linea que pide mas de lo que hay, o que llega incompleta. */
.transf-linea--corta > td:first-child,
.recibir__fila--corta > td:first-child {
  box-shadow: inset var(--linea-2) 0 0 var(--peligro);
}
.transf-linea--corta .transf-cantidad { border-color: var(--peligro); }

.recibir__resumen--falta {
  border-left: var(--linea-2) solid var(--peligro);
  color: var(--peligro);
}

/* 27.8 Personas y configuracion — usuarios, roles y locales ---------------- */

.usuarios-cuenta { font-family: var(--mono); }
.locales-dias    { font-family: var(--mono); letter-spacing: var(--espaciado-etiqueta); }

/* Matriz de permisos: 44 filas por 5 roles. */
.matriz__tabla { min-width: 0; }
.matriz__permiso-nombre { color: var(--texto); }

/* Casilla tocada y todavia sin guardar. */
.matriz__casilla--cambiada { background: var(--aviso-suave); }

/* Tarjeta de rol con cambios pendientes y su aviso al pie. */
.rol-tarjeta--sucia { border-left: var(--linea-2) solid var(--aviso); }
.roles-aviso--activo { color: var(--aviso); font-weight: var(--peso-medio); }

/* 27.9 Ajustes — gastos fijos --------------------------------------------- */

.gastos__marca  { margin-left: var(--e2); }
.gastos__local  { color: var(--texto-2); }
.gastos__estado { white-space: nowrap; }
.gastos__acciones { display: inline-flex; gap: var(--e1); justify-content: flex-end; }
.gastos__inactivo .tabla__flexible { color: var(--texto-3); }

/* 27.10 Auditoria --------------------------------------------------------- */

.auditoria__mono {
  font-family: var(--mono);
  font-size: var(--t-mini);
  white-space: nowrap;
}

/* 27.10b Selector de local de la barra superior ---------------------------- */

/*
   Elegir mal el local descuadra stock y caja, asi que el selector se queda a la
   vista tambien en tablet. Por debajo de 560 px la barra ya no da mas de si y
   se esconde; ahi el local se cambia desde /locales.
*/
.barra-superior__local {
  display: inline-flex;
  align-items: center;
  min-width: 0;
}
.barra-superior__local .campo__control { min-width: 0; }

@media (max-width: 560px) {
  .barra-superior__local { display: none; }
}

/* 27.11 Paginas de error (403, 404, 500) ---------------------------------- */

.pagina-error {
  max-width: var(--ancho-lectura);
  margin: var(--e6) auto;
  text-align: center;
}
.pagina-error__acciones {
  display: flex;
  justify-content: center;
  gap: var(--e2);
  margin: var(--e4) 0 0;
}

/* 27.14 Combos — constructor, lista y produccion (modules/kits) ------------ */

/*
   El taller del combo: a la izquierda el formulario y a la derecha el panel de
   cuentas, que se queda a la vista mientras se edita (es lo que avisa cuando el
   margen cae por debajo del minimo). Por debajo de 768 px el panel pasa arriba
   en una sola columna.
*/
/*
   Dos columnas cuando el contenido da para las dos y una sola cuando no. Se
   decide por el ANCHO DISPONIBLE, no por el de la pantalla: con la barra
   lateral desplegada el contenido es mas estrecho que el navegador y una
   consulta de medios no lo sabe. Las bases del flex son los anchos del §9.3:
   el editor necesita el de un buscador y el panel el de un selector; cuando
   los dos no caben, el panel baja solo.
*/
.kit-taller {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--e5);
}

.kit-taller__editor {
  flex: 3 1 var(--ancho-busqueda);
  min-width: 0;
}

.kit-taller__panel {
  position: sticky;
  top: var(--e4);
  flex: 1 1 var(--ancho-select);
  min-width: 0;
}

/* Franja de aviso del panel cuando el margen queda bajo el minimo del combo. */
.kit-taller__panel--alerta > .tarjeta {
  border-left: var(--linea-2) solid var(--aviso);
}

/* El desglose es una tabla: si el panel se estrecha, se desplaza ella sola. */
.kit-panel__lineas {
  margin-top: var(--e4);
  overflow-x: auto;
}
.kit-panel__lineas .tabla { width: 100%; }

/* Lista de componentes del constructor. */
.kit-lista {
  display: flex;
  flex-direction: column;
  gap: var(--e2);
}

.kit-lista__vacio {
  margin: var(--e2) 0;
  color: var(--texto-3);
  font-size: var(--t-mini);
}

/* Una fila de componente: nombre a la izquierda, campos al centro, quitar al final. */
.kit-comp {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--e3);
  padding: var(--e2) var(--e3);
  border: var(--linea) solid var(--borde);
  border-radius: var(--r);
  background: var(--superficie);
}

.kit-comp--obsequio { border-left: var(--linea-2) solid var(--exito); }

/*
   El nombre del componente ocupa su propia linea y los controles van debajo:
   asi la fila no crece a tres lineas cuando el combo lleva casillas y el
   nombre nunca sale truncado a 768 px.
*/
.kit-comp__datos {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--e2);
  flex: 1 1 100%;
  min-width: 0;
}

.kit-comp__nombre {
  font-weight: var(--peso-medio);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Segunda linea gris: SKU, presentacion o el desglose de una cantidad. */
.kit-comp__sku {
  display: block;
  color: var(--texto-3);
  font-family: var(--mono);
  font-size: var(--t-badge);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.kit-comp__campos {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--e3);
}

.kit-comp__campos .campo {
  margin-bottom: 0;
  flex: 0 0 auto;
  width: var(--ancho-numero);
}
.kit-comp__campos .campo--select { width: var(--ancho-select); }
.kit-comp__campos .campo--casilla { width: auto; align-self: center; }
.kit-comp__campos .campo--casilla .campo__etiqueta { font-size: var(--t-mini); }

.kit-comp__acciones {
  display: flex;
  align-items: center;
  gap: var(--e2);
  margin-left: auto;
}

.kit-comp__pie {
  color: var(--texto-3);
  font-family: var(--mono);
  font-size: var(--t-badge);
}

/* Grupo de eleccion: "Elige tu gaseosa" con sus opciones. */
.kit-grupo {
  padding: var(--e3);
  border: var(--linea) solid var(--borde);
  border-radius: var(--r-2);
  background: var(--superficie-2);
  margin-bottom: var(--e3);
}

.kit-grupo__cabecera {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--e3);
}

.kit-grupo__cabecera .campo { margin-bottom: 0; }
.kit-grupo__cabecera .campo--casilla { align-self: center; }
.kit-grupo__cabecera > .btn { margin-left: auto; }

.kit-grupo__opciones {
  display: flex;
  flex-direction: column;
  gap: var(--e2);
  margin: var(--e3) 0 0;
}

.kit-grupo__vacio {
  margin: var(--e2) 0 0;
  color: var(--texto-3);
  font-size: var(--t-mini);
}

.kit-grupo__buscador { margin: var(--e3) 0 0; }

/* Motivos por los que un combo dejo de ofrecerse (lista y panel). */
.kit-motivos {
  margin: var(--e1) 0 0;
  padding: 0;
  list-style: none;
}

.kit-motivo {
  color: var(--aviso);
  font-size: var(--t-badge);
  line-height: var(--lh);
}

/* Previa de la produccion: lo que va a salir y a entrar de bodega. */
.ens-previa { margin: var(--e4) 0 0; }

@media (max-width: 768px) {
  .kit-taller { grid-template-columns: 1fr; }

  .kit-taller__panel {
    position: static;
    order: -1;
  }

  .kit-comp__acciones { margin-left: 0; }
}

/* ==========================================================================
   28. Pantalla de acceso con fotografía del local
   ==========================================================================
   La vista pasa la imagen en la variable --acceso-imagen (su URL depende de la
   subcarpeta de instalación). Si no la pasa, la pantalla sigue siendo correcta:
   queda el fondo liso de siempre. */

.acceso {
  min-height: 100vh;
  background-color: var(--acento);
  background-image: var(--acceso-imagen, none);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

/* Velo sobre la foto: la imagen tiene zonas muy iluminadas (las estanterías) y
   otras casi negras. Sin velo, el pie en claro desaparece sobre las primeras. */
.acceso::before {
  content: "";
  position: fixed;
  inset: 0;
  background: var(--acceso-velo);
  pointer-events: none;
}

.acceso > * {
  position: relative;
}

/* El pie va sobre la foto, no sobre superficie: necesita color propio. */
.acceso__pie {
  color: var(--acceso-pie);
}
