/* =====================================================================
   HOJA DE ESTILOS CENTRALIZADA

   Ni un solo color literal en este fichero: todo se resuelve contra las
   variables que `base.html` inyecta en :root a partir de la paleta que la
   empresa eligio en su panel. Cambiar de esquema de color es cambiar una
   fila en la base de datos, no tocar CSS.

   Las utilidades de Tailwind v4 se compilan aparte a
   `static/css/tailwind.build.css` y se sirven en local (sin CDN).
   Este fichero define los COMPONENTES y los tokens de marca.
   ===================================================================== */

/* --------------------------------------------------------------------
   Tokens derivados
   -------------------------------------------------------------------- */
:root {
  --lateral-ancho: 16rem;
  --cabecera-alto: 3.5rem;
  /* Lo ancho que se pone el contenido de un panel a hoja completa. Lo demas es
     blanco a los lados: la sabana es del panel, pero el texto no. Subirlo
     aprovecha mas un monitor grande; bajarlo centra mas. */
  --hoja-medida: 72rem;

  /* Lo alto que puede ponerse una tabla DENTRO de un panel antes de hacer
     scroll ella sola. El tope existe para que el resto del formulario —el
     carrito, el desglose, el boton de guardar— siga a la vista: sin el, un
     catalogo de cincuenta y ocho filas empuja todo lo demas fuera de la
     pantalla. A hoja completa hay sitio de sobra, asi que sube. */
  --tabla-panel: 20rem;
  --tabla-panel-completa: 62vh;
  --transicion: 160ms ease;

  /* Tipografia y tamano DE FABRICA. Cada empresa escoge los suyos en
     Identidad visual y base.html los reescribe en un <style> posterior a esta
     hoja (theming/tipografias.py). Estos quedan para cualquier pagina que se
     pinte sin el tema. Las diez familias se sirven en local desde
     static/vendor/fuentes/ (scripts/vendorizar-fuentes.mjs). */
  --fuente: 'IBM Plex Sans', system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --tamano-texto: 100%;
  --sombra: 0 1px 3px rgb(0 0 0 / 0.08), 0 1px 2px rgb(0 0 0 / 0.04);
}

/* `x-cloak` evita el parpadeo de los elementos que Alpine controla antes
   de que el framework se haya inicializado. */
[x-cloak] { display: none !important; }

* { box-sizing: border-box; }

/* El tamano que escoge la empresa se aplica a la RAIZ: todo el sistema esta
   medido en rem, asi que texto, rellenos, anchos del menu y alto de las tablas
   crecen juntos y en proporcion. Un porcentaje, y no pixeles, para respetar el
   tamano que el usuario ya tenga puesto en su navegador. */
html { font-size: var(--tamano-texto); }

body {
  margin: 0;
  background: var(--fondo);
  color: var(--texto);
  font-family: var(--fuente);
  font-size: 0.9375rem;
  line-height: 1.5;
  /* Cifras de ancho fijo con CUALQUIERA de las diez familias. IBM Plex Sans
     las trae por defecto y por eso se eligio; las demas las tienen pero de
     serie las dan proporcionales, y con ellas las columnas de importes dejaban
     de quedar alineadas en vertical. */
  font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------------------
   Disposicion general
   -------------------------------------------------------------------- */
.disposicion { display: flex; min-height: 100vh; }

/* El menu es un CAJON que flota, no una columna que empuja.

   Cerrado NO deja carril: el `aside` no ocupa NADA en la fila y el contenido
   usa el ancho completo. Lo que se despliega es `.lateral__panel`, posicionado
   en absoluto encima del contenido, asi que abrir y cerrar no reflujan la
   pagina —con el ancho del `aside` animandose, rozar el borde izquierdo con el
   raton reflujaba la pagina entera, tabla y cabecera incluidas—.

   `sticky` la deja pegada al alto de la ventana: un cajon al que solo se llega
   desplazandose hasta arriba del todo en un listado de cincuenta filas no
   serviria para navegar, que es justo para lo que esta. */
.lateral {
  position: sticky;
  top: 0;
  align-self: flex-start;
  height: 100vh;
  width: 0;
  flex-shrink: 0;
  /* Por debajo del panel derecho (50): con una ficha abierta manda la ficha. */
  z-index: 40;
}

.lateral__panel {
  position: absolute;
  inset: 0 auto 0 0;
  width: var(--lateral-ancho);
  background: var(--primary);
  color: var(--contraste-primary);
  display: flex;
  flex-direction: column;
  /* Los rotulos no caben en el carril plegado y `white-space: nowrap` los
     desborda: sin esto asomarian por encima del contenido mientras se cierra. */
  overflow: hidden;
  transition: width var(--transicion), visibility var(--transicion);
  box-shadow: 4px 0 20px rgb(0 0 0 / 0.18);
}
.lateral--colapsada .lateral__panel {
  /* Cerrado del todo: ni iconos ni carril. La unica puerta es el boton de la
     cabecera, y mientras esta cerrado el contenido tiene la pantalla entera. */
  width: 0;
  /* `visibility` ademas del ancho: `overflow: hidden` solo recorta lo que se
     VE, y los enlaces de un panel de cero de ancho siguen recibiendo el
     tabulador. Sin esto, quien navega a teclado cae en un menu invisible y no
     tiene forma de saber donde esta el foco. */
  visibility: hidden;
  /* Cerrado no flota sobre nada: la sombra ahi solo ensucia el borde. */
  box-shadow: none;
}

.lateral__marca {
  height: var(--cabecera-alto);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 0.75rem;
  border-bottom: 1px solid rgb(255 255 255 / 0.1);
}
.lateral__logo { max-height: 2rem; max-width: 100%; object-fit: contain; }
.lateral__texto { font-weight: 600; letter-spacing: 0.01em; }

.lateral__nav { padding: 0.5rem; overflow-y: auto; flex: 1; }

.lateral__enlace {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem 0.75rem;
  border-radius: var(--radio);
  color: inherit;
  text-decoration: none;
  white-space: nowrap;
  transition: background var(--transicion);
}
.lateral__enlace:hover { background: rgb(255 255 255 / 0.08); }
.lateral__enlace[aria-current="page"] {
  background: var(--accent);
  color: var(--contraste-accent);
}
.lateral__enlace i { width: 1.25rem; text-align: center; flex-shrink: 0; }

.principal { flex: 1; display: flex; flex-direction: column; min-width: 0; }

.cabecera {
  height: var(--cabecera-alto);
  /* Ancla del desplegable de usuario, que se posiciona en absoluto. */
  position: relative;
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0 1rem;
  background: var(--superficie);
  border-bottom: 1px solid var(--borde);
}
.cabecera__titulo { font-size: 1rem; font-weight: 600; margin: 0; flex: 1; }
.cabecera__alternar {
  background: none;
  border: 0;
  color: var(--texto-suave);
  cursor: pointer;
  padding: 0.5rem;
  border-radius: var(--radio);
}
.cabecera__alternar:hover { background: var(--fondo); color: var(--primary-texto); }

/* Los botones de la pantalla. En escritorio se ven igual que cuando eran hijos
   sueltos de la cabecera —mismo hueco de 1 rem—; el grupo existe para que en
   pantalla estrecha bajen JUNTOS a su propia fila (ver «Responsive»).

   Vacio no se pinta: un hijo de cero de ancho en una fila flex sigue sumando
   su hueco, y la tasa se correria 1 rem en cada pantalla sin botones. Va con
   `:has` y no con `:empty` porque el bloque deja espacios en blanco, y para
   `:empty` eso ya es contenido. */
.cabecera__acciones { display: flex; align-items: center; gap: 1rem; }
.cabecera__acciones:not(:has(*)) { display: none; }

/* --- La tasa del BCV en la cabecera --------------------------------------
   Va en cifras TABULARES. IBM Plex Sans se eligio justamente por eso y aqui
   importa el doble: la tasa cambia cada dia y el ojo la compara con la que
   recuerda; con cifras de ancho variable, 827,7371 y 811,1119 no ocupan lo
   mismo y esa comparacion deja de ser inmediata.

   `flex-shrink: 0` porque el titulo de la cabecera lleva `flex: 1` y en una
   pantalla estrecha se comeria la tasa hasta partirle la cifra. Lo que cede es
   el titulo, que se puede leer a medias; una tasa a medias es un numero
   distinto.

   SIN borde visible: es un dato y no se pulsa. Con el mismo recuadro que los
   botones de al lado se leia como uno mas, y ellos como la tasa. El borde
   queda transparente para no mover ni un pixel la cabecera. */
.tasa-cabecera {
  display: inline-flex;
  align-items: baseline;
  gap: 0.375rem;
  flex-shrink: 0;
  padding: 0.25rem 0.5rem;
  border: 1px solid transparent;
  border-radius: var(--radio);
  background: var(--fondo);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.tasa-cabecera__rotulo {
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--texto-suave);
}
.tasa-cabecera__cifra { font-size: 0.875rem; font-weight: 600; color: var(--texto); }

/* Sin tasa NO se pinta en rojo, y es deliberado: no es un error del usuario ni
   algo que pueda arreglar desde la pantalla, y una alarma permanente en la
   cabecera de todas las pantallas se deja de mirar a los dos dias. Se apaga,
   que es lo que dice «aqui falta un dato» sin gritar. */
.tasa-cabecera--ausente .tasa-cabecera__cifra {
  font-weight: 400;
  font-style: italic;
  color: var(--texto-suave);
}

/* En pantalla estrecha se queda la cifra y se va el rotulo: el recuadro con
   cuatro decimales ya solo puede ser una cosa, y son 2,5 rem que en un movil
   competian con el nombre del usuario. */
@media (max-width: 640px) {
  .tasa-cabecera__rotulo { display: none; }
}

.contenido { padding: 1.25rem; flex: 1; }

/* --------------------------------------------------------------------
   Banda de impersonacion
   -------------------------------------------------------------------- */
.banda-impersonacion {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  padding: 0.5rem 1rem;
  background: var(--peligro);
  color: #fff;
  font-size: 0.8125rem;
  /* Rayado diagonal: hace imposible confundir esta sesion con una normal. */
  background-image: repeating-linear-gradient(
    45deg,
    rgb(0 0 0 / 0.12) 0 10px,
    transparent 10px 20px
  );
}
.banda-impersonacion form { margin: 0; }

/* --------------------------------------------------------------------
   Componentes
   -------------------------------------------------------------------- */
/* UN BOTON TIENE QUE PARECER UN BOTON.

   Lo que se pulsa se distingue de lo que se lee por varias senales a la vez
   —trazo, cuerpo, sombra—, porque una sola no basta en una pantalla con
   brillo ni para quien distingue mal el color:

   - El peso es 600 y no 500. De cada familia se sirven SOLO los pesos 400 y
     600 (`scripts/vendorizar-fuentes.mjs`), y un 500 que no existe el
     navegador lo resuelve hacia abajo, a 400: el rotulo de TODOS los botones
     salia con el mismo trazo que el texto corrido de la tabla de al lado.
   - Una sombra corta los levanta del fondo. Al pulsar se hunden un pixel y la
     pierden: es la respuesta al clic antes de que llegue la del servidor.
   - El foco de teclado es el anillo de acento de los campos, por FUERA del
     borde para no confundirse con el.

   Los rellenos no cambian: un boton mas alto descuadraria las tablas que lo
   llevan en cada renglon.

   EL ESTILO es el de la muestra que se escogio el 26-10-2026: un degradado en
   diagonal entre DOS tonos vecinos —en la muestra, violeta a indigo—, un filo
   claro por dentro y un halo del propio color. Los dos tonos salen del color
   de la EMPRESA girandolo 14 grados a cada lado (`oklch(from …)`), nunca de un
   violeta escrito aqui: cada empresa escoge su paleta en Identidad visual y un
   color fijo la ignoraria en todos los botones a la vez.

   Medido en las diez paletas con `_luminancia()`: el arranque conserva el
   contraste de letra que ya tenia cada color —por eso no se aclara: aclararlo
   bajaba el naranja de 2,80:1 a 2,30:1— y el final, 0,05 mas oscuro, lo sube.
   Con giros de 30 grados el dorado de Borgona se volvia verde oliva.

   Un navegador que no entienda `oklch(from …)` descarta el degradado entero y
   pinta el `background-color` de debajo: el boton sigue siendo del color de la
   empresa, solo que liso. */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.875rem;
  border: 1px solid transparent;
  border-radius: var(--radio);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
  box-shadow: 0 1px 2px rgb(0 0 0 / 0.08);
  transition: background var(--transicion), border-color var(--transicion),
              box-shadow var(--transicion), filter var(--transicion);
}
.btn-sm { padding: 0.3125rem 0.625rem; font-size: 0.8125rem; }

.btn:hover:not(:disabled) { box-shadow: 0 2px 6px rgb(0 0 0 / 0.12); }
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* LOS BOTONES DE COLOR: la muestra tal cual, con el tono de cada variante.

   El SECUNDARIO —250 apariciones, el que mas se usa— lleva el tono OSCURO de la
   marca, `--primary-texto`, con letra blanca. Fue blanco con un borde a
   1,23:1 y despues una version pastel, y las dos fallaron por lo mismo: el
   tono salia del PRINCIPAL, y una empresa que escoge el principal BLANCO en
   Identidad visual —chifletos, 26-10-2026— no tiene ningun color ahi. El
   pastel quedaba gris sobre gris y los botones no se distinguian del fondo.

   `--primary-texto` es el principal oscurecido hasta 7:1 contra el fondo
   (`_tinta_de()`), asi que existe SIEMPRE: con un principal oscuro es el mismo
   color, y con uno claro, su version oscura —gris carbon para el blanco—. La
   letra blanca pasa de 7,6:1 en los dos extremos del degradado, en las diez
   paletas y en principales claros de la rueda —medido con `_luminancia()`—.

   El PRIMARIO va con el mismo tono por la misma razon: con `--primary` a
   secas, un principal blanco lo dejaba blanco sobre blanco. La accion
   principal de cada pantalla la distingue el ACENTO, que es otro tono.

   EL SECUNDARIO VA ANTES QUE EL ACENTO, y no es estetica. Facturacion marca el
   filtro escogido poniendo `btn-acento` ENCIMA de `btn-secundario`; con la
   misma especificidad gana la regla que se lee la ultima, y declarado detras
   el secundario pisaba al acento: el filtro activo salia igual que los demas
   —medido en Chrome—.

   `--tono-boton` y no `--tono`, que ya es de las graficas del panel de ventas.
   Cada variante fija tambien su BORDE, el filo claro.

   Y ni siquiera el tono oscuro del principal bastaba: una empresa quiere sus
   botones de UN color, no del que le salga a su marca. Desde el 27-10-2026
   los dos llevan el suyo, escogido en Identidad visual —`--boton` y
   `--boton-principal`, con su letra—, y lo de arriba queda de RESPALDO dentro
   del `var()`: es «Automatico». Sin color propio el tema no declara la
   variable (`paletas.botones_de()`) y el boton pinta lo de siempre; por eso
   tambien un tema guardado en cache antes de existir la opcion sigue
   pintandolos, en vez de dejarlos sin fondo. */
.btn-secundario,
.btn-primario  { --tono-boton: var(--boton, var(--primary-texto)); color: var(--contraste-boton, #fff); }
.btn-acento    { --tono-boton: var(--boton-principal, var(--accent));
                 color: var(--contraste-boton-principal, var(--contraste-accent)); }
.btn-peligro   { --tono-boton: var(--peligro); color: #fff; }

.btn-secundario, .btn-primario, .btn-acento, .btn-peligro {
  background-color: var(--tono-boton);
  background-image: linear-gradient(135deg in oklch,
    oklch(from var(--tono-boton) l c calc(h + 14)),
    oklch(from var(--tono-boton) calc(l - 0.05) c calc(h - 14)));
  border-color: rgb(255 255 255 / 0.25);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.2),
              0 2px 8px -2px color-mix(in srgb, var(--tono-boton) 55%, transparent);
}
.btn-secundario:hover:not(:disabled),
.btn-primario:hover:not(:disabled),
.btn-acento:hover:not(:disabled),
.btn-peligro:hover:not(:disabled) {
  filter: brightness(1.08);
  border-color: rgb(255 255 255 / 0.25);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.2),
              0 4px 14px -3px color-mix(in srgb, var(--tono-boton) 70%, transparent);
}

/* Desactivar y salir de la sesion. Sin borde era, literalmente, un texto rojo.
   El fondo NO se tine ni con el raton encima: el rojo de la paleta da 4,83:1
   sobre blanco, y con un 5 % de tinte baja a 4,47, por debajo del 4,5:1 de
   WCAG AA. */
.btn-alerta {
  background: var(--superficie);
  color: var(--peligro);
  border-color: color-mix(in srgb, var(--peligro) 40%, var(--superficie));
}
.btn-alerta:hover { border-color: var(--peligro); }

/* Al pulsar se hunde un pixel y pierde sombra y halo. Va DETRAS de las
   variantes a proposito: con la misma especificidad que su `:hover`, delante
   perdia contra el halo y el boton de color no se hundia. */
.btn:active:not(:disabled) { transform: translateY(1px); box-shadow: none; filter: none; }

.btn:disabled { opacity: 0.55; cursor: not-allowed; }

/* Un envio en vuelo. Lo pone `js/envio-unico.js` en CUALQUIER boton de envio
   del sistema, venga de una plantilla o de otra.

   El usuario tiene que VER que su clic hizo algo: el problema que esto resuelve
   no es que el segundo clic se cuele, es que la pantalla se queda muda y por
   eso el usuario vuelve a pulsar. El giro es la respuesta inmediata. */
.boton--enviando { position: relative; cursor: progress; }
.boton--enviando::after {
  content: "";
  position: absolute;
  top: 50%;
  right: 0.625rem;
  width: 0.75rem;
  height: 0.75rem;
  margin-top: -0.375rem;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: giro-envio 0.6s linear infinite;
}
@keyframes giro-envio { to { transform: rotate(360deg); } }

/* Quien pidiera no animar nada se queda sin giro, pero no sin el aviso: el
   borde queda quieto y el cursor sigue diciendo que hay algo en curso. */
@media (prefers-reduced-motion: reduce) {
  .boton--enviando::after { animation: none; }
}

.tarjeta {
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  box-shadow: var(--sombra);
  padding: 1rem;
}

.etiqueta {
  display: inline-block;
  padding: 0.125rem 0.5rem;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 600;
}
.etiqueta--pendiente { background: var(--accent-suave); color: var(--accent-hover); }
.etiqueta--pagado    { background: #DCFCE7; color: var(--exito); }
.etiqueta--anulado   { background: #FEE2E2; color: var(--peligro); }

/* Tablas: el desbordamiento se contiene en el propio bloque, la pagina
   nunca hace scroll horizontal. */
.tabla-envoltorio { overflow-x: auto; border-radius: var(--radio); border: 1px solid var(--borde); }
.tabla { width: 100%; border-collapse: collapse; background: var(--superficie); }
.tabla th {
  background: var(--fondo);
  text-align: left;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--texto-suave);
  padding: 0.625rem 0.75rem;
  white-space: nowrap;
}
.tabla td { padding: 0.625rem 0.75rem; border-top: 1px solid var(--borde); }

/* La cabecera no se va con el scroll: en una tabla que se desplaza por dentro,
   a la tercera fila ya no se sabe que columna es cual.

   Va en el `th` y no en el `thead` porque `position: sticky` sobre un `thead`
   no lo respetan todos los navegadores. Y necesita fondo opaco —lo tiene: el
   `background` de arriba— o el contenido se leeria por debajo.

   En los listados de pagina no hace nada y no estorba: su envoltorio no tiene
   tope de altura, asi que no hay scroll vertical del que despegarse. */
.tabla-envoltorio th { position: sticky; top: 0; z-index: 1; }
.tabla tbody tr:hover { background: var(--fondo); }
.tabla .numerico { text-align: right; font-variant-numeric: tabular-nums; }

/* Filas FINAS: apenas mas altas que su texto.

   Son para las listas con las que se TRABAJA a teclado —las coincidencias del
   buscador del ajuste y sus renglones—, no para las de consulta. Con el relleno
   normal, doce coincidencias no caben en pantalla y resaltar la siguiente con
   la flecha deja de significar nada porque esa fila no se ve; y veinte
   renglones alejan el boton de guardar. Aqui la altura de la fila es un limite
   de cuantas se pueden operar sin desplazarse, no una preferencia estetica.

   El `input` pierde ademas el margen que `.campo` le pone: dentro de una celda
   no hay etiqueta debajo de la que separarse. */
.tabla--fina td, .tabla--fina th { padding: .25rem .5rem; }
.tabla--fina .campo { margin-bottom: 0; gap: 0; }
.tabla--fina input, .tabla--fina select {
  padding: .1875rem .375rem;
  font-size: .8125rem;
}

/* La fila que las flechas tienen marcada. Va por CLASE y no por `:focus`: el
   foco se queda en el buscador mientras se recorre la lista con las flechas —si
   saltara a la fila, cada pulsacion sacaria el cursor del campo donde se esta
   escribiendo el codigo.

   El color se pinta en las CELDAS y la barra lateral es una sombra del primer
   `<td>`. Puesto en el `<tr>`, como estaba, el navegador no pinta la sombra:
   con `border-collapse: collapse` la fila no tiene area propia donde pintarla.
   La marca quedaba reducida a un tinte palido, y recorrer la lista a ciegas
   seguia siendo recorrerla a ciegas.

   Dos senales y no una, porque un tinte claro solo no basta en una pantalla con
   brillo. Lo que NO se toca es el grosor de la letra: cambiarlo por fila
   recalcularia el ancho de las columnas y la tabla daria un salto en cada
   pulsacion de la flecha. */
.tabla tbody tr.fila--resaltada > td,
.tabla tbody tr.fila--resaltada:hover > td {
  background: var(--accent-suave);
}
.tabla tbody tr.fila--resaltada > td:first-child {
  box-shadow: inset 4px 0 0 var(--accent);
}

/* Un listado de PAGINA con la cabecera quieta.

   `.tabla-envoltorio th` ya es `sticky`, pero en un listado de pagina eso no
   hace nada: quien se desplaza es la pagina entera, asi que la cabecera no
   tiene de que despegarse. Esta clase le da al envoltorio su propio tope de
   altura y su propio scroll, y entonces el `sticky` significa algo.

   No se aplica a todos los listados y por eso es una clase aparte: acotar la
   altura parte el scroll de la pagina en dos barras, y eso solo compensa donde
   se TRABAJA sobre la lista sin perder de vista la cabecera —hoy, escoger los
   documentos de una hoja de despacho—, no donde solo se mira. */
.tabla-envoltorio--alta { max-height: 60vh; overflow-y: auto; }

/* La fila de OTRA sede mientras se arma una hoja de despacho.

   Un camion sale de un muelle, asi que en cuanto hay un documento marcado los
   de las demas sedes dejan de poder marcarse. Se apagan en vez de esconderse a
   proposito: desaparecer filas de una lista que se esta recorriendo hace perder
   el sitio, y ademas la fila apagada explica por si sola por que no se puede
   escoger —la columna de sede sigue ahi al lado—. */
.tabla tbody tr.fila--ajena > td,
.tabla tbody tr.fila--ajena:hover > td { opacity: .45; }

/* La fila que abre el grupo de un almacen en el inventario.

   Lleva el fondo de la cabecera de columnas para leerse como un rotulo y no
   como un dato mas, y un borde mas grueso encima para que el paso de un
   almacen a otro se vea bajando deprisa. Es un `<tr>` del mismo `<tbody>` y
   no un `<tbody>` propio porque el scroll cose los lotes como `<tr>` sueltos.

   El nombre va en el color principal del tema y la barra de la izquierda en
   el de acento: son las dos senales que dejan ubicar cada almacen de un
   vistazo. El acento NO va en la letra: esta pensado como fondo, y como texto
   sobre `--fondo` se queda entre 1.7:1 y 3.6:1 segun la paleta, por debajo
   del 4.5:1 de WCAG AA en las diez; el principal pasa de 7:1 en todas. La
   sede no cambia porque `.hallazgo__rif` trae su propio color. La barra es
   una sombra de la celda por lo mismo que en `.fila--resaltada`, y va sin su
   tinte de fondo para no confundirse con una fila escogida. */
.tabla tbody tr.fila--grupo > td {
  background: var(--fondo);
  border-top: 2px solid var(--borde);
  color: var(--primary-texto);
  box-shadow: inset 4px 0 0 var(--accent);
}

/* La franja que separa dos partes de un mismo listado (Facturacion: lo que falta
   por emitir, y debajo lo emitido). Delgada y en el tono suave del acento, el
   segundo color de la empresa: tiene que partir la tabla sin competir con los
   datos. Va en acento y no en el principal porque el principal puede ser blanco y
   la franja no se veria. La letra es la de `.pestana--activa`, la misma pareja. */
.tabla tbody tr.fila--franja > td {
  padding: .1875rem .75rem;
  background: var(--accent-suave);
  color: var(--accent-hover);
  font-size: .75rem;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
}

/* Las casillas de seleccion de una tabla, en el acento como las de un formulario. */
.marca-fila, .marca-todas { accent-color: var(--accent); cursor: pointer; }

/* El numero de parada, en la propia fila. Es lo que hace innecesaria una
   segunda lista creciendo debajo de la tabla: el orden que se lleva armado se
   lee donde ya se esta mirando.

   Ancho FIJO aunque este vacio. Sin el, marcar la primera casilla ensancharia
   la columna y toda la tabla daria un salto lateral justo al empezar a
   trabajar. */
.parada__marca { display: flex; align-items: center; gap: .5rem; cursor: pointer; }
.parada__orden {
  display: inline-block;
  min-width: 1.5rem;
  text-align: center;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--accent);
}

/* Las dos flechas de orden: en la cabecera del listado de documentos y en cada
   parada de la hoja. Son el mismo control y por eso comparten clase.

   Existen las DOS a la vez y no un boton que alterne: despues de un intercambio
   de HTMX la cabecera no se vuelve a pintar, asi que un boton que guardara el
   estado en el servidor se quedaria diciendo lo contrario de lo que hace. */
.orden { display: inline-flex; gap: .125rem; margin-left: .25rem; }
.parada__flechas { display: inline-flex; flex-direction: column; margin-left: .25rem; vertical-align: middle; }
.orden__flecha {
  background: none;
  border: 1px solid transparent;
  border-radius: var(--radio);
  color: var(--texto-suave);
  cursor: pointer;
  /* Alto minimo de blanco de dedo. Las flechas de una parada son el camino de
     la TABLETA —el arrastre nativo no existe ahi— y quien las usa esta de pie
     en un muelle: a 15 px de alto se falla y se mueve la parada de al lado.
     El ancho lo da el relleno; el alto tiene que declararse porque un icono de
     .75rem no llega solo. */
  min-height: 1.75rem;
  padding: .125rem .375rem;
  line-height: 1;
  font-size: .75rem;
}
.orden__flecha:hover { background: var(--fondo); border-color: var(--borde); color: var(--texto); }
.orden__flecha--activa { color: var(--accent); border-color: var(--accent); }

/* La parada que se esta arrastrando. Translucida y no con borde: el borde
   cambia la altura de la fila y las demas dan un salto al levantarla, que es lo
   contrario de lo que hace falta para saber donde se va a soltar. */
.parada--arrastrando td { opacity: .4; }
tbody.parada[draggable="true"] { cursor: grab; }

/* Los tres botones de entrega, en su propia fila DEBAJO de los datos de la
   parada.

   Estuvieron en una septima columna y saturaban la tabla: tres botones por fila
   obligaban al `.tabla-envoltorio` a desplazarse en horizontal en cuanto la
   ventana bajaba de 900 px —medido—, con el cliente y el punto de entrega
   compitiendo por el ancho que se llevaban los botones. Debajo no compiten con
   nadie y la tabla recupera su ancho util.

   Aqui SI se permite `wrap`: la fila es suya y crecer una linea en una ventana
   estrecha no descoloca ninguna columna, que era el motivo de prohibirlo antes.

   Y no se ocultan tras un menu: son la accion que se repite una vez por parada,
   y esconderla detras de un clic mas convierte una ruta de treinta entregas en
   sesenta clics. */
.parada__acciones > td {
  /* Sin borde superior: esta fila y la de arriba son la MISMA parada, y una
     linea entre las dos las leeria como dos registros distintos. El separador
     de verdad es el borde del `<tbody>`, mas abajo. */
  border-top: 0;
  padding-top: 0;
  padding-bottom: .5rem;
}
.acciones-parada {
  display: flex;
  gap: .375rem;
  flex-wrap: wrap;
  align-items: center;
}
.acciones-parada .btn { white-space: nowrap; }
.acciones-parada__rotulo {
  font-size: .75rem;
  color: var(--texto-suave);
  text-transform: uppercase;
  letter-spacing: .04em;
  margin-right: .25rem;
}

/* La separacion entre paradas la marca el GRUPO, no cada fila: cada parada es
   un `<tbody>` con sus dos filas dentro, y el borde va entre grupos para que las
   dos filas de una misma parada se lean como una sola cosa. */
#paradas-tabla tbody.parada + tbody.parada > tr:first-child > td {
  border-top: 1px solid var(--borde);
}

/* La cabecera del historial: el titulo a la izquierda y el acceso al completo a
   la derecha, en la misma linea. */
.historial__cabecera {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: .75rem;
  margin: 1.5rem 0 .5rem;
}
.historial__titulo { font-size: 1rem; margin: 0; }

/* Los tres papeles de una hoja de despacho.

   Van en una tarjeta del cuerpo y NO en `acciones_cabecera`: la cabecera es una
   fila de alto fijo y sin `wrap`, asi que tres botones mas el de anular y el
   menu de usuario la desbordan y ponen a la pagina entera a hacer scroll
   horizontal —medido a 474 px, donde ningun otro listado desborda—. Aqui ademas
   cabe decir para que sirve cada uno, que hace falta: «ciego» no se entiende por
   el nombre, y sacar el papel equivocado estropea el conteo del galpon. */
.papeles { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: .75rem; }
.papel {
  display: flex;
  gap: .625rem;
  align-items: flex-start;
  padding: .75rem;
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  background: var(--superficie);
  color: var(--texto);
  text-decoration: none;
}
.papel:hover { border-color: var(--accent); background: var(--fondo); }
.papel > i { color: var(--accent); margin-top: .15rem; }
.papel strong { display: block; }
.papel small { color: var(--texto-suave); font-size: .75rem; }

/* El pie del formulario de la hoja nueva: el contador y el boton, juntos.

   El contador tiene tamano FIJO y no crece con lo escogido. Es la leccion del
   catalogo del pedido: una lista que crece mientras se trabaja aleja el boton
   de guardar y obliga a mirar dos sitios para saber lo mismo. */
.hoja-nueva__pie {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-top: .5rem;
  padding-top: .75rem;
  border-top: 1px solid var(--borde);
}

/* `max-width`: dentro de una fila flex, un campo no encoge por debajo de su
   contenido, y el de un `<select>` mide lo que su opción MÁS LARGA. En Backorders y
   Procesar pedidos, «almacén — sede» medía 318 px y a 360 px se salía 9 px de la
   tarjeta. `min-width: 0` también lo evita, pero encoge el campo en escritorio hasta
   su `flex-basis` y corta el nombre; esto sólo actúa cuando de verdad no cabe. */
.campo { display: flex; flex-direction: column; gap: 0.25rem; margin-bottom: 0.875rem; max-width: 100%; }
.campo label { font-size: 0.8125rem; font-weight: 500; color: var(--texto-suave); }
.campo input, .campo select, .campo textarea {
  padding: 0.5rem 0.625rem;
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  background: var(--superficie);
  color: var(--texto);
  font: inherit;
}
.campo input:focus, .campo select:focus, .campo textarea:focus {
  outline: 2px solid var(--accent);
  outline-offset: -1px;
  border-color: var(--accent);
}

/* Un `<select>` ENVUELTO en un `<div>` dentro del campo. Pasa cuando el
   desplegable comparte campo con algo mas —el de cliente vive dentro de su
   bloque de busqueda, junto al recuento de coincidencias—: al no ser hijo
   directo del flex no lo estira nadie y se queda en su ancho natural, media
   fila mas corto que los demas. Se ve como un descuadre, y dentro de una
   rejilla de dos columnas, como una celda a medio llenar. */
.campo > div > select { width: 100%; }

/* La contraseña con su ojo para verla (registro/acceso.html). El botón va
   DENTRO de la caja del campo, y el relleno derecho del campo le guarda el
   sitio para que el texto no corra por debajo del icono. Edge trae su propio
   ojo en todo campo de contraseña (`::-ms-reveal`): sin quitarlo salían dos. */
.clave-visible { position: relative; display: flex; }
.clave-visible input { flex: 1; min-width: 0; padding-right: 2.5rem; }
.clave-visible input::-ms-reveal { display: none; }
.clave-visible__boton {
  position: absolute;
  inset-block: 0;
  right: 0;
  width: 2.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: var(--radio);
  background: none;
  color: var(--texto-suave);
  cursor: pointer;
}
.clave-visible__boton:hover { color: var(--texto); }
.clave-visible__boton:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* Dos campos por fila. Para cabeceras de formulario donde cuatro campos
   apilados gastan media pantalla antes de llegar a lo que se viene a hacer.

   El hueco es solo el HORIZONTAL: el vertical ya lo pone el `margin-bottom` de
   cada `.campo`, y sumar los dos separaria las filas el doble que en el resto
   del formulario.

   `minmax(0, 1fr)` y no `1fr` a secas: `1fr` es `minmax(auto, 1fr)`, asi que un
   `<option>` largo —una razon social con su RIF— ensancha su columna y la otra
   se encoge. Con el minimo en cero las dos miden lo mismo pase lo que pase
   dentro. */
.campos-pareados {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: 1rem;
}

/* Y una sola columna cuando no caben dos. Dos columnas de ocho caracteres no
   son mas compactas, son ilegibles. */
@media (max-width: 40rem) {
  .campos-pareados { grid-template-columns: 1fr; }
}

/* Una casilla suelta no es un campo de texto: no lleva relleno de caja, no
   ocupa el ancho y va AL LADO de su etiqueta, no debajo. `_campos_form.html`
   pinta todos los campos con la misma estructura —esa es su razon de ser—, asi
   que la diferencia se resuelve aqui y no duplicando la plantilla. */
.campo:has(> input[type="checkbox"]) {
  flex-direction: row;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
}
.campo:has(> input[type="checkbox"]) label { order: 2; }
.campo:has(> input[type="checkbox"]) small { order: 3; flex-basis: 100%; }
.campo input[type="checkbox"] {
  order: 1;
  width: auto;
  padding: 0;
  accent-color: var(--accent);
}

/* --------------------------------------------------------------------
   Medidor de la contraseña (`ClaveConMedidor`, erp/forms.py)

   Lo repinta el servidor a cada pausa al teclear. `.htmx-added` deja en
   opacidad cero lo recien insertado durante el asentamiento, y aqui eso era
   un parpadeo de la lista entera en cada pausa: dentro del medidor se anula.
   -------------------------------------------------------------------- */
.medidor-clave {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
  font-size: 0.75rem;
  color: var(--texto-suave);
}
.medidor-clave .htmx-added { opacity: 1; }

.medidor-clave__fuerza { display: flex; align-items: center; gap: 0.5rem; }
.medidor-clave__fuerza--1 { color: var(--peligro); }
.medidor-clave__fuerza--2 { color: var(--alerta); }
.medidor-clave__fuerza--3,
.medidor-clave__fuerza--4 { color: var(--exito); }

.medidor-clave__barra {
  flex: 1;
  height: 0.375rem;
  border-radius: 999px;
  background: var(--borde);
  overflow: hidden;
}
.medidor-clave__barra > span {
  display: block;
  height: 100%;
  background: currentColor;
  transition: width var(--transicion);
}
.medidor-clave__etiqueta { min-width: 4.5rem; font-weight: 600; text-align: right; }

.medidor-clave__requisitos {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  margin: 0;
  padding: 0;
  list-style: none;
}
.medidor-clave__requisitos li { display: flex; align-items: baseline; gap: 0.375rem; }
.medidor-clave__requisitos i { flex-shrink: 0; width: 0.875rem; text-align: center; }
.medidor-clave__cumple { color: var(--exito); }
.medidor-clave__falla { color: var(--peligro); }

/* --------------------------------------------------------------------
   Cifras que se leen, y la casilla que se rellena

   Nacieron en la revision de un pedido, donde conviven cuatro numeros: tres
   son un HECHO —lo pedido, lo que hay, lo prometido— y el cuarto es lo unico
   que la persona tiene que decidir. Pintados los cuatro igual, el cuarto se
   leia como un dato mas y la pantalla parecia un informe en vez de un
   formulario: «Aprobado 2» se entendia como que ya alguien habia aprobado dos.

   `.cifra` resalta el rotulo de lo que solo se lee; `.casilla-accion` sombrea
   con el color de acento del tema lo que hay que rellenar. El color viene de
   la paleta de la empresa, asi que la casilla destaca igual sea cual sea.
   -------------------------------------------------------------------- */
.cifra__rotulo {
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--texto);
}
.cifra__valor { font-size: 1.125rem; font-weight: 600; }

.casilla-accion {
  background: var(--accent-suave);
  border: 1px solid var(--accent);
  border-radius: var(--radio);
  padding: 0.375rem 0.5rem 0.5rem;
}
/* Gana en especificidad a `.campo label`, que la tine de gris suave: aqui el
   rotulo es una instruccion —«Aprobar»— y tiene que leerse como tal. */
.casilla-accion.campo > label {
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--texto);
}
.casilla-accion input { width: 100%; }

.mensajes { padding: 0.75rem 1.25rem 0; display: flex; flex-direction: column; gap: 0.5rem; }
.mensaje {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.625rem 0.875rem;
  border-radius: var(--radio);
  border-left: 3px solid;
}
.mensaje button { background: none; border: 0; font-size: 1.25rem; cursor: pointer; color: inherit; }
.mensaje--success { background: #DCFCE7; border-color: var(--exito); }
.mensaje--warning { background: var(--accent-suave); border-color: var(--alerta); }
.mensaje--error   { background: #FEE2E2; border-color: var(--peligro); }
.mensaje--info    { background: var(--superficie); border-color: var(--primary-suave); }

/* --------------------------------------------------------------------
   Menu de usuario
   -------------------------------------------------------------------- */
.usuario { position: relative; flex-shrink: 0; }

.usuario__disparador {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.25rem 0.5rem 0.25rem 0.25rem;
  background: none;
  border: 1px solid transparent;
  border-radius: 999px;
  font: inherit;
  color: var(--texto);
  cursor: pointer;
  transition: background var(--transicion), border-color var(--transicion);
}
.usuario__disparador:hover { background: var(--fondo); border-color: var(--borde); }
.usuario__disparador:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.usuario__inicial {
  display: grid;
  place-items: center;
  width: 1.875rem;
  height: 1.875rem;
  border-radius: 50%;
  background: var(--accent);
  color: var(--contraste-accent);
  font-size: 0.8125rem;
  font-weight: 600;
}

.usuario__nombre { font-size: 0.875rem; font-weight: 500; }

.usuario__menu {
  position: absolute;
  top: calc(100% + 0.375rem);
  right: 0;
  z-index: 30;
  min-width: 14rem;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  box-shadow: 0 4px 12px rgb(0 0 0 / 0.1), 0 1px 3px rgb(0 0 0 / 0.06);
  overflow: hidden;
}

.usuario__cabecera {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  padding: 0.75rem;
  border-bottom: 1px solid var(--borde);
}
.usuario__cabecera strong { font-size: 0.875rem; }
.usuario__cabecera span { font-size: 0.75rem; color: var(--texto-suave); }

.usuario__salir {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  width: 100%;
  padding: 0.625rem 0.75rem;
  background: none;
  border: 0;
  font: inherit;
  font-size: 0.875rem;
  color: var(--peligro);
  text-align: left;
  cursor: pointer;
}
.usuario__salir:hover { background: var(--fondo); }
.usuario__salir i { width: 1rem; text-align: center; }

/* --------------------------------------------------------------------
   Integracion con HTMX
   -------------------------------------------------------------------- */
/* HTMX pone .htmx-request en el elemento que lanza la peticion; el
   indicador global solo aparece mientras hay una en vuelo. */
.indicador-htmx { opacity: 0; transition: opacity var(--transicion); color: var(--accent); }
body.htmx-request .indicador-htmx { opacity: 1; }

/* Atenua el fragmento que esta siendo sustituido, para que el usuario
   perciba que la zona esta desactualizada. */
.htmx-swapping { opacity: 0.5; transition: opacity 120ms; }
.htmx-added { opacity: 0; }
.htmx-settling { opacity: 1; transition: opacity 160ms; }

/* --------------------------------------------------------------------
   Responsive
   -------------------------------------------------------------------- */
@media (max-width: 1024px) {
  /* El cajon ya nace sin carril en cualquier tamano, asi que aqui no hay nada
     que ajustar: se abre con el boton de la cabecera y tapa el contenido
     mientras esta abierto, que es lo normal en un movil. */

  /* Solo el circulo con la inicial: el nombre se come el ancho del titulo. */
  .usuario__nombre { display: none; }

  /* La cabecera en dos filas: arriba el menu, el titulo, la tasa y el usuario;
     debajo, los botones de la pantalla, que se reparten en las filas que
     hagan falta.

     En una sola fila no cabian. Medido a 390 px: 27 de las 34 pantallas del
     menu se desbordaban —Cuentas por Cobrar hasta 890 px—, los botones partian
     su rotulo en dos lineas y el titulo en tres, por encima del borde de una
     cabecera de alto fijo. Y no se quedaba en la cabecera: un movil que recibe
     una pagina mas ancha que la pantalla agranda el viewport hasta abarcarla,
     y los paneles de la derecha, que son `fixed`, se median contra ese ancho:
     el de reportar un pago salia de 570 px y cortaba sus ayudas por la derecha.

     El corte en 1024 px y no en el del telefono: a 768 los cinco botones de
     Cuentas por Cobrar tampoco caben, y una tableta de pie vive ese mismo
     problema. */
  .cabecera {
    flex-wrap: wrap;
    height: auto;
    min-height: var(--cabecera-alto);
    padding-block: 0.5rem;
    row-gap: 0.5rem;
  }
  .cabecera__acciones { order: 1; flex-basis: 100%; flex-wrap: wrap; gap: 0.5rem; }
  /* Un titulo largo —«Facturas de proveedores», una hoja con su numero— baja
     a una segunda linea en vez de empujar la tasa fuera de la pantalla. */
  .cabecera__titulo { min-width: 0; line-height: 1.25; overflow-wrap: anywhere; }
}

@media print {
  .lateral, .cabecera, .banda-impersonacion, .mensajes { display: none !important; }
  .contenido { padding: 0; }
}

/* ---------------------------------------------------------------------------
   Panel lateral derecho
   Lo inyecta HTMX en #panel-lateral. Se declara como componente porque ya lo
   usan varios formularios y repetir los estilos en linea los dejaba divergir.
--------------------------------------------------------------------------- */
.panel-derecho {
  position: fixed;
  top: 0; right: 0; bottom: 0;
  width: min(32rem, 100%);
  background: var(--superficie);
  border-left: 1px solid var(--borde);
  padding: 1.25rem;
  overflow-y: auto;
  z-index: 50;
  box-shadow: -4px 0 20px rgb(0 0 0 / .10);
  animation: panel-entra .18s ease-out;
}

@keyframes panel-entra {
  from { transform: translateX(100%); }
  to   { transform: none; }
}

/* Quien pide menos movimiento no deberia recibir un panel deslizante. */
@media (prefers-reduced-motion: reduce) {
  .panel-derecho { animation: none; }
  .panel-derecho--completo { transition: none; }
}

/* --------------------------------------------------------------------
   HOJA COMPLETA

   El panel de la derecha sirve para una ficha corta, pero hay formularios
   —levantar un pedido, reportar un pago de doce facturas— donde treinta
   caracteres de ancho por columna obligan a trabajar mirando por una rendija.
   Con el boton que pone `js/hoja-completa.js`, el mismo panel se estira a toda
   la ventana y se vuelve atras con el mismo boton.

   No es un panel distinto: es UNA CLASE sobre el que ya hay. Un segundo tipo de
   contenedor obligaria a que cada formulario supiera en cual de los dos vive.

   El `!important` no es pereza: quince plantillas fijan su ancho en un `style=`
   en linea —46rem el pedido, 42rem la revision— y un atributo `style` gana a
   cualquier regla de hoja de estilos. Se crece hacia la IZQUIERDA porque el
   panel esta anclado a `right: 0`, asi que no hace falta tocar `left` y el
   ancho se puede animar.

   LA COLUMNA LA CREA EL PANEL, NO CADA HIJO
   -----------------------------------------
   La sabana entera es del panel, pero el texto no: una etiqueta y su casilla
   estiradas a 2.500 px no se leen, se rastrean. Asi que el contenido se queda
   en una medida comoda y centrada, y lo que gana sitio de verdad son las
   tablas, que desbordan por su cuenta.

   La primera version lo hacia centrando cada hijo:

       .panel-derecho--completo > * { max-width: 78rem; margin-inline: auto; }

   y era un empate de especificidad esperando a ocurrir. `.ficha` declara
   `margin: 0 0 1.25rem` y `.detalle__titulo` declara `margin: 1.25rem 0 .5rem`
   MAS ABAJO en este mismo fichero: misma especificidad (0,1,0), gana la ultima,
   y ese cero de en medio pone el margen izquierdo a cero. Resultado: los dos
   se corrian 291 px a la izquierda —medido— mientras la cabecera, los mensajes,
   el formulario y las tablas se centraban. `.ficha` sale en 11 plantillas y
   `.detalle__titulo` en 22, asi que el descuadre aparecia en casi todas las
   pantallas y se descubrio MIRANDOLAS, no leyendo el CSS.

   La cura es que la columna la haga el RELLENO del panel. Los hijos siguen
   siendo bloques normales —no cambian de modo de layout— y el margen que cada
   componente traiga de serie deja de importar, porque el hueco ya esta hecho
   por fuera. Tambien deja de importar el `style="margin:.75rem 0"` que varias
   plantillas llevan en linea, que habria vuelto a romperlo aunque aqui se
   subiera la especificidad: un atributo `style` gana siempre.

   Se probo antes con `display: grid` y funcionaba, pero convertir en items de
   rejilla el contenido de cincuenta y cuatro paneles distintos es una
   superficie de riesgo que no hace falta correr para colocar dos margenes.
   El relleno no puede romper nada por construccion.

   El `%` del calculo se resuelve contra el bloque contenedor, que para un
   `position: fixed` es la ventana; y el panel mide justo el 100% de esa
   ventana, asi que las dos medidas son la misma. El `max()` es el que protege
   la pantalla estrecha: por debajo de 72rem manda el relleno normal.
   -------------------------------------------------------------------- */
.panel-derecho--completo {
  width: 100% !important;
  border-left: 0;
  padding-left: max(1.25rem, calc((100% - var(--hoja-medida)) / 2));
  padding-right: max(1.25rem, calc((100% - var(--hoja-medida)) / 2));
}

/* TODA tabla dentro de un panel tiene el MISMO alto y su propio scroll.

   Antes lo llevaban seis plantillas en un `style=` en linea, con topes de 14,
   16, 18 y 20rem sin ningun criterio, y las nueve fichas de detalle no lo
   llevaban en absoluto: cada pantalla se comportaba distinta. Ahora es una
   regla y una medida.

   Los listados de PAGINA quedan fuera por construccion —el selector pide
   `.panel-derecho`—: ahi la que se desplaza es la pagina entera, y acotar la
   tabla partiria el scroll infinito en dos barras. */
.panel-derecho .tabla-envoltorio,
.panel-derecho .panel-acotado {
  max-height: var(--tabla-panel);
  overflow-y: auto;
}
.panel-derecho--completo .tabla-envoltorio,
.panel-derecho--completo .panel-acotado {
  max-height: var(--tabla-panel-completa);
}

.panel-derecho { transition: width var(--transicion); }

.panel-derecho__cabecera {
  display: flex;
  justify-content: space-between;
  align-items: center;
  /* Medio rem y no uno: con el titulo comiendose el hueco, esta separacion solo
     se ve ya entre los dos botones, y a un rem parecen de dos grupos distintos. */
  gap: .5rem;
  margin-bottom: 1rem;
  padding-bottom: .75rem;
  border-bottom: 1px solid var(--borde);
}

/* El titulo se come el hueco sobrante para que los botones queden JUNTOS a la
   derecha. Sin esto, el `space-between` de la cabecera manda el boton de hoja
   completa a un extremo y el de cerrar al otro, con el titulo perdido en medio. */
.panel-derecho__titulo { margin: 0; font-size: 1rem; flex: 1; }

/* Cada paso del asistente de alta. El numero va fuera del flujo del texto
   para que se lea como una secuencia y no como una lista de campos mas. */
.paso { margin-bottom: 1.25rem; }

.paso__titulo {
  display: flex;
  align-items: center;
  gap: .5rem;
  font-size: .8125rem;
  font-weight: 600;
  color: var(--texto-suave);
  margin-bottom: .5rem;
}

.paso__numero {
  display: inline-grid;
  place-items: center;
  width: 1.5rem; height: 1.5rem;
  border-radius: 50%;
  background: var(--accent);
  color: var(--contraste-accent);
  font-size: .75rem;
}

.paso__numero--hecho { background: var(--exito); color: #fff; }

/* Ficha de un cliente encontrado por el buscador. */
.hallazgo {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: .75rem;
  padding: .625rem .75rem;
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  margin-bottom: .5rem;
}

.hallazgo__rif {
  font-size: .8125rem;
  color: var(--texto-suave);
  font-variant-numeric: tabular-nums;
}

/* Un cliente que existe pero cae fuera del alcance del usuario: se muestra
   para que no lo duplique, sin ofrecerle ninguna accion sobre el. */
.hallazgo--ajeno { opacity: .7; border-style: dashed; }

/* ---------------------------------------------------------------------------
   Boton de envio con estado "en curso"
   HTMX pone la clase `htmx-request` en el elemento mientras la peticion esta en
   vuelo. Con eso basta para cambiar el rotulo sin JavaScript propio, y sin que
   el boton cambie de tamano y descoloque el formulario.
--------------------------------------------------------------------------- */
.boton-envio__curso { display: none; }
.boton-envio.htmx-request .boton-envio__reposo { display: none; }
.boton-envio.htmx-request .boton-envio__curso { display: inline; }

/* ---------------------------------------------------------------------------
   Ficha de detalle (panel derecho)
   Lista de definiciones en dos columnas: la etiqueta a la izquierda, el dato a
   la derecha. Se usa <dl> y no una tabla porque son pares dato-valor, no filas
   comparables entre si.
--------------------------------------------------------------------------- */
.ficha {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: .375rem .875rem;
  margin: 0 0 1.25rem;
  font-size: .875rem;
}

.ficha dt { color: var(--texto-suave); }

/* El valor va pegado a su etiqueta, no al borde derecho del panel.

   Con el panel estrecho la alineacion a la derecha se leia como una ficha de
   banco y estaba bien. Estirado a hoja completa dejaba «RIF» a mil ciento
   cuarenta pixeles de `V151075505` —medido— y emparejar cada rotulo con su dato
   obligaba a barrer la pantalla con la vista. Pegados, la pareja se lee de un
   golpe sea cual sea el ancho. */
.ficha dd { margin: 0; text-align: left; }

/* Cifras de ancho fijo para que los importes no bailen de una fila a otra. */
.ficha__dato { font-variant-numeric: tabular-nums; }

.detalle__titulo {
  font-size: .8125rem;
  font-weight: 600;
  color: var(--texto-suave);
  margin: 1.25rem 0 .5rem;
  padding-bottom: .375rem;
  border-bottom: 1px solid var(--borde);
}

.detalle__cuenta {
  display: inline-block;
  min-width: 1.25rem;
  padding: 0 .375rem;
  border-radius: 999px;
  background: var(--fondo);
  color: var(--texto);
  text-align: center;
}

/* Enlace que abre la ficha desde el listado. No lleva subrayado permanente
   para no ensuciar la tabla, pero si al pasar por encima: tiene que verse que
   es pulsable. Va en `--primary-texto` y no en `--primary`: con un principal
   claro escogido en la rueda, la razon social quedaba blanco sobre blanco. */
.enlace-ficha {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--primary-texto);
  cursor: pointer;
  text-align: left;
}

.enlace-ficha:hover { text-decoration: underline; }

/* Distintivo del filtro activo en Cuentas por Cobrar. */
.filtro-activo {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  padding: .3125rem .625rem;
  border-radius: 999px;
  background: var(--accent-suave);
  border: 1px solid var(--accent);
  font-size: .8125rem;
}

/* Cliente dado de baja. Rojo suave y no gris: no es un estado neutro, es una
   prohibicion —no se le factura ni se le montan pedidos— y tiene que leerse
   como tal de un vistazo en una tabla larga. */
.etiqueta--inactivo { background: #FEE2E2; color: var(--peligro); }

/* Aviso que no impide nada —el renglon sin descuento por volumen al revisar un
   pedido—. Amarillo para que el rojo quede para lo que SI falta, el backorder:
   con los dos en rojo, en un pedido de diez renglones no se distinguian. El
   texto no es `--alerta`: ese ambar sobre fondo claro se queda por debajo de
   3:1 y a 12 px no se lee; este marron del mismo tono pasa de 6:1. */
.etiqueta--aviso { background: #FEF3C7; color: #92400E; }

/* La fila entera se atenua para que la baja se note sin leer la etiqueta. */
.fila--inactiva td { opacity: .6; }
.fila--inactiva .etiqueta--inactivo { opacity: 1; }

/* Baja logica. Gris y no rojo: no es una prohibicion como `inactivo`, es un
   registro archivado que puede volver. */
.etiqueta--baja { background: var(--fondo); color: var(--texto-suave); border: 1px solid var(--borde); }

/* Bloque de sucursales dadas de baja dentro de la ficha. El borde discontinuo
   repite el lenguaje que ya usa `hallazgo--ajeno`: esta ahi, pero no cuenta. */
.hallazgo--baja { opacity: .75; border-style: dashed; }

/* --- Semaforo de la cartera ----------------------------------------------
   El color de un cliente o de una sucursal segun sus facturas y notas de
   entrega pendientes: rojo si alguna vencio, amarillo si alguna esta por
   vencer, verde si todas estan vigentes. Sin nada pendiente no lleva clase y
   conserva el aspecto de siempre. Lo decide `cobros.semaforo_de()`; aqui solo
   se pinta.

   Cada estado fija `--color-semaforo` y las reglas de abajo lo usan, igual que
   `--color-grupo` en el menu: los tres salen de los tokens del tema.

   En la tabla el tinte va en las CELDAS y la barra es una sombra del primer
   `<td>`, por lo mismo que en `.fila--resaltada`: con `border-collapse` la fila
   no tiene area propia donde pintarla. Y dos senales y no una, porque un tinte
   claro solo no se distingue en una pantalla con brillo. El `:hover` necesita
   su propia regla: el de `.tabla` pinta el `<tr>`, que queda debajo del tinte
   de las celdas. */
.semaforo--rojo     { --color-semaforo: var(--peligro); }
.semaforo--amarillo { --color-semaforo: var(--alerta); }
.semaforo--verde    { --color-semaforo: var(--exito); }

.tabla tbody tr.semaforo > td {
  background: color-mix(in srgb, var(--color-semaforo) 10%, var(--superficie));
}
.tabla tbody tr.semaforo:hover > td {
  background: color-mix(in srgb, var(--color-semaforo) 16%, var(--superficie));
}
.tabla tbody tr.semaforo > td:first-child {
  box-shadow: inset 4px 0 0 var(--color-semaforo);
}

.hallazgo.semaforo {
  border-left: 4px solid var(--color-semaforo);
  background: color-mix(in srgb, var(--color-semaforo) 8%, var(--superficie));
}

/* --- Pestanas de sede operativa -----------------------------------------
   El filtro que llevan los listados de modelos que pertenecen a una sede. Son
   ENLACES, no botones: la sede elegida queda en la URL para poder compartirla y
   para que el boton atras signifique algo.

   `overflow-x:auto` porque una empresa puede tener ocho sedes y en un movil no
   caben: se desplazan en horizontal en lugar de partir la fila. */
.pestanas {
  display: flex;
  gap: .375rem;
  margin-bottom: 1rem;
  overflow-x: auto;
  padding-bottom: .25rem;
}

.pestana {
  display: inline-flex;
  align-items: center;
  gap: .4375rem;
  padding: .4375rem .75rem;
  border-radius: 999px;
  border: 1px solid var(--borde);
  background: var(--superficie);
  color: var(--texto-suave);
  font-size: .8125rem;
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
  flex-shrink: 0;
}

.pestana:hover { background: var(--fondo); border-color: var(--primary-suave); }

.pestana--activa {
  background: var(--accent-suave);
  border-color: var(--accent-hover);
  color: var(--accent-hover);
  font-weight: 600;
}

/* --- Paginacion ---------------------------------------------------------- */
.paginacion {
  display: flex;
  align-items: center;
  gap: .75rem;
  margin-top: 1rem;
  flex-wrap: wrap;
}

.paginacion__cuenta { color: var(--texto-suave); font-size: .8125rem; }

/* --- Panel: cifras y menu por areas --------------------------------------
   Las cifras van compactas para que el menu de abajo quede a la vista sin
   desplazarse. Cada grupo del menu toma su color de un TOKEN de la paleta
   (`--color-grupo`), nunca de un literal: al cambiar de esquema, cambia. */
.panel-cifras {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
  gap: .75rem;
  margin-bottom: 1.75rem;
}

.cifra { padding: .625rem .875rem; text-decoration: none; color: inherit; display: block; }
a.cifra:hover { border-color: var(--primary-suave); }
.cifra__rotulo { color: var(--texto-suave); font-size: .75rem; }
.cifra__valor { font-size: 1.25rem; font-weight: 600; font-variant-numeric: tabular-nums; line-height: 1.3; }
.cifra__valor--peligro { color: var(--peligro); }
.cifra__nota { color: var(--texto-suave); font-size: .6875rem; }

.menu-areas__cabecera {
  display: flex; flex-wrap: wrap; justify-content: space-between;
  align-items: flex-end; gap: 1rem; margin-bottom: 1rem;
}
.menu-areas__titulo { display: flex; align-items: center; gap: .75rem; }
.menu-areas__titulo::before {
  content: ""; width: 3px; height: 1.5rem; border-radius: 2px;
  background: linear-gradient(180deg, var(--primary), var(--accent));
}
.menu-areas__antetitulo {
  font-size: .65625rem; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--texto-suave); margin: 0;
}
.menu-areas__nombre { font-size: 1.0625rem; font-weight: 700; margin: 0; }

.menu-areas__buscador { position: relative; display: flex; align-items: center; }
.menu-areas__buscador i {
  position: absolute; left: .7rem; font-size: .75rem;
  color: var(--texto-suave); pointer-events: none;
}
.menu-areas__buscador input {
  width: 15rem; padding: .45rem .875rem .45rem 2.25rem;
  border: 1px solid var(--borde); border-radius: var(--radio);
  background: var(--superficie); color: var(--texto); font: inherit; font-size: .8125rem;
}
.menu-areas__buscador input:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-suave);
}

.menu-areas {
  display: grid;
  /* `auto-fill` y no `auto-fit`: con el buscador quedan una o dos areas, y
     `auto-fit` las estiraba a todo el ancho de la pagina. */
  grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
  /* Todas las tarjetas del MISMO alto, tambien entre filas: sin esto cada fila
     media lo de su tarjeta mas alta, y al partirse la cuadricula en dos filas
     las de abajo salian de otro tamano. */
  grid-auto-rows: 1fr;
  gap: 1.125rem;
}

/* LA TARJETA DE AREA, la misma para todas: sin franja de color, del mismo alto
   que las demas (`grid-auto-rows` de arriba) y con su fila de titulo CENTRADA
   en ese alto —arriba dejaba un hueco vacio debajo—.

   Resplandor de neon, del COLOR DE ACENTO del tema —el segundo de Identidad
   visual—, asi que cambia solo con el tema de cada empresa. Sutil a proposito:
   un borde tenido y dos halos en capas muy transparentes, uno cerrado que
   dibuja el contorno y otro amplio que lo difumina. Estatico: un brillo que
   late distrae en una pantalla de trabajo. Al pasar el raton se aviva un poco,
   sumado a la sombra que la eleva.

   Sin `overflow: hidden` a proposito: lo que se despliega SOBRESALE por debajo y
   tapa la fila siguiente en vez de empujarla. Empujarla movia de sitio la
   tarjeta sobre la que iba a hacerse el siguiente clic. */
.menu-area {
  position: relative;
  display: flex; flex-direction: column; justify-content: center;
  background: var(--superficie);
  border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--borde));
  border-radius: calc(var(--radio) * 2);
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--accent) 10%, transparent),
    0 0 10px color-mix(in srgb, var(--accent) 16%, transparent),
    0 0 24px color-mix(in srgb, var(--accent) 9%, transparent);
  transition: box-shadow var(--transicion), transform var(--transicion);
}
.menu-area:hover {
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--accent) 16%, transparent),
    0 0 14px color-mix(in srgb, var(--accent) 24%, transparent),
    0 0 32px color-mix(in srgb, var(--accent) 14%, transparent),
    0 8px 32px rgb(0 0 0 / .09);
  transform: translateY(-2px); z-index: 40;
}
.menu-area--abierta, .menu-area--grafico { z-index: 50; }

.menu-area__nombre { display: block; font-size: .9375rem; font-weight: 700; }
.menu-area__resumen { display: block; font-size: .6875rem; color: var(--texto-suave); }
.menu-area__flecha {
  margin-left: auto; font-size: .8125rem; color: var(--texto-suave);
  transition: transform var(--transicion), color var(--transicion);
}
.menu-area:hover .menu-area__flecha { color: var(--color-grupo); }
.menu-area--abierta .menu-area__flecha { transform: rotate(180deg); color: var(--color-grupo); }

/* Lo que se despliega —esta lista y el panel del grafico— FLOTA y nunca hay
   dos abiertos. No cuelga del borde de abajo de la tarjeta sino de
   `menu-area__ancla`, justo bajo la fila: la tarjeta mide lo que la mas alta
   de la cuadricula, y colgado del borde se abria lejos de la fila que se
   acababa de pulsar. Por eso la tarjeta conserva sus esquinas al abrirse. */
.menu-area__ancla { position: relative; }
.menu-area__lista {
  position: absolute; top: 100%; left: -1px; width: calc(100% + 2px);
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: 0 0 calc(var(--radio) * 2) calc(var(--radio) * 2);
  box-shadow: 0 16px 32px rgb(0 0 0 / .12);
  padding: .25rem .75rem 1rem;
  opacity: 0; visibility: hidden; transform: translateY(-10px);
  transition: opacity var(--transicion), transform var(--transicion), visibility var(--transicion);
}
.menu-area--abierta .menu-area__lista { opacity: 1; visibility: visible; transform: translateY(0); }

.menu-area__enlace {
  display: flex; align-items: center; gap: .625rem;
  padding: .6875rem 1rem; border-radius: var(--radio);
  font-size: .84375rem; font-weight: 500; color: var(--texto); text-decoration: none;
  transition: background var(--transicion), color var(--transicion);
}
.menu-area__enlace i {
  width: 1.125rem; text-align: center; font-size: .8125rem; color: var(--texto-suave);
  transition: color var(--transicion);
}
.menu-area__enlace:hover {
  background: color-mix(in srgb, var(--color-grupo) 9%, transparent);
  color: var(--color-grupo);
}
.menu-area__enlace:hover i { color: var(--color-grupo); }

/* La cabecera en una rejilla de DOS filas, y cada icono en la fila de lo que
   representa: el del area junto al nombre, el del grafico junto a su cifra.
   Un area sin cifras lleva en la segunda fila el resumen de sus modulos.

       [icono del area]    Nombre del area            [v]
       [icono grafico]     [cifra] que significa
                           Resumen · de · modulos      <- area sin cifras

   Con una sola columna de iconos centrada contra el texto, el del area quedaba
   por encima del nombre y no a su altura.

   El boton que despliega ocupa la columna del texto en las dos filas y las
   HEREDA con `subgrid`: asi su nombre y su cifra se alinean con los iconos de
   fuera, que no pueden ir dentro —un boton no lleva otro boton—. El icono del
   area abre los accesos con su propio clic, como si estuviera dentro. */
.menu-area__barra {
  display: grid; grid-template-columns: 2.5rem minmax(0, 1fr);
  align-items: center; column-gap: .75rem; row-gap: .375rem;
  padding: 1rem 1.25rem;
}
.menu-area__icono-plano {
  grid-column: 1; grid-row: 1; justify-self: center;
  font-size: 1.125rem; color: var(--color-grupo); cursor: pointer;
}
.menu-area__boton-grafico {
  grid-column: 1; grid-row: 2; justify-self: center;
  width: 2rem; height: 2rem;
  display: flex; align-items: center; justify-content: center;
  border: 0; border-radius: var(--radio); background: none; cursor: pointer;
  color: var(--texto-suave); font-size: 1rem;
  transition: background var(--transicion), color var(--transicion);
}
.menu-area__boton-grafico:hover,
.menu-area--grafico .menu-area__boton-grafico {
  color: var(--color-grupo);
  background: color-mix(in srgb, var(--color-grupo) 10%, transparent);
}
.menu-area__alternar {
  grid-column: 2; grid-row: 1 / span 2;
  display: grid; grid-template-rows: subgrid; grid-template-columns: minmax(0, 1fr) auto;
  align-items: center; column-gap: .75rem;
  padding: 0; border: 0; background: none;
  font: inherit; color: inherit; text-align: left; cursor: pointer;
}
.menu-area__alternar .menu-area__nombre { grid-column: 1; grid-row: 1; }
.menu-area__alternar .menu-area__flecha { grid-column: 2; grid-row: 1 / span 2; }
.menu-area__boton-grafico:focus-visible,
.menu-area__alternar:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* La cifra y lo que significa, siempre juntas: un «-72,7 %» suelto no dice de
   que es. Del tamano del resumen, que ocupa ese mismo sitio en las areas sin
   cifras. */
.menu-area__cifra {
  grid-column: 1; grid-row: 2;
  display: flex; flex-wrap: wrap; align-items: center; gap: .25rem .375rem;
}
.menu-area__alternar .menu-area__resumen { grid-column: 1; grid-row: 2; }
.menu-area__cifra .menu-area__variacion { font-size: .6875rem; }
.menu-area__cifra-texto { font-size: .6875rem; color: var(--texto-suave); }

/* El fondo se mezcla con la SUPERFICIE y no con transparente: la insignia del
   grafico se posa sobre las lineas y el degradado, y translucida dejaba ver la
   linea cruzandole el numero. */
.menu-area__variacion {
  font-size: .75rem; font-weight: 700; line-height: 1.4;
  padding: .0625rem .5rem; border-radius: var(--radio); white-space: nowrap;
}
.menu-area__variacion--sube  { color: var(--exito);   background: color-mix(in srgb, var(--exito) 14%, var(--superficie)); }
.menu-area__variacion--baja  { color: var(--peligro); background: color-mix(in srgb, var(--peligro) 12%, var(--superficie)); }
.menu-area__variacion--igual { color: var(--texto-suave); background: var(--fondo); }

.menu-area__panel {
  position: absolute; top: 100%; left: -1px; width: calc(100% + 2px);
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: 0 0 calc(var(--radio) * 2) calc(var(--radio) * 2);
  box-shadow: 0 16px 32px rgb(0 0 0 / .12);
  padding: .875rem 1.125rem 1rem;
  opacity: 0; visibility: hidden; transform: translateY(-10px);
  transition: opacity var(--transicion), transform var(--transicion), visibility var(--transicion);
}
.menu-area--grafico .menu-area__panel { opacity: 1; visibility: visible; transform: translateY(0); }

.grafico__titulo { margin: 0; font-size: .875rem; font-weight: 600; }
/* El margen de arriba es el sitio de la insignia: cuando hoy es el maximo de
   las dos semanas, el punto toca el techo del lienzo y la insignia va encima. */
.semana__lienzo { position: relative; margin-top: 1.75rem; }
.semana__lienzo svg { display: block; width: 100%; height: auto; overflow: visible; }
.semana__degradado-inicio { stop-color: var(--color-grupo); stop-opacity: .22; }
.semana__degradado-fin    { stop-color: var(--color-grupo); stop-opacity: 0; }
.semana__area { fill: url(#ventas-degradado); }
/* `non-scaling-stroke`: el lienzo se escala al ancho de la tarjeta y sin esto
   el grosor de las lineas cambiaba con el de la pantalla. */
.semana__linea-anterior {
  fill: none; stroke: var(--texto-suave); stroke-width: 2; stroke-dasharray: 5 5;
  stroke-linecap: round; opacity: .55; vector-effect: non-scaling-stroke;
}
.semana__linea-actual {
  fill: none; stroke: var(--color-grupo); stroke-width: 3;
  stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke;
}
.semana__punto { fill: var(--color-grupo); stroke: var(--superficie); stroke-width: 2; }
/* Sobre el punto de hoy; el `clamp` la mete dentro cuando el punto cae en un
   borde, que es lo que pasa cada lunes. */
.semana__variacion {
  position: absolute; top: var(--y);
  left: clamp(2.25rem, var(--x), calc(100% - 2.25rem));
  transform: translate(-50%, calc(-100% - .5rem));
}

.semana__leyenda {
  display: flex; flex-wrap: wrap; gap: .25rem 1rem;
  margin: .625rem 0 0; font-size: .75rem; color: var(--texto-suave);
}
.semana__leyenda span { display: inline-flex; align-items: center; gap: .375rem; }
.semana__muestra {
  display: inline-block; width: .5rem; height: .5rem; border-radius: 50%;
  background: var(--color-grupo);
}
.semana__muestra--anterior { background: var(--texto-suave); opacity: .6; }

/* La cartera de Cobranza. Las cifras van en tinta de TEXTO y el color solo en
   las marcas: la cifra de lo vencido en rojo se leeria como un error. Lo que
   importa —lo vencido— es lo unico con color; lo por vencer va en gris para no
   competir con ello. */
.cartera__total { margin: .25rem 0 0; font-size: 1.625rem; font-weight: 700; line-height: 1.2; }
.cartera__nota { margin: 0; font-size: .75rem; color: var(--texto-suave); }
/* Dos tramos separados por 2px de superficie, cada uno con sus extremos
   redondeados. `min-width` para que un vencido de un 0,4 % se siga viendo. */
.cartera__barra { display: flex; gap: 2px; height: .75rem; margin-top: .875rem; }
.cartera__tramo {
  min-width: 4px; border-radius: 4px;
  background: color-mix(in srgb, var(--texto-suave) 40%, var(--superficie));
}
.cartera__tramo--vencido { background: var(--peligro); }
.cartera__leyenda {
  display: grid; grid-template-columns: auto 1fr auto auto; align-items: center;
  gap: .375rem .5rem; margin-top: .75rem; font-size: .8125rem; color: var(--texto-suave);
}
.cartera__leyenda strong { color: var(--texto); font-weight: 600; text-align: right; }
.cartera__porcentaje { min-width: 3.25rem; text-align: right; }
.cartera__muestra {
  display: inline-block; width: .625rem; height: .625rem; border-radius: 2px;
  background: color-mix(in srgb, var(--texto-suave) 40%, var(--superficie));
}
.cartera__muestra--vencido { background: var(--peligro); }

/* El pie del panel: la cifra de HOY de cada area. En Ventas es un enlace al
   listado del dia; en Cobranza, una cifra. El texto no toma el color del
   grupo: en un tema donde ese color es rojo, «38 ventas hoy» se leia como una
   alarma. Solo al pasar por encima, y solo si es enlace. */
.grafico__pie {
  display: flex; align-items: center; justify-content: space-between; gap: .75rem;
  margin-top: .875rem; padding-top: .875rem; border-top: 1px solid var(--borde);
  color: var(--texto); text-decoration: none;
}
.grafico__pie:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.grafico__pie-rotulo,
.grafico__pie-nota { display: block; font-size: .75rem; color: var(--texto-suave); }
.grafico__pie-cifra { display: block; font-size: 1.3125rem; font-weight: 700; line-height: 1.3; }
.grafico__pie i { font-size: 1.25rem; color: var(--texto-suave); transition: color var(--transicion); }
a.grafico__pie:hover .grafico__pie-cifra,
a.grafico__pie:hover i { color: var(--color-grupo); }

@media (prefers-reduced-motion: reduce) {
  .menu-area, .menu-area__lista, .menu-area__panel, .menu-area__flecha,
  .menu-area__boton-grafico { transition: none; }
  .menu-area:hover { transform: none; }
}

/* --------------------------------------------------------------------
   Panel de ventas (templates/erp/panel_ventas.html)

   Los graficos son SVG y CSS, sin libreria. Cada parte de un reparto toma su
   color de la clase `tablero--*`, que fija `--tono` desde un TOKEN de la
   paleta: el anillo lo usa de trazo, la barra de fondo y la leyenda de
   muestra, asi que las tres dicen el mismo color sin repetirlo. Lo que no
   importa —lo dado de baja, lo que falta— va en gris para no competir.
   -------------------------------------------------------------------- */
.tablero--primario   { --tono: var(--accent); }
.tablero--secundario { --tono: var(--primary-texto); }
.tablero--exito      { --tono: var(--exito); }
.tablero--alerta     { --tono: var(--alerta); }
.tablero--peligro    { --tono: var(--peligro); }
.tablero--neutro     { --tono: color-mix(in srgb, var(--texto-suave) 40%, var(--superficie)); }

.tablero__filtro { margin-bottom: 1rem; }
.tablero__formulario { display: flex; flex-wrap: wrap; gap: .75rem; align-items: flex-end; }
.tablero__formulario .campo { margin-bottom: 0; flex: 0 1 11rem; }
.tablero__error { color: var(--peligro); font-size: .75rem; }
.tablero__mensaje { margin-top: .75rem; }
.tablero__atajos { display: flex; flex-wrap: wrap; gap: .375rem; margin-top: .75rem; }
.tablero__atajo {
  padding: .25rem .75rem; border: 1px solid var(--borde); border-radius: 999px;
  font-size: .8125rem; color: var(--texto); text-decoration: none;
  transition: border-color var(--transicion), background var(--transicion);
}
.tablero__atajo:hover { border-color: var(--accent); }
.tablero__atajo--activo { background: var(--accent); border-color: var(--accent); color: var(--contraste-accent); }

.tablero__periodo { margin: 0 0 1.25rem; color: var(--texto-suave); }
.tablero__periodo strong { color: var(--texto); }

.tablero__seccion { margin-bottom: 2.25rem; }
.tablero__encabezado {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
  gap: .25rem 1rem;
}
.tablero__encabezado h2 { margin: 0; font-size: 1.125rem; font-weight: 700; }
.tablero__encabezado h2 i { margin-right: .25rem; color: var(--accent); }
.tablero__enlace { font-size: .8125rem; color: var(--accent); text-decoration: none; }
.tablero__enlace:hover { text-decoration: underline; }
.tablero__criterio { max-width: 60rem; margin: .25rem 0 .875rem; font-size: .8125rem; color: var(--texto-suave); }
.tablero__seccion .panel-cifras { margin-bottom: .75rem; }
.cifra__valor small { font-size: .75rem; font-weight: 500; color: var(--texto-suave); }
.tablero__aviso { color: var(--alerta); }

/* `min(100%, …)` para que en un telefono la columna no exija mas ancho que la
   pantalla: sin el, 19rem en una ventana de 320px desbordaba en horizontal. */
.tablero__rejilla {
  display: grid; gap: .75rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 19rem), 1fr));
}
.tablero__grafico { min-width: 0; }
.tablero__grafico--ancho { grid-column: 1 / -1; }
.tablero__unidad { font-size: .75rem; font-weight: 400; color: var(--texto-suave); }
.tablero__subtitulo { margin: .875rem 0 0; font-size: .75rem; color: var(--texto-suave); }
.tablero__vacio { margin: 1rem 0 0; font-size: .8125rem; color: var(--texto-suave); }
.tablero__desplazable { max-height: var(--tabla-panel-completa); overflow-y: auto; padding-right: .25rem; }

/* La serie. El margen de arriba es el sitio del rotulo del maximo. */
.serie { position: relative; margin-top: 1.5rem; }
.serie__tope { position: absolute; top: -1.25rem; left: 0; font-size: .6875rem; color: var(--texto-suave); }
.serie__lienzo { display: block; width: 100%; height: auto; overflow: visible; }
.serie__degradado-inicio { stop-color: var(--accent); stop-opacity: .25; }
.serie__degradado-fin    { stop-color: var(--accent); stop-opacity: 0; }
.serie__area { fill: url(#serie-degradado); }
/* `non-scaling-stroke`, como en el grafico de la semana: el lienzo se escala al
   ancho de la tarjeta y sin esto el grosor cambiaba con el de la pantalla. */
.serie__linea {
  fill: none; stroke: var(--accent); stroke-width: 2.5;
  stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke;
}
.serie__guia { stroke: var(--borde); stroke-width: 1; stroke-dasharray: 3 4; vector-effect: non-scaling-stroke; }
.serie__guia--base { stroke-dasharray: none; }
.serie__punto { fill: var(--accent); stroke: var(--superficie); stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.serie__punto:hover { r: 6; }
.serie__punto--oculto { fill: transparent; stroke: none; }
.serie__punto--oculto:hover { fill: var(--accent); r: 4; }
.serie__eje { position: relative; height: 1.25rem; margin-top: .25rem; font-size: .6875rem; color: var(--texto-suave); }
.serie__eje span { position: absolute; transform: translateX(-50%); white-space: nowrap; }

/* El anillo: circunferencia 100, ver `_reparto()` en erp/views.py. */
.dona { position: relative; width: min(11rem, 100%); margin: .75rem auto 0; }
.dona__anillo { display: block; width: 100%; height: auto; }
.dona__pista { fill: none; stroke: color-mix(in srgb, var(--texto-suave) 15%, var(--superficie)); stroke-width: 5; }
.dona__parte { fill: none; stroke: var(--tono); stroke-width: 5; transition: stroke-width var(--transicion); }
.dona__parte:hover { stroke-width: 6.5; }
.dona__centro {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; text-align: center; pointer-events: none;
}
.dona__centro strong { font-size: 1rem; font-weight: 700; line-height: 1.2; }
.dona__centro span { font-size: .6875rem; color: var(--texto-suave); }

.leyenda {
  display: grid; grid-template-columns: auto 1fr auto auto; align-items: center;
  gap: .375rem .5rem; margin: .875rem 0 0; padding: 0; list-style: none;
  font-size: .8125rem; color: var(--texto-suave);
}
.leyenda__fila { display: contents; }
.leyenda strong { color: var(--texto); font-weight: 600; text-align: right; }
.leyenda__porcentaje { min-width: 3.25rem; text-align: right; }
.leyenda__muestra { display: inline-block; width: .625rem; height: .625rem; border-radius: 2px; background: var(--tono); }

/* La barra apilada, con la misma forma que la de la cartera del panel. */
.reparto {
  display: flex; gap: 2px; height: .75rem; margin-top: .5rem; border-radius: 4px;
  background: color-mix(in srgb, var(--texto-suave) 12%, var(--superficie));
}
.reparto__tramo { min-width: 4px; border-radius: 4px; background: var(--tono); }

.barras { display: flex; flex-direction: column; gap: .625rem; margin: .75rem 0 0; padding: 0; list-style: none; }
.barras__fila { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: .125rem .75rem; font-size: .8125rem; }
.barras__rotulo { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.barras__cifra { font-weight: 600; text-align: right; }
.barras__pista {
  grid-column: 1 / -1; height: .5rem; overflow: hidden; border-radius: 999px;
  background: color-mix(in srgb, var(--texto-suave) 12%, var(--superficie));
}
.barras__relleno { display: block; height: 100%; min-width: 2px; border-radius: inherit; background: var(--accent); }
.barras__detalle { grid-column: 1 / -1; font-size: .6875rem; color: var(--texto-suave); }

@media (prefers-reduced-motion: reduce) {
  .tablero__atajo, .dona__parte { transition: none; }
}

/* --------------------------------------------------------------------
   Identidad visual: tarjetas de tipografia y ruedas de color
   (templates/theming/ajustes.html, static/js/identidad-visual.js)
   -------------------------------------------------------------------- */
.identidad__opcion { position: absolute; opacity: 0; }
.identidad__tarjeta {
  border: 1px solid var(--borde); border-radius: var(--radio);
  padding: .625rem; background: var(--superficie);
}
.identidad__opcion:checked + .identidad__tarjeta {
  border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent);
}
.identidad__opcion:focus-visible + .identidad__tarjeta { outline: 2px solid var(--accent); outline-offset: 2px; }

.identidad__rueda-bloque { display: flex; flex-direction: column; gap: .5rem; }
.identidad__rueda {
  position: relative; width: 12rem; aspect-ratio: 1; border-radius: 50%;
  cursor: crosshair;
  /* Sin esto, en una pantalla tactil arrastrar sobre la rueda desplaza la
     pagina en vez de mover el marcador. */
  touch-action: none;
  box-shadow: var(--sombra);
}
.identidad__marcador {
  position: absolute; width: 1.125rem; height: 1.125rem; border-radius: 50%;
  transform: translate(-50%, -50%);
  border: 3px solid var(--superficie); box-shadow: 0 0 0 1px var(--texto);
  pointer-events: none;
}
.identidad__luz-rotulo {
  display: flex; flex-direction: column; gap: .25rem; width: 12rem;
  font-size: .8125rem; font-weight: 500; color: var(--texto-suave);
}
.identidad__luz {
  appearance: none; width: 100%; height: .625rem; border-radius: 999px;
  border: 1px solid var(--borde);
}
.identidad__luz::-webkit-slider-thumb {
  appearance: none; width: 1rem; height: 1rem; border-radius: 50%;
  background: var(--superficie); border: 2px solid var(--texto); cursor: pointer;
}
.identidad__luz::-moz-range-thumb {
  width: 1rem; height: 1rem; border-radius: 50%;
  background: var(--superficie); border: 2px solid var(--texto); cursor: pointer;
}
.identidad__muestra {
  width: 2rem; height: 2rem; border-radius: var(--radio); border: 1px solid var(--borde);
}
.identidad__hex {
  width: 7rem; padding: .375rem .5rem;
  border: 1px solid var(--borde); border-radius: var(--radio);
  background: var(--superficie); color: var(--texto);
  font-family: ui-monospace, Consolas, monospace; text-transform: uppercase;
}
.identidad__hex:focus { outline: 2px solid var(--accent); outline-offset: -1px; border-color: var(--accent); }

/* Los colores de los botones: tres botones de muestra, que la vista previa
   repinta en vivo, y las muestras rapidas, que son atajos de la rueda. La
   escogida se marca con `aria-pressed`, que es lo que lee un lector de
   pantalla: la misma senal para los dos. */
.identidad__ejemplo {
  display: flex; flex-wrap: wrap; gap: .5rem; padding: .75rem;
  border: 1px dashed var(--borde); border-radius: var(--radio); background: var(--fondo);
}
.identidad__muestras { display: flex; flex-wrap: wrap; align-items: center; gap: .375rem; }
.identidad__color {
  width: 1.75rem; height: 1.75rem; padding: 0; border-radius: 50%;
  border: 2px solid var(--superficie); box-shadow: 0 0 0 1px var(--borde);
  cursor: pointer;
}
.identidad__auto {
  height: 1.75rem; padding: 0 .625rem; border-radius: 999px;
  border: 1px solid var(--borde); background: var(--superficie); color: var(--texto);
  font: inherit; font-size: .75rem; font-weight: 600; cursor: pointer;
}
.identidad__color[aria-pressed="true"],
.identidad__auto[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--texto); }
.identidad__color:focus-visible,
.identidad__auto:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.identidad__otro summary {
  cursor: pointer; font-size: .8125rem; font-weight: 600; color: var(--primary-texto);
}
.identidad__otro[open] summary { margin-bottom: .5rem; }

/* --------------------------------------------------------------------
   AYUDA

   El boton flotante, el indice de guias de la pantalla (`templates/ayuda/`)
   y el globo con el aro que pone `js/ayuda.js` sobre el elemento de cada paso.

   El boton va abajo a la izquierda y por ENCIMA del panel lateral (50): el
   panel se abre a toda la altura por la derecha, y en la cabecera lo tapaba
   justo cuando hace falta. Con el menu abierto se esconde, porque caeria
   encima de sus ultimas entradas; el menu es hermano de `.principal` y su
   clase basta, sin que la ayuda sepa nada de Alpine.

   El aro no intercepta el raton (`pointer-events: none`): la guia se sigue
   HACIENDO, y el clic tiene que llegar al boton resaltado.
   -------------------------------------------------------------------- */
.ayuda {
  position: fixed; left: 1rem; bottom: 1rem; z-index: 55;
}
.lateral:not(.lateral--colapsada) ~ .principal .ayuda { visibility: hidden; }

.ayuda__abrir {
  --tono-boton: var(--boton, var(--primary-texto));
  display: inline-flex; align-items: center; gap: .375rem;
  padding: .5rem .875rem; border: 0; border-radius: 999px;
  background: var(--tono-boton); color: var(--contraste-boton, #fff);
  font: inherit; font-size: .8125rem; font-weight: 600; cursor: pointer;
  box-shadow: 0 4px 14px rgb(0 0 0 / .18);
}
.ayuda__abrir:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.ayuda__indice {
  position: absolute; left: 0; bottom: calc(100% + .5rem);
  width: min(24rem, calc(100vw - 2rem)); max-height: min(34rem, 75vh); overflow-y: auto;
  background: var(--superficie); color: var(--texto);
  border: 1px solid var(--borde); border-radius: calc(var(--radio) * 2);
  box-shadow: 0 10px 30px rgb(0 0 0 / .18);
}
.ayuda__cabecera {
  position: sticky; top: 0;
  display: flex; align-items: center; justify-content: space-between;
  padding: .625rem .875rem; border-bottom: 1px solid var(--borde);
  background: var(--superficie); font-size: .875rem; font-weight: 600;
}
.ayuda__cerrar,
.ayuda-globo__cerrar {
  border: 0; background: none; color: var(--texto-suave); cursor: pointer;
  font-size: 1.25rem; line-height: 1; padding: 0 .25rem;
}
.ayuda__guia { padding: .875rem; border-bottom: 1px solid var(--borde); }
.ayuda__guia:last-child { border-bottom: 0; }
.ayuda__guia-titulo { margin: 0 0 .25rem; font-size: .9375rem; font-weight: 600; }
.ayuda__guia-resumen { margin: 0 0 .625rem; font-size: .8125rem; color: var(--texto-suave); }
.ayuda__detalle { margin-top: .625rem; }
.ayuda__detalle summary {
  cursor: pointer; font-size: .8125rem; font-weight: 600; color: var(--primary-texto);
}
.ayuda__pasos {
  margin: .5rem 0 0; padding-left: 1.25rem;
  font-size: .8125rem; line-height: 1.45; color: var(--texto-suave);
}
.ayuda__pasos li { margin-bottom: .375rem; }
.ayuda__pasos strong { color: var(--texto); font-weight: 600; }
.ayuda__pasos strong::after { content: "."; }

.ayuda-capa { position: fixed; inset: 0; z-index: 60; pointer-events: none; }
.ayuda-aro {
  position: fixed; border-radius: var(--radio);
  box-shadow: 0 0 0 3px var(--accent), 0 0 0 7px color-mix(in srgb, var(--accent) 28%, transparent);
  transition: top var(--transicion), left var(--transicion), width var(--transicion), height var(--transicion);
  animation: ayuda-latido 1.6s ease-in-out infinite;
}
.ayuda-aro[hidden] { display: none; }
@keyframes ayuda-latido {
  50% { box-shadow: 0 0 0 3px var(--accent), 0 0 0 11px color-mix(in srgb, var(--accent) 12%, transparent); }
}

.ayuda-globo {
  position: fixed; top: 1rem; left: 1rem; pointer-events: auto;
  width: min(20rem, calc(100vw - 2rem));
  padding: .75rem .875rem .875rem;
  background: var(--superficie); color: var(--texto);
  border: 1px solid var(--borde); border-radius: calc(var(--radio) * 2);
  box-shadow: 0 10px 30px rgb(0 0 0 / .2);
  transition: top var(--transicion), left var(--transicion);
}
/* En un telefono no hay sitio AL LADO de nada: se acopla abajo, a lo ancho. */
.ayuda-globo--abajo { top: auto; left: 1rem; right: 1rem; bottom: 1rem; width: auto; }

.ayuda-globo__cabecera { display: flex; align-items: center; justify-content: space-between; }
.ayuda-globo__cuenta { font-size: .75rem; color: var(--texto-suave); }
.ayuda-globo__progreso {
  height: 3px; margin: .375rem 0 .625rem; border-radius: 999px;
  background: var(--borde); overflow: hidden;
}
.ayuda-globo__progreso span {
  display: block; height: 100%; background: var(--accent); transition: width var(--transicion);
}
.ayuda-globo__titulo { margin: 0 0 .25rem; font-size: .9375rem; font-weight: 600; }
.ayuda-globo__texto { font-size: .8125rem; line-height: 1.5; }
.ayuda-globo__espera { margin: .5rem 0 0; font-size: .75rem; color: var(--texto-suave); font-style: italic; }
.ayuda-globo__espera[hidden] { display: none; }
.ayuda-globo__pie { display: flex; gap: .5rem; margin-top: .75rem; }
.ayuda-globo__pie .btn-acento { margin-left: auto; }
/* `.btn` declara su `display` y le ganaria al `hidden` del navegador. */
.ayuda-globo__pie[hidden],
.ayuda-globo__pie .btn[hidden] { display: none; }

@media (prefers-reduced-motion: reduce) {
  .ayuda-aro { animation: none; transition: none; }
  .ayuda-globo, .ayuda-globo__progreso span { transition: none; }
}
