/*
 * Lenguaje visual de SigoOne.
 *
 * Portado del prototipo «Grupo Occidente - V2» (Figma Make, 2026-09-03; el
 * análisis está en docs/diagnostico-prototipo-v2.md del proyecto gdo-app), que
 * estaba escrito en Tailwind v4. Acá va en CSS a mano y a propósito: el
 * proyecto no tiene Node ni paso de compilación, y meter uno para las clases de
 * utilidad obligaría a compilar los estilos antes de cada despliegue. Los
 * valores son los mismos del prototipo; lo que cambia es cómo se escriben.
 *
 * Los componentes llevan el nombre de lo que son, no de cómo se ven: .card,
 * .stat, .badge. Un `.blue-box` deja de tener sentido el día que se decide que
 * es verde.
 */

/* `@import` va primero, antes que cualquier otra regla —es una exigencia de
 * CSS—, y funde antes que esta hoja: con las dos clases juntas
 * (`.field.cobro-apl__monto`, por ejemplo, más abajo) `sigoone.css` pesa más
 * y gana siempre. Es donde va cada parcial de pantalla nuevo, para que dos
 * tareas de la misma ronda no editen este archivo a la vez. */
@import url("parciales/nav.3ebbf8680102.css");
@import url("parciales/prod2.4a384d68b285.css");
@import url("parciales/movements.91aa76c8e1da.css");
@import url("parciales/shrinkage.88579784d17d.css");
@import url("parciales/cycle-counts.95ccf0a7cc19.css");
@import url("parciales/mobile-warehouse.b15db7fddb22.css");
@import url("parciales/reception.b005e00e32dd.css");
@import url("parciales/dispatch-routes.9f68db52f55c.css");

/* ── Letras ───────────────────────────────────────────────────────────────────
 * DM Sans y DM Mono, las del prototipo V4 (22-09-2026). La V2 no declaraba
 * tipografía y la aplicación iba con la del sistema; la V3 y la V4 sí la
 * declaran, y es lo que más se nota al ponerlas lado a lado.
 *
 * **Viajan con el proyecto**, en `static/fonts/`, y no se piden a Google: la
 * aplicación tiene que verse igual en una red sin salida a internet, que era
 * la razón para no tener ninguna. Cada una en dos juegos: `latin`, que trae el
 * español, y `latin-ext`, que trae el ₡. El navegador baja el segundo solo si
 * la página lo usa, que en esta aplicación es siempre.
 *
 * `font-display: swap`: mientras llega el archivo se lee con la del sistema,
 * en vez de dejar los textos en blanco. */
@font-face {
  font-family: "DM Sans"; font-style: normal; font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/dm-sans-latin.aef1c6b4d4a3.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "DM Sans"; font-style: normal; font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/dm-sans-latin-ext.63513ba49cca.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
                 U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "DM Mono"; font-style: normal; font-weight: 400;
  font-display: swap;
  src: url("../fonts/dm-mono-400-latin.b4ddae058212.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "DM Mono"; font-style: normal; font-weight: 400;
  font-display: swap;
  src: url("../fonts/dm-mono-400-latin-ext.1b65496d99bd.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
                 U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "DM Mono"; font-style: normal; font-weight: 500;
  font-display: swap;
  src: url("../fonts/dm-mono-500-latin.ecb3fd43cbea.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "DM Mono"; font-style: normal; font-weight: 500;
  font-display: swap;
  src: url("../fonts/dm-mono-500-latin-ext.83187a548722.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
                 U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ── Tokens ───────────────────────────────────────────────────────────────────
 * Los grises son los de Tailwind, que es lo que el prototipo usa en cada
 * pantalla; los tres colores «de marca» —las dos barras y el lienzo— son los
 * únicos que el prototipo escribe a mano.
 */
:root {
  /* Detrás de cada una, la del sistema: es la que se ve mientras llega el
   * archivo y la que queda si no llega. */
  --font-sans: "DM Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI",
               Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-display: var(--font-sans);
  --font-mono: "DM Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --shell: #0f1929;          /* la barra superior */
  --shell-2: #162540;        /* la barra de pestañas del módulo */
  --subbar-uso: #25437a;     /* su tramo de lo que se usa a diario */
  --shell-text: #bedbff;     /* texto sobre las barras */
  /* **El tema de la V4** (Carlos, 22-09-2026: «pasa todo el tema»). La V3 y
   * la V4 cambiaron el tema de la aplicación entera —fondo, texto corriente,
   * radios y letra— y no los colores de los componentes: los botones, los
   * bordes y los grises siguen siendo los de Tailwind que ya estaban acá
   * (allá hay 241 botones en `blue-600` y 9 en el azul del tema). Por eso
   * cambian estos pocos tokens y no la paleta. Medidos en el navegador contra
   * el prototipo corriendo, como los de abajo. */
  --canvas: #f6f8fc;
  /* El texto corriente, el que no lleva color propio: `--foreground` del
   * tema de la V4. Los títulos y las cifras siguen en `--foreground`, que
   * allá son `text-gray-900` escrito a mano. */
  --ink: #15233c;
  --foreground: #101828;
  --card: #ffffff;
  --muted: #f3f4f6;
  --muted-foreground: #6a7282;
  /* El rótulo de un campo: un paso más oscuro que el texto apagado, como
   * en el prototipo. Con el gris de «apagado» el formulario se leía como
   * si estuviera deshabilitado entero. */
  --foreground-soft: #4a5565;
  --faint: #99a1af;
  --border: #e5e7eb;
  --border-soft: #f3f4f6;
  --border-input: #e5e7eb;
  /* **La paleta del prototipo, medida contra él** (Carlos, 10-09-2026).
   *
   * Allá es la de Tailwind 4, declarada en `oklch`; SigoOne traía la de la 3,
   * escrita en hexadecimal. Los grises salen casi iguales —uno o dos puntos—
   * pero los colores con saturación no: el azul del botón era `#3b82f6` y
   * allá es `#155dfc`, y lo mismo el verde, el ámbar y el rojo. Lado a lado
   * se nota, y es lo que Carlos vio: «el color de los botones no son iguales,
   * es otro tono de azul».
   *
   * Los valores son la conversión a sRGB de los `oklch` del prototipo, hecha
   * en el navegador: no son un azul parecido, es el suyo.
   *
   * El azul son **dos**, no uno: el botón y la ficha de filtro van en el
   * fuerte; la píldora de la subbarra, un paso más clara. */
  --primary: #155dfc;
  --primary-foreground: #ffffff;
  --primary-soft: #eff6ff;
  --primary-text: #1447e6;
  --primary-bright: #2b7fff;
  --ring: rgba(21, 93, 252, .25);
  --success: #00a63e;
  --success-soft: #dbfce7;
  --success-text: #008236;
  --warning: #d08700;
  --warning-soft: #fef9c2;
  --warning-text: #a65f00;
  --danger: #e7000b;
  --danger-soft: #ffe2e2;
  --danger-text: #c10007;

  --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, .05);
  --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -4px rgba(0, 0, 0, .1);
  --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, .1), 0 8px 10px -6px rgba(0, 0, 0, .1);

  /* **Los radios crecieron con el tema.** La V4 estira la escala de Tailwind
   * desde un radio base de 14 px (era de 10): `rounded-md` pasó de 8 a 12 y
   * `rounded-lg` de 10 a 14. Son los dos que usan los botones, el menú de
   * arriba y los cuadros de icono (`--radius-sm`), y los campos
   * (`--radius-md`). La tarjeta no cambia: `rounded-2xl` es de Tailwind y
   * no sale del radio base. */
  --radius-sm: 12px;
  /* El radio del control, que en el prototipo es `rounded-lg`. */
  --radius-md: 14px;
  /* **16 px, el de la tarjeta del prototipo** (`rounded-2xl`). Estaba en 12 y
   * se veía al ponerlas lado a lado; lo pidió igualado Carlos el 10-09-2026.
   * Lo leen las dieciséis reglas de contenedores con caja —tarjetas, paneles,
   * fichas, avisos—, que allá son todos `rounded-2xl`. */
  --radius: 16px;
  --radius-lg: 16px;
  --radius-pill: 999px;
  /* 60 px más el filo de abajo: los 61 que mide la del prototipo. */
  --topbar: 60px;
  --content: 1800px;
}

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

html, body { height: 100%; }

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink);
  background: var(--canvas);
  -webkit-font-smoothing: antialiased;
}

/* **Ningún enlace se subraya al pasar por encima** (Carlos, 06-09-2026). El
 * subrayado del navegador servía cuando un enlace era texto azul dentro de un
 * párrafo; acá casi todo lo que se puede apretar es una fila, una tarjeta o
 * una pestaña, y subrayarlas entera se lee como un error de dibujo. Lo que
 * dice que algo responde es el color y el fondo, no la raya.
 *
 * Se escribe en la base y no tarjeta por tarjeta porque las treinta y pico de
 * reglas que ya lo apagaban una por una eran la prueba de que la que estaba
 * mal era esta. */
a { color: var(--primary-text); text-decoration: none; }
a:hover { text-decoration: none; }

h1, h2, h3 { font-family: var(--font-display); font-weight: 700; margin: 0; letter-spacing: -.01em; }

::selection { background: var(--primary); color: #fff; }

.icon { width: 1em; height: 1em; stroke: currentColor; fill: none;
        stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
        flex: 0 0 auto; }

.num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
/*
Lo mismo que `.num` pero sin correr la columna a la derecha. Una hora y una
fecha se escriben con cifras de ancho fijo para que se lean en columna, y no son
cantidades: no se suman ni se comparan por magnitud, así que no van al margen
derecho. Es la misma distinción que hace la prueba de formato con los años y los
números de página.
*/
.digits { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

/* ── Armazón ───────────────────────────────────────────────────────────────
 * Dos barras arriba y el lienzo debajo, como el prototipo: la superior lleva
 * la marca, la empresa, las cuatro categorías y las acciones; la segunda, las
 * pantallas del módulo abierto. No hay menú lateral.
 */
.app { min-height: 100vh; display: flex; flex-direction: column; }

/* Las dos barras se quedan quietas al desplazar la pantalla, como en el
 * prototipo (Carlos, 27-09-2026): quien haga scroll siempre las tiene a la
 * vista, sin importar en qué pantalla del módulo esté.
 *
 * La marca queda en este envoltorio y no en `.topbar` (que la tenía antes,
 * sola) porque la altura de la barra superior no es fija: por debajo de
 * 1359 px las categorías bajan a un segundo renglón dentro de ella (ver el
 * `@media` de abajo) y en el teléfono `.subbar` directamente desaparece. Con
 * las dos barras adentro de un mismo envoltorio pegado, la segunda siempre
 * queda justo debajo de la primera sin necesitar su alto exacto en píxeles;
 * fijar `.subbar` con un `top` calculado aparte se habría desalineado en
 * cualquiera de esos anchos. */
.header-stack { position: sticky; top: 0; z-index: 40; }

/* `position: relative` y no `static`: en la ventana angosta de abajo
 * (`@media (max-width: 1359px)`) `.company` y `.mega` se apagan y el panel
 * que cuelga de cada uno —`.company__panel`, `.mega__panel`— busca el
 * siguiente antepasado con posición propia para anclarse. Sin esto sería
 * `.header-stack`, que ya incluye `.subbar`, y el panel aparecería debajo de
 * las dos barras en vez de justo debajo de esta. */
.topbar {
  position: relative;
  background: var(--shell);
  border-bottom: 1px solid rgba(255, 255, 255, .1);
}
.topbar__inner {
  max-width: var(--content); margin: 0 auto;
  height: var(--topbar); padding: 0 24px;
  display: flex; align-items: center; gap: 16px;
}
.topbar__brand {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 14px; font-weight: 700; letter-spacing: -.01em;
  color: #fff; white-space: nowrap;
}
.topbar__brand:hover { text-decoration: none; }
/* El isotipo se sirve a más resolución de la que ocupa, que es lo que lo deja
 * nítido en una pantalla densa; el alto lo fija el CSS y el ancho lo saca de
 * su proporción. */
.topbar__logo { height: 26px; width: auto; }
.topbar__right { margin-left: auto; display: flex; align-items: center; gap: 4px; }
.topbar__user { display: none; font-size: 13px; color: var(--shell-text); padding: 0 8px;
  /* Un nombre largo no parte la barra en dos: se corta con puntos. */
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 220px; }
@media (min-width: 1100px) { .topbar__user { display: inline; } }

.iconbtn {
  display: grid; place-items: center;
  width: 36px; height: 36px; padding: 0;
  border: 0; border-radius: var(--radius-sm);
  background: transparent; color: var(--shell-text);
  font-size: 16px; cursor: pointer;
}
.iconbtn:hover { background: rgba(255, 255, 255, .1); color: #fff; text-decoration: none; }
.iconbtn--on { background: rgba(255, 255, 255, .15); color: #fff; }

/* La campana del V4 (`Header.tsx`, fase 6 del módulo de aprobaciones): el
 * número rojo arriba a la derecha y el panel de 420 px que cae alineado a la
 * derecha, blanco con la sombra grande y las filas separadas por una línea
 * apenas visible. Los tamaños son los del prototipo (`text-sm`, `text-xs`,
 * `text-[10px]`). */
.bell { position: relative; }
.bell > summary { list-style: none; position: relative; }
.bell > summary::-webkit-details-marker { display: none; }
.bell__count {
  position: absolute; top: 2px; right: 2px; transform: translate(35%, -35%);
  min-width: 16px; height: 16px; padding: 0 4px; border-radius: 999px;
  background: #fb2c36; color: #fff; font-size: 11px; font-weight: 600;
  display: inline-flex; align-items: center; justify-content: center; line-height: 1;
}
.bell__panel {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 60;
  width: min(420px, calc(100vw - 16px));
  background: rgba(255, 255, 255, .97); color: var(--foreground);
  border: 1px solid rgba(229, 231, 235, .6); border-radius: 16px;
  box-shadow: 0 25px 50px -12px rgba(0, 0, 0, .25); overflow: hidden;
}
.bell__loading { margin: 0; padding: 24px; font-size: 13px; color: var(--muted-foreground); text-align: center; }
.bell__head {
  display: flex; align-items: center; gap: 8px;
  padding: 16px 20px 12px; border-bottom: 1px solid var(--border-soft);
}
.bell__title { margin: 0; font-size: 16px; font-weight: 600; color: #101828; }
.bell__new {
  display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 999px;
  font-size: 10px; font-weight: 500; background: #dbeafe; color: var(--primary-text);
  border: 1px solid #bedbff;
}
.bell__list { max-height: 400px; overflow-y: auto; }
.bell__item {
  position: relative; display: flex; gap: 12px; padding: 14px 20px;
  border-bottom: 1px solid #f9fafb; color: inherit; text-decoration: none;
  /* `bg-blue-50/30`: lo que espera firma se lee como «no leído». */
  background: rgba(239, 246, 255, .3);
}
.bell__item:hover { background: rgba(239, 246, 255, .6); text-decoration: none; }
.bell__icon {
  flex: none; width: 32px; height: 32px; margin-top: 2px; border-radius: 8px;
  display: inline-flex; align-items: center; justify-content: center;
  background: #fffbeb; color: #e17100;
}
.bell__body { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.bell__item-title { font-size: 14px; line-height: 1.35; font-weight: 600; color: #101828; }
.bell__desc {
  margin-top: 2px; font-size: 12px; line-height: 1.6; color: #6a7282;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.bell__meta { display: flex; align-items: center; gap: 8px; margin-top: 6px; }
.bell__chip {
  display: inline-flex; padding: 2px 6px; border-radius: 4px; font-size: 10px;
  background: #fffbeb; color: #e17100;
}
.bell__time { font-size: 10px; color: var(--faint); }
.bell__action { margin-top: 6px; font-size: 12px; font-weight: 500; color: var(--primary); }
.bell__dot {
  flex: none; width: 8px; height: 8px; margin-top: 6px; border-radius: 999px;
  background: #2b7fff;
}
.bell__empty { padding: 48px 20px; text-align: center; color: var(--faint); }
.bell__empty .icon { width: 32px; height: 32px; color: #d1d5dc; }
.bell__empty p { margin: 8px 0 0; font-size: 14px; }
.bell__empty .bell__empty-sub { margin-top: 2px; font-size: 12px; color: #d1d5dc; }
.bell__foot {
  padding: 12px 20px; border-top: 1px solid var(--border-soft);
  background: rgba(249, 250, 251, .5); font-size: 10px; color: var(--faint); text-align: center;
}
.bell__foot a { color: var(--primary); }

/* Un desplegable es un `<details>`: se abre y se cierra sin JavaScript, y el
 * guion de al lado solo agrega lo que el navegador no da —cerrar los demás al
 * abrir uno, y cerrarse al hacer clic afuera o con Escape—. */
details[data-menu] > summary { list-style: none; cursor: pointer; }
details[data-menu] > summary::-webkit-details-marker { display: none; }

/* La empresa, a la izquierda, con lo que se puede hacer con ella. */
.company { position: relative; }
.company__summary {
  display: flex; align-items: center; gap: 4px;
  height: 36px; padding: 0 12px; max-width: 260px;
  border-radius: var(--radius-sm);
  color: var(--shell-text); font-size: 14px; font-weight: 500;
}
.company__summary:hover, .company[open] > .company__summary { color: #fff; background: rgba(255, 255, 255, .1); }
.company__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.company__chevron, .mega__chevron { font-size: 14px; transition: transform .15s ease; }
.company[open] .company__chevron, .mega[open] .mega__chevron { transform: rotate(180deg); }
.company__panel {
  position: absolute; left: 0; top: 100%; z-index: 60;
  width: 320px; margin-top: 8px; padding: 16px;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--card); color: var(--foreground);
  box-shadow: var(--shadow-lg);
}
.company__title { font-size: 16px; font-weight: 600; }
.company__sub { margin: 0 0 12px; font-size: 14px; color: var(--muted-foreground); }
.company__branch {
  display: flex; align-items: center; gap: 12px;
  margin-bottom: 12px; padding: 12px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--muted);
}
.company__avatar {
  display: grid; place-items: center; flex: 0 0 auto;
  width: 40px; height: 40px; border-radius: var(--radius-pill);
  background: #9333ea; color: #fff; font-size: 18px;
}
.company__branch-name { font-size: 14px; font-weight: 500; }
.company__branch-sub { font-size: 12px; color: var(--muted-foreground); }
.company__links { display: flex; flex-direction: column; gap: 2px; padding-top: 12px; border-top: 1px solid var(--border); }
.company__link {
  display: flex; align-items: center; gap: 8px;
  padding: 8px; border-radius: 6px;
  font-size: 14px; color: #374151;
}
.company__link:hover { background: var(--muted); color: var(--foreground); text-decoration: none; }

/* La barra de módulos, al centro. */
.topnav { display: flex; align-items: center; gap: 2px; margin: 0 auto; }
.topnav__link {
  display: inline-flex; align-items: center; gap: 6px;
  height: 36px; padding: 0 12px;
  border-radius: var(--radius-sm);
  font-size: 14px; font-weight: 500; color: var(--shell-text); white-space: nowrap;
}
.topnav__link:hover { color: #fff; background: rgba(255, 255, 255, .1); text-decoration: none; }
.topnav__link--on { color: #fff; background: rgba(255, 255, 255, .15); font-weight: 500; }

/* Una categoría abre su megamenú: el encabezado con el número romano y los
 * módulos en dos columnas, cada uno con su letra, su nombre y una línea. */
.mega { position: relative; }
.mega[open] > .topnav__link { color: #fff; background: rgba(255, 255, 255, .1); }
.mega__panel {
  position: absolute; left: 0; top: 100%; z-index: 60;
  width: 480px; margin-top: 8px; padding: 12px;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--card); color: var(--foreground);
  box-shadow: var(--shadow-xl);
}
.mega--right .mega__panel { left: auto; right: 0; }
.mega__heading {
  margin: 0 0 8px; padding: 0 4px;
  font-size: 10px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
  color: var(--faint);
}
.mega__grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 4px; }

.module {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 12px 16px; border-radius: var(--radius-sm);
  color: var(--foreground); text-align: left;
}
.module:hover { background: #f9fafb; text-decoration: none; }
.module--on { background: var(--primary-soft); }
/* El icono del módulo. Antes era la letra del catálogo (A–Q), que le decía
 * algo a quien tenía el documento delante y nada a quien usa el sistema. La
 * letra sigue en el código, que es donde sirve para no perderle el hilo al
 * catálogo. */
.module__badge {
  display: grid; place-items: center; flex: 0 0 auto;
  width: 28px; height: 28px; margin-top: 2px;
  border: 1px solid #dbeafe; border-radius: 6px;
  background: var(--primary-soft); color: var(--primary-text);
}
.module__badge .icon { font-size: 16px; }
/* Apagado, pero se abre: lleva a su portada. Solo el `span` —el caso en que
 * ni siquiera hay portada— no responde al cursor. */
.module--soon { background: none; }
a.module--soon:hover { background: #f9fafb; }
span.module--soon, span.module--soon:hover { background: none; cursor: default; }
.module--soon .module__badge { background: var(--muted); border-color: var(--border); color: var(--muted-foreground); }
.module--soon .module__name, .module--soon .module__desc { color: var(--muted-foreground); }
.module__soon {
  display: inline-block; margin-left: 8px; padding: 1px 7px;
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  background: var(--muted); color: var(--muted-foreground);
  font-size: 10px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  vertical-align: 1px;
}
.module__name { display: block; font-size: 14px; font-weight: 500; line-height: 1.25; }
.module__desc { display: block; margin-top: 2px; font-size: 12px; line-height: 1.35; color: var(--muted-foreground); }

/* Las pantallas del módulo abierto, como píldoras. Si no caben, la barra se
 * desplaza de lado; el prototipo hace lo mismo.
 *
 * **Y se nota que sigue.** Recursos humanos tiene quince: en una ventana de
 * 1920 sobran 441 px y la última se cortaba a la mitad sin ninguna señal —en
 * macOS la barra de desplazamiento no se dibuja hasta que uno la mueve—, así
 * que se leía como rota. El degradado dice que continúa y la flecha la mueve,
 * que con un ratón sin rueda horizontal no se podía. Cuando todo cabe, ni el
 * degradado ni la flecha aparecen. */
.subbar { background: var(--shell-2); border-bottom: 1px solid rgba(255, 255, 255, .1); }
/* **La barra ocupa el ancho de la ventana y el contenido se centra con el
 * relleno**, no acotando la caja. Es lo mismo de ver en una pantalla angosta
 * y distinto en una ancha: así el tramo pintado puede llegar hasta el borde
 * izquierdo en vez de frenarse donde empieza la columna (Carlos, 20-09-2026:
 * «completalo con el nuevo color hasta el margen izquierdo»). Con la caja
 * acotada el color no podía salir: la tira se desplaza, y lo que se pinta
 * fuera de ella se recorta.
 *
 * El relleno es el hueco hasta la columna **más** los 24 px de siempre, para
 * que la primera píldora quede a plomo con el contenido de abajo. */
.subbar__wrap { position: relative; }
.subbar__inner {
  --subbar-pad: max(24px, calc((100% - var(--content)) / 2 + 24px));
  /* Lo mismo medido contra la ventana, para que el tramo pintado sepa cuánto
   * tiene que salirse hacia la izquierda. **Va aparte y no es un capricho**:
   * un porcentaje dentro de un hijo se mide contra el hijo, no contra la
   * barra, así que el de arriba no le sirve. Si con una barra de desplazamiento
   * clásica esto se pasa por unos píxeles no se nota: lo que sobra queda fuera
   * de la tira y se recorta. Al relleno sí le hace falta ser exacto, para que
   * la primera píldora quede a plomo con el contenido de abajo. */
  --subbar-sangrado: max(24px, calc((100vw - var(--content)) / 2 + 24px));
  display: flex; align-items: center; gap: 4px;
  padding: 8px var(--subbar-pad);
  overflow-x: auto; overscroll-behavior-x: contain;
  scrollbar-width: thin; scrollbar-color: rgba(255, 255, 255, .2) transparent;
}
/* El degradado tapa el corte y deja ver que hay más del otro lado. No recibe
 * el ratón: lo que está debajo se sigue pudiendo apretar. */
.subbar__wrap::before, .subbar__wrap::after {
  content: ""; position: absolute; top: 0; bottom: 0; width: 56px;
  pointer-events: none; opacity: 0; transition: opacity .15s;
}
.subbar__wrap::before {
  left: 0; background: linear-gradient(to right, var(--shell-2), transparent);
}
.subbar__wrap::after {
  right: 0; background: linear-gradient(to left, var(--shell-2), transparent);
}
.subbar__wrap[data-mas-izq]::before, .subbar__wrap[data-mas-der]::after { opacity: 1; }

.subbar__flecha {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 1;
  display: none; align-items: center; justify-content: center;
  width: 28px; height: 28px; padding: 0;
  border: 0; border-radius: 50%;
  background: rgba(255, 255, 255, .12); color: #fff; cursor: pointer;
}
.subbar__flecha:hover { background: rgba(255, 255, 255, .22); }
.subbar__flecha--izq { left: 2px; }
.subbar__flecha--der { right: 2px; }
.subbar__wrap[data-mas-izq] .subbar__flecha--izq,
.subbar__wrap[data-mas-der] .subbar__flecha--der { display: inline-flex; }
.subbar__flecha .icon { font-size: 16px; }
/* El rótulo de un tramo —«Personal», «Planilla»— separa las píldoras de un
 * lado y las del otro sin partir la barra en dos. Solo el texto: el espacio
 * y la raya son cosa de `.subbar__divider`, abajo (auditoría del
 * 25-09-2026, punto 28 de COMPRAS). */
.subbar__section {
  font-size: 10.5px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: rgba(191, 219, 254, .55);
}
/* El primer tramo de la barra, si trae rótulo —«Personal», en Recursos
 * humanos—: el espacio alrededor del texto. Sin tramo anterior no hay nada
 * que separar, así que no lleva raya (ver `.subbar__divider`). */
.subbar__tramo:first-child > .subbar__section { padding: 0 8px 0 4px; }
/* **Un tramo sin color no dibuja caja.** Con `display: contents` sus píldoras
 * siguen siendo hijas de la barra, así que los módulos que no piden color
 * quedan exactamente como estaban: mismo ancho, mismo espacio, mismo
 * desplazamiento. */
.subbar__tramo { display: contents; }

/* El tramo de lo que se usa a diario, en un azul más claro que la barra
 * (punto 31 del COMERCIAL.docx: «que se distinga también por los colores»).
 *
 * **Empezó en verde, como el diseño, y no funcionó**: una banda verde dentro
 * de una barra azul marino se leía como un aviso y se comía la pantalla
 * (Carlos, 20-09-2026). Un paso más claro del mismo azul dice «este grupo es
 * otro» sin gritar, y deja que la píldora abierta —que es el azul vivo— siga
 * siendo lo que más salta.
 *
 * Se come el relleno de la barra —arriba, abajo y por la izquierda— para que
 * sea una banda de borde a borde y no un recuadro flotando, que es como lo
 * dibuja el diseño.
 *
 * **`--uso-con-raya` es la misma pintura**, para Compras (auditoría del
 * 25-09-2026, punto 28): su captura del menú (`docx/media/image17.png`)
 * trae la banda de color **y** la raya del tramo siguiente a la vez, a
 * diferencia de Gestión comercial y Ventas, donde la raya sobra detrás del
 * color (la regla de abajo). Dos clases con la misma declaración en vez de
 * una condición en la plantilla: así la raya se apaga o no según el módulo,
 * sin que `base.html` tenga que saber cuál es cuál. */
.subbar__tramo--uso,
.subbar__tramo--uso-con-raya {
  display: flex; align-items: center; gap: 4px; flex: 0 0 auto;
  align-self: stretch;
  margin: -8px 6px -8px calc(-1 * var(--subbar-sangrado));
  padding: 0 14px 0 var(--subbar-sangrado);
  background: var(--subbar-uso);
}

/*
 * La raya que separa un tramo del anterior (auditoría del 25-09-2026, punto
 * 28 de COMPRAS). **Vive en su propio elemento** —`.subbar__divider`— y no
 * en el rótulo: antes era el borde izquierdo de `.subbar__section`, así que
 * un tramo sin rótulo —el segundo de Compras, que la captura del menú trae
 * con la raya y sin texto «Tablas»— se quedaba también sin raya. `base.html`
 * dibuja `.subbar__divider` en todo tramo que no sea el primero, lleve o no
 * lleve rótulo adentro; el texto, cuando lo hay, es un `.subbar__section`
 * anidado.
 *
 * Sin tramo anterior no hay nada que separar, así que el primero de la
 * barra no lleva ningún `.subbar__divider` (lo decide `base.html`, no esta
 * hoja).
 */
.subbar__divider {
  flex: 0 0 auto; display: flex; align-items: center;
  margin-left: 10px; padding-left: 14px;
  border-left: 1px solid rgba(255, 255, 255, .12);
}
.subbar__divider > .subbar__section { padding: 0 8px 0 0; }
/* Detrás de una banda de color la raya sobra: el corte ya se ve. **Solo en
 * Gestión comercial y Ventas** (`--uso`); Compras usa `--uso-con-raya` y
 * no entra acá, porque su captura sí trae las dos cosas juntas. */
.subbar__tramo--uso + .subbar__tramo > .subbar__divider {
  margin-left: 0; padding-left: 4px; border-left: 0;
}

/* **Acotadas a la barra**, y no `.pill` a secas. Más abajo hay otra `.pill`
 * —la de los filtros de una lista, sobre fondo claro— y como iba después,
 * ganaba: las de acá salían gris claro sobre la barra azul marino en vez de
 * transparentes con el texto claro, que es lo que pide el prototipo
 * (`text-blue-200`, transparente, y azul solo la abierta). */
.subbar .pill {
  display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto;
  height: 32px; padding: 0 14px;
  border-radius: var(--radius-pill);
  background: none;
  font-size: 14px; font-weight: 400; color: var(--shell-text); white-space: nowrap;
}
.subbar .pill:hover { background: rgba(255, 255, 255, .1); color: #fff; text-decoration: none; }
/* Un paso más clara que el botón, como en el prototipo: la subbarra dice
 * dónde se está parado y no compite con lo que hay que apretar. */
.subbar .pill--on, .subbar .pill--on:hover { background: var(--primary-bright); color: #fff; }
.subbar .pill .icon { font-size: 15px; }

/* Declarada y todavía sin pantalla: se ve, no se abre. Apagada de más para que
 * no se confunda con una que sí lleva a algún lado, y con el rótulo al lado
 * porque el gris solo también podría leerse como «no tenés permiso». */
.subbar .pill--soon,
.subbar .pill--soon:hover {
  background: none; color: rgba(191, 219, 254, .45); cursor: default;
}
.pill__soon {
  margin-left: 2px; padding: 1px 6px;
  border: 1px solid rgba(191, 219, 254, .25); border-radius: var(--radius-pill);
  font-size: 10px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
}

/* En pantallas angostas la barra de módulos baja a un segundo renglón y se
 * desplaza de lado; el prototipo no contempla teléfonos y esto es lo mínimo
 * para que nada desaparezca. Los paneles cuelgan de la barra entera, no del
 * botón: anclados al botón se saldrían por la derecha, y dentro del renglón
 * con desplazamiento los recortaría el desplazamiento mismo. La barra es el
 * bloque contenedor porque es `sticky`, que posiciona.
 *
 * **El corte está en 1359 y no en 1023.** Con cinco categorías (06-09-2026) el
 * renglón necesita unos 1340 píxeles, y en el rango de en medio —un portátil
 * de 1280— la barra empujaba la configuración y la salida fuera de la
 * pantalla. Cuando no caben en una línea, bajan a la suya. */
@media (max-width: 1359px) {
  .topbar__inner { flex-wrap: wrap; height: auto; padding: 8px 12px; gap: 8px; row-gap: 2px; }
  .company__summary { max-width: 160px; }
  .topnav {
    order: 3; flex: 1 1 100%; min-width: 0; margin: 0;
    overflow-x: auto; scrollbar-width: none;
  }
  .topnav::-webkit-scrollbar { display: none; }
  .company, .mega { position: static; }
  .mega__panel, .company__panel {
    position: absolute; left: 12px; right: 12px; top: 100%;
    width: auto; margin-top: 4px;
  }
  .subbar__inner { --subbar-pad: 12px; --subbar-sangrado: 12px; }
  /* En una ventana angosta se mueve con el dedo: la flecha estorbaría. */
  .subbar__flecha { display: none !important; }
}

/* ── Teléfono ─────────────────────────────────────────────────────────────
 *
 * En un teléfono la barra de categorías **no se desplaza de lado: se acomoda
 * en varios renglones**. Con desplazamiento cabe todo, pero «Inicio» y la
 * segunda categoría quedan fuera de vista y hay que descubrir a mano que la
 * tira se mueve; son tres o cuatro puntos, así que verlos todos cuesta un
 * renglón más y se gana no tener que buscarlos.
 *
 * El nombre en texto se va y queda el isotipo. Es lo único que se puede quitar
 * de la primera fila sin perder nada: el logo sigue diciendo de qué sistema se
 * trata, y las tres cosas que sí hacen falta —qué empresa, la configuración y
 * la salida— caben al lado.
 */
/* ── El teléfono: un cajón, no desplegables ──────────────────────────────────
 *
 * La barra de escritorio no se encoge bien y no hay que forzarla. Son tres
 * niveles —categoría, módulo, pantalla— y en 375 px eso salía como dos
 * renglones de categorías, un panel que tapaba el contenido y una tira de diez
 * píldoras que había que desplazar de lado. Cada nivel pedía un gesto distinto
 * y ninguno decía dónde estaba uno.
 *
 * Acá la barra hace **una sola cosa**: un botón de menú y el nombre de la
 * pantalla abierta. Todo lo demás vive en un cajón de pantalla completa donde
 * la jerarquía se lee de arriba abajo, con el módulo abierto ya desplegado.
 *
 * Los blancos de toque son de 44 px, que es el mínimo con el que un dedo no
 * falla. Nada se desplaza de lado.
 */
.drawer { display: none; }
.drawer > summary { list-style: none; cursor: pointer; }
.drawer > summary::-webkit-details-marker { display: none; }
.drawer__btn {
  display: grid; place-items: center;
  width: 40px; height: 40px; margin-left: -6px;
  border-radius: var(--radius-sm); color: var(--shell-text); font-size: 20px;
}
.drawer__btn:hover { background: rgba(255, 255, 255, .1); color: #fff; }
.drawer__btn:active { transform: scale(.92); }
.drawer__btn, .drawer__row, .drawer__sub {
  transition: background-color .12s ease, color .12s ease, transform .12s ease;
}
/* En un teléfono no hay puntero que se pose: lo único que confirma el toque es
 * que el renglón se hunda. Sin esto una lista larga se siente muerta. */
.drawer__row:active, .drawer__sub:active { background: var(--border); }

.drawer__panel { position: fixed; inset: 0; z-index: 80; }

/* El cajón entra: el velo se funde y la hoja se desliza desde el borde.
 *
 * Va con fotogramas y no con una transición. Una transición necesita dos
 * estados y un `<details>` pasa de no existir a existir, así que haría falta
 * `@starting-style`; probado, deja el valor de arranque pegado y el panel no
 * se mueve. Una animación corre sola cuando el elemento aparece, que es
 * exactamente lo que pasa acá.
 *
 * **Solo entra.** Al cerrarse, `<details>` retira el contenido en el acto y no
 * queda nada que animar sin meter JavaScript de por medio; entrar es donde el
 * movimiento sirve —dice de dónde vino el panel— y salir es donde estorba,
 * porque uno ya decidió irse.
 *
 * La curva es de salida rápida y frenado largo: así el panel se siente
 * arrastrado y no disparado. El movimiento reducido lo apaga solo, por la
 * regla general del final del archivo.
 */
.drawer__scrim {
  position: absolute; inset: 0; background: rgba(11, 17, 32, .55);
  animation: cajon-velo .22s ease-out both;
}
.drawer__sheet {
  position: absolute; inset: 0 auto 0 0;
  display: flex; flex-direction: column;
  width: min(320px, 88vw); overflow-y: auto;
  background: var(--card);
  box-shadow: 0 0 40px rgba(11, 17, 32, .35);
  overscroll-behavior: contain;
  animation: cajon-entra .26s cubic-bezier(.32, .72, 0, 1) both;
}
@keyframes cajon-velo { from { opacity: 0; } }
@keyframes cajon-entra { from { transform: translateX(-100%); } }

/* Los renglones se asientan escalonados detrás de la hoja.
 *
 * Son 22 ms de diferencia entre uno y otro: menos y no se percibe, más y se ve
 * como un desfile. Se cortan en el catorceavo y de ahí en adelante todos
 * comparten el último retardo, porque a partir de ahí ya nadie está mirando el
 * movimiento —está buscando adónde ir— y encadenar veinte esperas solo
 * retrasaría el último.
 *
 * Va con `nth-child` y no con una variable puesta en la plantilla: el número de
 * renglones depende de los permisos de quien mira, y una plantilla llevando la
 * cuenta de su propia animación es marcado sucio para un efecto. */
.drawer__sheet > * {
  animation: cajon-fila .22s ease-out both;
  animation-delay: 346ms;
}
.drawer__sheet > *:nth-child(1) { animation-delay: 60ms; }
.drawer__sheet > *:nth-child(2) { animation-delay: 82ms; }
.drawer__sheet > *:nth-child(3) { animation-delay: 104ms; }
.drawer__sheet > *:nth-child(4) { animation-delay: 126ms; }
.drawer__sheet > *:nth-child(5) { animation-delay: 148ms; }
.drawer__sheet > *:nth-child(6) { animation-delay: 170ms; }
.drawer__sheet > *:nth-child(7) { animation-delay: 192ms; }
.drawer__sheet > *:nth-child(8) { animation-delay: 214ms; }
.drawer__sheet > *:nth-child(9) { animation-delay: 236ms; }
.drawer__sheet > *:nth-child(10) { animation-delay: 258ms; }
.drawer__sheet > *:nth-child(11) { animation-delay: 280ms; }
.drawer__sheet > *:nth-child(12) { animation-delay: 302ms; }
.drawer__sheet > *:nth-child(13) { animation-delay: 324ms; }
.drawer__sheet > *:nth-child(14) { animation-delay: 346ms; }
@keyframes cajon-fila { from { opacity: 0; transform: translateY(6px); } }
.drawer__head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 8px;
  padding: 16px; border-bottom: 1px solid var(--border-soft);
  background: var(--shell); color: #fff;
}
.drawer__head .iconbtn { color: rgba(255, 255, 255, .75); }
.drawer__head .iconbtn:hover { color: #fff; background: rgba(255, 255, 255, .1); }
.drawer__company { margin: 0; font-size: 15px; font-weight: 600; }
.drawer__branch { margin: 2px 0 0; font-size: 12px; color: var(--shell-text); }
.drawer__section {
  margin: 12px 0 2px; padding: 0 16px;
  font-size: 10.5px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted-foreground);
}
.drawer__row {
  display: flex; align-items: center; gap: 12px; width: 100%;
  min-height: 44px; padding: 10px 16px;
  border: 0; background: none; color: var(--foreground);
  font-family: inherit; font-size: 15px; text-align: left; cursor: pointer;
}
.drawer__row:hover { background: var(--muted); text-decoration: none; }
.drawer__row--on { background: var(--primary-soft); color: var(--primary-text); font-weight: 600; }
.drawer__row--out { color: var(--danger-text); }
.drawer__row .icon { font-size: 18px; flex: 0 0 auto; }
.drawer__name { flex: 1 1 auto; min-width: 0; }
.drawer__badge {
  display: grid; place-items: center; flex: 0 0 auto;
  width: 26px; height: 26px;
  border: 1px solid #dbeafe; border-radius: 6px;
  background: var(--primary-soft); color: var(--primary-text);
}
.drawer__badge .icon { font-size: 15px; }
.drawer__row--soon .drawer__badge {
  background: var(--muted); border-color: var(--border); color: var(--muted-foreground);
}
.drawer__row--soon .drawer__name { color: var(--muted-foreground); }
.drawer__tag {
  flex: 0 0 auto; padding: 1px 7px;
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  background: var(--muted); color: var(--muted-foreground);
  font-size: 10px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
}
.drawer__caret { flex: 0 0 auto; font-size: 16px; color: var(--muted-foreground); transition: transform .15s; }
.drawer__mod[open] > summary .drawer__caret { transform: rotate(180deg); }
.drawer__mod > summary { list-style: none; }
.drawer__mod > summary::-webkit-details-marker { display: none; }
.drawer__sub {
  display: flex; align-items: center; gap: 8px;
  min-height: 42px; padding: 8px 16px 8px 54px;
  color: var(--muted-foreground); font-size: 14px;
}
.drawer__sub:hover { background: var(--muted); color: var(--foreground); text-decoration: none; }
.drawer__sub--on {
  background: var(--primary-soft); color: var(--primary-text); font-weight: 600;
  box-shadow: inset 3px 0 0 var(--primary);
}
.drawer__sub--soon, .drawer__sub--soon:hover { background: none; color: var(--faint); cursor: default; }
.drawer__foot {
  margin: auto 0 0; padding: 14px 16px;
  border-top: 1px solid var(--border-soft);
  font-size: 12px; color: var(--muted-foreground);
}

@media (max-width: 639px) {
  .mega__grid { grid-template-columns: minmax(0, 1fr); }
  .drawer { display: block; }

  /* La barra de escritorio entera se va: las categorías, los módulos y las
   * pantallas del módulo viven en el cajón. Sin esto habría dos navegaciones
   * a la vez diciendo lo mismo. */
  .topnav, .subbar, .company, .topbar__right { display: none; }
  .topbar__inner { flex-wrap: nowrap; height: 56px; padding: 0 12px; gap: 10px; }
  .topbar__brand { margin-left: auto; }
  .topbar__word { display: none; }
  .topbar__here {
    flex: 1 1 auto; min-width: 0;
    font-size: 16px; font-weight: 600; color: #fff;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }

  /* Con el cajón abierto la página de atrás no se mueve. Sin esto, al
   * desplazar el cajón hasta el final sigue la página, que es lo que hace que
   * un menú se sienta suelto. */
  body:has(.drawer[open]) { overflow: hidden; }
}
@media (min-width: 640px) { .topbar__here { display: none; } }

/* ── Lienzo ──────────────────────────────────────────────────────────────── */
/* ── Las personas de contacto ────────────────────────────────────────────────
 *
 * Una tarjeta por persona y no una fila de tabla (Laura, 06-09-2026). Con
 * cinco columnas para tres datos, la mitad de las celdas quedaban en «—» y
 * había que leer de lado buscando cuál tenía algo; acá el nombre va arriba y
 * cómo ubicarla debajo, que es el orden en que se busca.
 *
 * El bloque entero va sobre un fondo apenas distinto del de la tarjeta que lo
 * contiene: separa la lista de los teléfonos del cliente, que están justo
 * arriba, sin meter otra caja con borde adentro de una caja con borde. */
.contactlist {
  margin-top: 16px; padding: 4px 12px 12px;
  border-radius: var(--radius); background: var(--muted);
  display: flex; flex-direction: column; gap: 8px;
}
.contactlist__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; padding: 10px 8px 6px;
}
/* Los párrafos de acá van sin margen propio: el espacio lo pone el `gap` del
 * bloque. Se escriben con dos clases porque esta lista vive dentro del
 * formulario de la ficha, y `.form p` le mete 16px por debajo a todo párrafo
 * —que es lo correcto entre campos y sobra entre el nombre y el teléfono de
 * la misma persona. */
.contactlist .contactlist__count {
  margin: 0; font-size: 13px; font-weight: 600; color: var(--muted-foreground);
}
/* La tarjeta de contacto vivió acá hasta el 12-09-2026, cuando las tres listas
 * de la ficha —contactos, acuerdos y convenios— pasaron a tablas y se quedó sin
 * nadie que la usara. El encabezado de la lista, `.contactlist`, sí se queda:
 * es el que dice cuántos hay y lleva el botón de agregar. */

/* ── Configuración: el riel y la portada ─────────────────────────────────────
 *
 * El engranaje tiene diecisiete pantallas y la barra de pestañas no da para
 * tantas: en una ventana de 1440 la tira pedía 2.744 píxeles y siete quedaban
 * siempre fuera de vista. Van en un riel a la izquierda, agrupadas, con la
 * portada de tarjetas como entrada.
 *
 * El riel aguanta hasta el teléfono, donde lo reemplaza el cajón: entre 640 y
 * 1023 la barra de pestañas se acomoda sola, pero acá no hay barra que
 * acomodar y sin riel la pantalla se quedaría sin manera de moverse. */
.body { flex: 1 1 auto; display: flex; min-width: 0; }
.rail {
  flex: 0 0 260px; width: 260px;
  display: flex; flex-direction: column; gap: 2px;
  padding: 16px 12px;
  background: var(--card); border-right: 1px solid var(--border);
}
.rail__home {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 10px; margin-bottom: 8px;
  border-radius: var(--radius-sm);
  font-size: 13px; font-weight: 600; color: var(--foreground);
}
.rail__home:hover { background: var(--muted); text-decoration: none; }
.rail__home--on { background: var(--primary-soft); color: var(--primary-text); }
.rail__search {
  display: flex; align-items: center; gap: 8px;
  padding: 0 10px; margin-bottom: 12px;
  border: 1px solid var(--border-input); border-radius: var(--radius-sm);
  background: var(--card); color: var(--faint);
}
.rail__search input {
  flex: 1 1 auto; min-width: 0; height: 34px;
  border: 0; background: none; font: inherit; font-size: 13px;
  color: var(--foreground); outline: none;
}
.rail__group { display: flex; flex-direction: column; gap: 2px; }
/* `hidden` gana por especificidad solo si se escribe: una clase con `display`
 * le pasa por encima al `[hidden]` del navegador y el tramo filtrado se
 * quedaría a la vista. */
.rail__group[hidden] { display: none; }
/* Un subgrupo se sangra y lleva su hilo a la izquierda, para que se vea de
 * quién cuelga. Hoy no hay ninguno; el día que lo haya, ya está dibujado. */
.rail__group .rail__group {
  margin: 4px 0 4px 10px; padding-left: 8px;
  border-left: 1px solid var(--border);
}
.rail__title {
  margin: 14px 0 4px; padding: 0 10px;
  font-size: 11px; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--faint);
}
.rail__item {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 10px; border-radius: var(--radius-sm);
  font-size: 13px; color: var(--foreground);
}
.rail__item:hover { background: var(--muted); text-decoration: none; }
/* Igual que el tramo: sin escribirlo, `display: flex` le gana al `[hidden]`
 * del navegador y el punto filtrado se queda a la vista. */
.rail__item[hidden] { display: none; }
.rail__item--on { background: var(--primary-soft); color: var(--primary-text); font-weight: 600; }
.rail__item .icon, .rail__home .icon, .rail__search .icon { font-size: 16px; flex: 0 0 auto; }
.rail__empty { padding: 8px 10px; font-size: 13px; color: var(--muted-foreground); }

/* La portada. Las tarjetas se acomodan solas: tres en un monitor, dos en una
 * ventana a medias, una en el teléfono. */
.setgroup { margin-bottom: 32px; }
.setgroup__head { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 12px; }
.setgroup__icon {
  display: grid; place-items: center; flex: 0 0 auto;
  width: 32px; height: 32px;
  border: 1px solid #dbeafe; border-radius: var(--radius-sm);
  background: var(--primary-soft); color: var(--primary-text);
}
.setgroup__name { margin: 0; font-size: 15px; font-weight: 700; }
.setgroup__desc { margin: 2px 0 0; font-size: 13px; color: var(--muted-foreground); }
.setgroup__grid {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}
.setcard {
  display: grid; gap: 4px;
  padding: 16px; border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--card); color: var(--foreground);
}
.setcard:hover {
  border-color: #bfdbfe; background: var(--primary-soft); text-decoration: none;
}
.setcard__icon {
  display: grid; place-items: center;
  width: 32px; height: 32px; margin-bottom: 4px;
  border: 1px solid #dbeafe; border-radius: var(--radius-sm);
  background: var(--primary-soft); color: var(--primary-text);
}
.setcard__icon .icon { font-size: 16px; }
.setcard__name { font-size: 14px; font-weight: 600; color: var(--primary-text); }
.setcard__desc { font-size: 13px; color: var(--muted-foreground); }

/* La vuelta a la portada aparece justo cuando el riel se va: en el teléfono
 * la portada es el menú de Configuración, y sin este enlace había que salir
 * por el cajón para cambiar de pantalla. */
.setback { display: none; }
@media (max-width: 1023px) { .rail { flex-basis: 220px; width: 220px; } }
@media (max-width: 639px) {
  .rail { display: none; }
  .setback {
    display: inline-flex; align-items: center; gap: 4px;
    margin-bottom: 12px; padding: 6px 10px 6px 6px;
    border: 1px solid var(--border); border-radius: var(--radius-pill);
    background: var(--card);
    font-size: 13px; font-weight: 600; color: var(--foreground);
  }
  .setback .icon { font-size: 16px; }
}

.canvas { flex: 1 1 auto; min-width: 0; background: var(--canvas); }
.canvas__inner { max-width: var(--content); margin: 0 auto; padding: 24px; }
@media (max-width: 767px) { .canvas__inner { padding: 16px 12px; } }

/* ── Encabezado de página ────────────────────────────────────────────────── */
.page-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 16px; margin-bottom: 24px; flex-wrap: wrap;
}
.page-head h1 { font-size: 24px; letter-spacing: -.02em; }
.page-head p { margin: 4px 0 0; font-size: 14px; color: var(--faint); }
.page-head__actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

.section-title { font-family: var(--font-display); font-size: 15px; font-weight: 600; }
.section-sub { margin: 2px 0 0; font-size: 12px; color: var(--muted-foreground); }

/* ── Tarjetas ────────────────────────────────────────────────────────────── */
.card {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--card);
  box-shadow: var(--shadow-sm);
}

.card--pad { padding: 20px; }
/* Una ficha de una sola columna de tarjetas: centrada y con un ancho de
 * lectura, no estirada a los 1.800 del lienzo. */
.sheet { max-width: 1000px; margin-inline: auto; }
/* La hoja de un formulario de página. 860 px son dos columnas de campo
 * cómodas; 640, que es lo que había, daban una sola y dejaban media pantalla
 * en blanco. Va como clase y no como estilo suelto porque son treinta
 * pantallas: escrito treinta veces, cambiarlo es cambiarlo treinta veces. */
.sheet--form { max-width: 860px; }
/* La hoja angosta. La quieren tres cosas que no son un formulario de campos:
 * la página de una confirmación —que es una pregunta y a veces el motivo—, los
 * formularios de una sola columna (`form--simple`) y el de subir un archivo.
 * Anchas se leen peor: una línea de texto de 860 px obliga a barrer la cabeza.
 *
 * Va como clase y no como estilo suelto **por lo mismo que `sheet--form`**: se
 * escribía a mano en cada plantilla y había seis anchos distintos —520, 560,
 * 600, 640, 720 y 760— que nadie eligió, solo se fueron copiando. */
.sheet--narrow { max-width: 560px; }
.sheet--narrow .form { max-width: none; }
/* Y la que no se topa: la usa el simulador, que debajo del formulario lleva las
 * tablas del desglose. Con el ancho de lectura quedaba media pantalla en blanco
 * a la derecha. */
.sheet--wide { max-width: none; }

/* **Dos tarjetas seguidas se separan solas.**
 *
 * Antes cada plantilla se lo ponía a mano con un `style="margin-top:16px"` —lo
 * hacían veinte— y donde alguien lo olvidaba las tarjetas salían pegadas, sin
 * que nada avisara. Es el mismo caso que la cabecera de tarjeta: algo que hay
 * que acordarse de poner en el 90 % de los casos no es un detalle de cada
 * pantalla, es la regla.
 *
 * **La regla es general y se apaga dentro de una rejilla**, no al revés. Se
 * intentó acotarla a las hijas de la hoja y del lienzo y no alcanzaba: hay
 * pantallas que envuelven sus tarjetas en un `<div id="contenido">` para que
 * htmx lo reemplace, y ahí las tarjetas quedaban pegadas. Enumerar los
 * envoltorios buenos es la clase de lista que siempre queda corta; enumerar
 * las rejillas —donde el `gap` ya separa y un margen correría la fila— es
 * cerrado y se ve venir. */
.card + .card { margin-top: 16px; }
/* Y una tarjeta que va **después de una rejilla** también se separa: pasa en el
 * panel, en el detalle de bitácora y en el kardex, donde entre dos tarjetas hay
 * una fila de tarjetas lado a lado. */
.grid + .card { margin-top: 16px; }
/* Y al revés, que faltaba: una rejilla **después de una tarjeta** quedaba
 * pegada a ella. Se veía en la ficha del producto —la tarjeta de la foto y
 * debajo, sin una raya de aire, «Identidad» y «Costo» (Carlos, 14-09-2026)—.
 *
 * No se vio antes porque esa rejilla se escribe con un `{% if %}` en el
 * `class`, así que buscarla en las plantillas por su nombre no la encuentra.
 * `.stack` va en la misma regla porque es la otra rama de ese mismo `if`: sin
 * ella, apagar el permiso de ver costos arreglaba una pantalla y despegaba la
 * otra. Lo cuida `core/tests/test_los_bloques_se_separan.py`. */
.card + .grid,
.card + .stack,
.stack + .card { margin-top: 16px; }
.grid > .card + .card,
.kpis > .card + .card,
.tiles > .card + .card,
/* La vitrina y el carrito, lado a lado. Faltaba, y la segunda tarjeta bajaba
 * 16 px: las dos cajas quedaban descuadradas y se veía en las tres pantallas
 * que usan esta rejilla (Carlos, 10-09-2026).
 *
 * Es exactamente lo que el comentario de arriba daba por «cerrado y se ve
 * venir», y no se vio. Por eso la lista ya no se cuida sola: la recorre
 * `core/tests/test_las_tarjetas_cuadran.py`, que busca en las plantillas toda
 * rejilla con dos tarjetas hijas y exige que esté acá. */
.tomapedido > .card + .card { margin-top: 0; }
/* El título a la izquierda y lo que se pueda hacer a la derecha, igual que el
 * encabezado de página. Antes eso vivía en un modificador aparte —`--action`—
 * que había que acordarse de poner: diez cabeceras con botón no lo tenían y el
 * botón les quedaba pegado al título, como una palabra más del renglón. Un
 * modificador que se olvida en el 90 % de los casos no es un modificador, es la
 * regla, así que se mudó acá.
 *
 * La cabecera espera **dos piezas**: el título y el bloque de acciones. Cuando
 * el título lleva subtítulo, los dos van dentro de un `<div>`, o el subtítulo se
 * iría a la derecha creyéndose una acción. */
.card__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; flex-wrap: wrap;
  padding: 16px 20px; border-bottom: 1px solid var(--border);
}
.card__body { padding: 20px; }

.grid { display: grid; gap: 16px; }
/* Las dos rejillas arrancan angostas y se abren con la pantalla, igual que el
   resto del proyecto. `.grid--2` la usan las fichas —cliente, proveedor,
   persona, empresa, bitácora—: en un teléfono, dos columnas dejaban cada
   tarjeta con la etiqueta y el dato peleando por 150 px, y el nombre para
   tributación partido en cuatro renglones. */
.grid--2 { grid-template-columns: minmax(0, 1fr); }
@media (min-width: 768px) { .grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* Tres es lo que pide la cabecera de un pedido —cliente, condición de pago y
   observaciones— y arranca angosta por lo mismo que las otras: en un teléfono,
   tres columnas dejan la etiqueta y el dato peleando por cien píxeles. */
.grid--3 { grid-template-columns: minmax(0, 1fr); }
@media (min-width: 768px) { .grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 768px) { .grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.stack > * + * { margin-top: 16px; }

/* ── Indicador ───────────────────────────────────────────────────────────── */
.stat { padding: 20px; }
.stat__label {
  display: block;
  font-size: 12px; font-weight: 500; letter-spacing: .04em; text-transform: uppercase;
  color: var(--muted-foreground);
}
.stat__value {
  margin-top: 8px;
  font-family: var(--font-display); font-size: 24px; font-weight: 700; line-height: 1.2;
  letter-spacing: -.02em; font-variant-numeric: tabular-nums;
}
.stat__value--danger { color: var(--danger); }
.stat__value--warning { color: var(--warning); }
.stat__value--success { color: var(--success); }
.stat__value--primary { color: var(--primary); }
/* «Comprometido» de Stock y de su ficha: naranja (`text-orange-600` de la
   V4), no el ámbar de `--warning`. Aparte y no una redefinición de
   `--warning-text` porque esa variable la sigue usando el resto de la
   aplicación tal como estaba (Carlos, auditoría 25-09-2026). */
.stat__value--naranja { color: #c2410c; }
.stat__sub { margin-top: 4px; font-size: 12px; color: var(--faint); }

/* La cabecera de la ficha del producto (`ProductoDetalle` de la V4): la foto o
 * el ícono en un cuadro azul claro, y las cuatro cifras en fichas grises dentro
 * de la misma tarjeta —no tarjetas sueltas—, con comprometido en ámbar y en
 * tránsito en azul como el prototipo. */
/* 56 px (`size-14`, V4:2334) con el ícono de 24 px (`size-6`): antes 48 px
   (re-auditoría final del 25-09-2026, punto 23, menor). */
.product-thumb {
  flex: none; display: grid; place-items: center; width: 56px; height: 56px;
  border-radius: var(--radius); background: var(--primary-soft); color: var(--primary);
  overflow: hidden;
}
.product-thumb img { width: 100%; height: 100%; object-fit: cover; }
.product-thumb .icon { width: 24px; height: 24px; }
.product-figures {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 12px; margin-top: 16px;
}
.product-figures__item { padding: 12px 16px; border-radius: var(--radius); background: var(--muted); }
.product-figures__label { margin: 0 0 2px; font-size: 12px; color: var(--faint); }
.product-figures__value { margin: 0; font-size: 18px; font-weight: 700; font-family: var(--font-sans); font-variant-numeric: tabular-nums; }
.product-figures__name {
  margin: 0; font-size: 14px; font-weight: 700;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.product-figures__sub { margin: 2px 0 0; font-size: 11px; color: var(--faint); }
.product-figures__price { font-size: 12px; font-weight: 600; color: var(--success); }
@media (max-width: 720px) { .product-figures { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* ── Ficha ───────────────────────────────────────────────────────────────────
 * Un dato por renglón: la etiqueta a la izquierda en gris, el valor a la
 * derecha. Es la forma del prototipo para lo que se mira y no se edita.
 */
.detail { padding: 8px 20px 16px; }
.detail__row {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 8px 0;
}
.detail__row + .detail__row { border-top: 1px solid var(--border); }
.detail__row > span, .detail__row span:first-child {
  flex: 0 0 auto; white-space: nowrap;
  font-size: 12.5px; color: var(--muted-foreground);
}
/* `min-width: 0` no es decorativo: sin él un ítem de reparto no baja de lo que
   mide su contenido, y un valor sin espacios —un correo, una dirección— se sale
   de la tarjeta en vez de acomodarse. `anywhere` es el que lo parte, y además
   deja que encoja; `break-word` no lo haría. */
.detail__row strong {
  font-size: 13px; font-weight: 500; text-align: right;
  min-width: 0; overflow-wrap: anywhere;
}
.detail__row--action { align-items: center; }
/* La ayuda del punto de reorden calculado (regla 13 de INVENTARIOS): el
   desglose de la fórmula se abre al pasar el cursor o al enfocar la cifra con
   el teclado, sin pedir nada al servidor. */
.reorder-tip { position: relative; display: inline-flex; align-items: center; gap: 4px; cursor: help; }
.reorder-tip .icon { color: var(--muted-foreground); }
.reorder-tip__body {
  display: none; position: absolute; right: 0; top: calc(100% + 6px); z-index: 30;
  width: 320px; padding: 10px 12px; border-radius: 8px;
  background: var(--card, #fff); border: 1px solid var(--border);
  box-shadow: 0 8px 24px rgba(15, 23, 42, .12);
  flex-direction: column; gap: 4px; text-align: left;
  font-family: var(--font-sans); font-variant-numeric: normal;
}
.reorder-tip:hover .reorder-tip__body, .reorder-tip:focus .reorder-tip__body,
.reorder-tip:focus-within .reorder-tip__body { display: flex; }
.detail__row .reorder-tip__body span {
  white-space: normal; font-size: 12px; font-weight: 400; color: var(--foreground);
}
.detail__row .reorder-tip__body .reorder-tip__title { font-weight: 600; }
.detail__row--action > div { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; }
.detail__row--action strong { text-align: left; }

/* ── Distintivos ─────────────────────────────────────────────────────────── */
.badge {
  display: inline-flex; align-items: center; gap: 5px;
  border-radius: var(--radius-pill); padding: 2px 8px;
  font-size: 12px; font-weight: 500;
  background: var(--muted); color: var(--muted-foreground);
}
.badge--primary { background: var(--primary-soft); color: var(--primary-text); }
.badge--success { background: var(--success-soft); color: var(--success-text); }
.badge--warning { background: var(--warning-soft); color: var(--warning-text); }
.badge--danger  { background: var(--danger-soft);  color: var(--danger-text); }
/*
El cuarto estado de la validación. No alcanza con los tres de siempre: una
marca sin cerrar no es un error —nadie llegó tarde— pero tampoco cuadra, y
pintarla del amarillo de la discrepancia mandaría a explicar una diferencia
que todavía no existe. Lo que hay que hacer es ir a cerrarla.
*/
.badge--pending { background: #ffedd5; color: #c2410c; }

/*
La insignia «NUEVO» de la clasificación ABC del producto: morada, para no
repetir el verde de A ni el azul de B, calco exacto del Figma V4
—`bg-violet-100 text-violet-700 ring-1 ring-violet-300`, `ComprasPage.tsx` y
`BodegaPage.tsx`—. Carlos la pidió así el 25-09-2026, en lugar del naranja
provisional del punto 2 de COMPRAS.
*/
/* `font-weight: 700` y no los 500 de `.badge` (auditoría 25-09-2026: la V4
 * pinta «NUEVO» en `font-bold`, no en el peso normal de una insignia). */
.badge--purple { background: #ede9fe; color: #6d28d9; font-weight: 700; }
.badge--purple.badge--state { border-color: #c4b5fd; }
.badge--purple.badge--state .dot { background: #7c3aed; }

/*
─ Los rubros, cada uno con su color ──────────────────────────────────────────

El Figma pinta la píldora del rubro de un color distinto por rubro, y con seis
rubros de ejemplo eso se lee de un vistazo. La empresa tiene **51**, y no
existen 51 colores que una persona distinga: a partir del octavo son todos
«un azul».

Así que el color no identifica al rubro: **marca dónde termina un grupo**. La
vitrina va ordenada por rubro, así que las tarjetas del mismo caen juntas y lo
que el ojo necesita es ver el corte. Doce tonos alcanzan para eso, y el rubro
sigue diciendo su nombre adentro de la píldora, que es lo que de verdad lo
identifica.

Cada uno es un fondo suave con su texto oscuro encima, como los distintivos de
estado. En oscuro se invierten: fondo hondo y texto claro.
*/
.badge--rubro-1  { background: #dbeafe; color: #1e40af; }
.badge--rubro-2  { background: #dcfce7; color: #166534; }
.badge--rubro-3  { background: #fef3c7; color: #92400e; }
.badge--rubro-4  { background: #fce7f3; color: #9d174d; }
.badge--rubro-5  { background: #ede9fe; color: #5b21b6; }
.badge--rubro-6  { background: #ccfbf1; color: #115e59; }
.badge--rubro-7  { background: #ffe4e6; color: #9f1239; }
.badge--rubro-8  { background: #e0e7ff; color: #3730a3; }
.badge--rubro-9  { background: #fef9c3; color: #854d0e; }
.badge--rubro-10 { background: #f3e8ff; color: #6b21a8; }
.badge--rubro-11 { background: #cffafe; color: #155e75; }
.badge--rubro-12 { background: #ffedd5; color: #9a3412; }

[data-theme="dark"] .badge--rubro-1  { background: #1e3a5f; color: #bfdbfe; }
[data-theme="dark"] .badge--rubro-2  { background: #14532d; color: #bbf7d0; }
[data-theme="dark"] .badge--rubro-3  { background: #4a2c0a; color: #fde68a; }
[data-theme="dark"] .badge--rubro-4  { background: #500724; color: #fbcfe8; }
[data-theme="dark"] .badge--rubro-5  { background: #2e1065; color: #ddd6fe; }
[data-theme="dark"] .badge--rubro-6  { background: #042f2e; color: #99f6e4; }
[data-theme="dark"] .badge--rubro-7  { background: #4c0519; color: #fecdd3; }
[data-theme="dark"] .badge--rubro-8  { background: #1e1b4b; color: #c7d2fe; }
[data-theme="dark"] .badge--rubro-9  { background: #422006; color: #fef08a; }
[data-theme="dark"] .badge--rubro-10 { background: #3b0764; color: #e9d5ff; }
[data-theme="dark"] .badge--rubro-11 { background: #083344; color: #a5f3fc; }
[data-theme="dark"] .badge--rubro-12 { background: #431407; color: #fed7aa; }

/*
El estado de una marca, como lo pinta el prototipo: la píldora lleva borde y un
punto de su color adentro. El punto no decora: repite lo que ya dice el color,
que es lo que deja leer el estado a quien no distingue el verde del amarillo.
*/
.badge--state { border: 1px solid transparent; }
.badge--state .dot { width: 6px; height: 6px; }
.badge--success.badge--state { border-color: #bbf7d0; }
.badge--success.badge--state .dot { background: #22c55e; }
.badge--warning.badge--state { border-color: #fef08a; }
.badge--warning.badge--state .dot { background: #eab308; }
.badge--danger.badge--state { border-color: #fecaca; }
.badge--danger.badge--state .dot { background: #ef4444; }
.badge--pending.badge--state { border-color: #fed7aa; }
.badge--pending.badge--state .dot { background: #f97316; }
/* **«Pendiente» de la captura de Alisto** (punto 6, 26-09-2026): gris, sin
 * teñir —es `.badge` a secas—, solo con el borde y el punto que le faltan
 * para leerse como estado. */
.badge--muted.badge--state { border-color: var(--border); }
.badge--muted.badge--state .dot { background: var(--muted-foreground); }
/* **El que avanza solo va en azul**, como «En proceso» en el prototipo.
 *
 * Sin este cuarto color, todo lo que no estaba terminado ni muerto caía en
 * ámbar, y el ámbar quiere decir «alguien tiene que hacer algo». Con 493
 * pedidos facturados gritando, gritar deja de significar nada. */
.badge--primary.badge--state { border-color: #bedbff; }
.badge--primary.badge--state .dot { background: var(--primary-bright); }

/*
La insignia ABC de la letra —A, B o C—, un solo componente para las cuatro
pantallas que la dibujan (Stock, el kardex, Clasificación ABC y la ficha del
producto): un círculo de 24 px, calco exacto de la V4
(`inline-flex items-center justify-center size-6 rounded-full text-xs
font-bold`). «NUEVO» no entra acá: en la V4 es una píldora con texto, no un
círculo —sigue siendo `.badge--purple`—. Carlos, auditoría 25-09-2026.
*/
.abc-circle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; border-radius: var(--radius-pill);
  font-size: 12px; font-weight: 700; line-height: 1;
}
.abc-circle--a { background: var(--success-soft); color: var(--success-text); }
/* `#dbeafe` (blue-100) y no `--primary-soft` (#eff6ff, blue-50): la V4 usa
 * `bg-blue-100` para la B, un tono más marcado que el resto de lo «primary»
 * suave de la aplicación (auditoría 25-09-2026). Va suelto y no en la
 * variable compartida, que sí tiene que quedarse en blue-50 para todo lo
 * demás que la usa. */
.abc-circle--b { background: #dbeafe; color: var(--primary-text); }
.abc-circle--c { background: var(--muted); color: var(--muted-foreground); }

/* ── Botones ─────────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: 36px; padding: 0 14px;
  border: 1px solid transparent; border-radius: var(--radius-sm);
  font-family: inherit; font-size: 14px; font-weight: 500;
  cursor: pointer; white-space: nowrap;
  background: var(--primary); color: var(--primary-foreground);
}
.btn:hover { filter: brightness(1.08); text-decoration: none; }
.btn:disabled { opacity: .5; cursor: not-allowed; filter: none; }

/* ── Que se vea que está trabajando ──────────────────────────────────────────
 * La aplicación no recarga: cambia pedazos por detrás. Eso la hace rápida y
 * también la hace parecer muerta, porque el navegador ya no pone su barra de
 * carga. Acá se pone la nuestra.
 *
 * `htmx-request` la agrega htmx sola mientras la petición está en vuelo, y la
 * quita al terminar: no hay nada que apagar a mano.
 */

/* En el botón que se apretó. La rueda ocupa el sitio del texto en vez de
 * agregarse al lado, para que el botón no cambie de ancho y no empuje lo que
 * tiene alrededor. */
.btn__esperando { display: none; }
/* Un formulario enviado con htmx pone `htmx-request` en el `<form>`, no en su
 * botón: por eso las dos formas, la del botón que pide y la del botón de envío
 * del formulario que está pidiendo (el modal genérico, 25-09-2026). */
.btn.htmx-request .btn__texto,
form.htmx-request .btn[type="submit"] .btn__texto { visibility: hidden; }
.btn.htmx-request .icon,
form.htmx-request .btn[type="submit"] .icon { visibility: hidden; }
.btn.htmx-request,
form.htmx-request .btn[type="submit"] { position: relative; }
.btn.htmx-request .btn__esperando,
form.htmx-request .btn[type="submit"] .btn__esperando {
  display: block; position: absolute;
  width: 14px; height: 14px;
  border: 2px solid currentColor; border-right-color: transparent;
  border-radius: 50%;
  animation: girando .6s linear infinite;
}
@keyframes girando { to { transform: rotate(360deg); } }

/* Y arriba, cruzando la ventana, para cualquier petición: es la que reemplaza
 * a la barra de carga del navegador. Va fija y por encima de todo, incluido el
 * modal, porque también se usa desde adentro. */
.cargando {
  position: fixed; inset: 0 auto auto 0;
  width: 100%; height: 2px;
  z-index: 100;
  opacity: 0; pointer-events: none;
  background: linear-gradient(90deg,
    transparent 0%, var(--primary) 40%, var(--primary) 60%, transparent 100%);
  background-size: 40% 100%; background-repeat: no-repeat;
}
.cargando--si { opacity: 1; animation: cruzando 1.1s ease-in-out infinite; }
@keyframes cruzando {
  from { background-position: -40% 0; }
  to   { background-position: 140% 0; }
}
.btn--outline { background: var(--card); color: var(--foreground); border-color: var(--border-input); }
.btn--outline:hover { background: var(--muted); filter: none; }
/* Texto y borde azules, como el «Ver» de la V4 (`BodegaPage.tsx:1509`,
 * auditoría de Alisto, 27-09-2026): un botón secundario que no compite con
 * el gris de `--outline`, para la acción que se usa en cada fila. */
.btn--link { background: var(--card); color: var(--primary); border-color: var(--primary); }
.btn--link:hover { background: var(--primary-soft); filter: none; }
.btn--ghost { background: transparent; color: var(--muted-foreground); }
.btn--ghost:hover { background: var(--muted); color: var(--foreground); filter: none; }
.btn--danger { background: var(--danger); }
/*
Aprobar y rechazar, como en el prototipo: la que confirma va en verde lleno y la
que niega en blanco con el borde rojo. Es el único par de la aplicación donde
las dos acciones pesan igual y se ofrecen juntas; en el resto, la que niega es
un renglón del menú y le alcanza con `--danger`.
*/
.btn--ok { background: var(--success); }
.btn--no { background: var(--card); color: var(--danger-text); border-color: #ffa2a2; }
.btn--no:hover { background: var(--danger-soft); filter: none; }
.btn--ok:hover { background: var(--success-text); filter: none; }
/* El botón de cobrar, con el degradado del prototipo.
 *
 * Es la única acción de la fila que mueve plata, y comparte renglón con «A
 * trámite», que no lo hace. Con las dos en blanco pesaban lo mismo y había
 * que leer las dos para encontrar la que se usa todos los días (Carlos,
 * 12-09-2026). El degradado es el del botón de cobro del prototipo móvil, que
 * es el mismo gesto en la otra pantalla.
 *
 * No lleva `:hover` propio: el aclarado del botón base sirve igual sobre el
 * degradado, y un segundo degradado para el `hover` son dos que mantener. */
.btn--cobro {
  background: linear-gradient(135deg, #4f7fff, #7c3aed);
  border-color: transparent; color: #fff;
}

.btn--sm { height: 30px; padding: 0 10px; font-size: 12px; border-radius: 6px; }

/* El botón de dentro de una fila, como en el prototipo: una píldora chica.
 *
 * En Figma es `px-2.5 py-1 rounded-full text-xs font-medium`, que son 24 px de
 * alto y esquina completa. Con `btn--sm` salían de 30 px y con la esquina de 6
 * px —un botón de formulario metido en una tabla—, y al lado de las píldoras
 * de estado de la misma fila se veían de otra familia. */
.btn--xs {
  height: 24px; padding: 0 10px;
  font-size: 12px;
  border-radius: var(--radius-pill);
}

/* Un botón cuyo texto cambia con el estado de la fila —«Suspender» o
 * «Reactivar»— y que cierra un grupo alineado a la derecha. Sin un ancho
 * reservado, la diferencia entre las dos palabras se la come el botón de la
 * izquierda: la columna de «Editar» baila ocho píxeles de fila en fila, que no
 * se sabe leer pero se ve desprolijo.
 *
 * El ancho es el de la palabra más larga que va ahí —«Suspender», 85 px— con
 * holgura. Si mañana se pone una más larga, el botón crece y el baile vuelve;
 * la prueba de al lado no mide anchos, avisa cuando aparece una variante sin la
 * clase, que es el olvido probable. */
.btn--pill { border-radius: var(--radius-pill); }

/* Un botón dentro de un formulario en línea, para acciones de una fila. */
.inline-form { display: inline; }

/* ── Formularios ─────────────────────────────────────────────────────────── */
.form { max-width: 560px; }
/* Una columna de 560 px es lo correcto para un formulario suelto y centrado.
 * Dentro de una tarjeta ancha —la del simulador, donde debajo van tablas que
 * sí necesitan el ancho— deja un blanco enorme a la derecha, y ahí el
 * formulario tiene que llenar lo suyo. */
.form--wide { max-width: none; }
/* Y en una hoja de formulario tampoco: la hoja ya fija el ancho, y el tope de
 * 560 encima dejaba dos columnas de 270 px dentro de una tarjeta de 780. */
.sheet--form .form { max-width: none; }
/* Y en la hoja que no se topa, por lo mismo: la toma de pedidos es una
 * pantalla envuelta en un `<form>` —el catálogo y el carrito viajan con él— y
 * no una columna de campos. Con el ancho de lectura, el catálogo quedaba del
 * tamaño del carrito. */
.sheet--wide .form { max-width: none; }
.form p, .field { margin: 0 0 16px; }
.form label, .field > label, .field > legend {
  display: block; margin-bottom: 6px;
  font-size: 14px; font-weight: 500; color: var(--foreground-soft);
}
.form input[type=text], .form input[type=email], .form input[type=password],
.form input[type=number], .form input[type=date], .form select, .form textarea,
.input, .field input, .field select, .field textarea {
  width: 100%; height: 40px; padding: 0 12px;
  border: 1px solid var(--border-input); border-radius: var(--radius-md);
  background: var(--card); color: var(--foreground);
  font-family: inherit; font-size: 14px;
  outline: none;
}
.form textarea, .field textarea { height: auto; min-height: 88px; padding: 10px 12px; }

/* El `<select>` original que Tom Select esconde para dibujar el suyo encima.
 *
 * La regla la trae `tom-select.min.css`, y se repite acá porque el día que esa
 * hoja no llegue —o llegue de un despliegue viejo— el original queda a la
 * vista **encima** del que sí se puede usar, y en pantalla aparecen dos
 * controles idénticos. Es lo que se vio, y desde afuera se lee como que la
 * aplicación se rompió.
 *
 * Va después de la regla que le da alto y ancho a los campos, que es la que
 * tiene que ganarle. */
.field select.tomselected, .field select.ts-hidden-accessible,
select.tomselected, select.ts-hidden-accessible {
  position: absolute; width: 1px; height: 1px; padding: 0;
  border: 0; overflow: hidden; white-space: nowrap;
  clip-path: inset(50%);
}
.form input:focus, .form select:focus, .form textarea:focus,
.input:focus, .field input:focus, .field select:focus, .field textarea:focus {
  border-color: var(--primary); box-shadow: 0 0 0 3px var(--ring);
}

/* La cantidad aceptada de una línea que no se recibe por vida útil
 * insuficiente (`reception_shelf_life.js`, punto 2 de INVENTARIOS): en cero
 * y sin poder tocarse mientras el vencimiento siga incumpliendo. */
.field input.input--readonly {
  background: var(--muted); color: var(--muted-foreground);
}

/* Un campo que se muestra pero no se escribe: la actividad económica, que la
 * contesta Hacienda, o el código de un vendedor, con el que ya quedaron
 * marcadas las facturas. Va apagado —el mismo gris que usa la lista de
 * selección apagada— porque un campo que parece escribible se intenta
 * escribir, y descubrir que no se puede después de hacer clic es peor que
 * verlo de entrada. El texto se queda oscuro: el valor es un dato que se lee,
 * no un rótulo de relleno. */
.field input:disabled, .field select:disabled, .field textarea:disabled {
  background: var(--muted);
  border-color: var(--muted);
  color: var(--foreground);
  cursor: default;
}
/* **La casilla, como la del prototipo.**
 *
 * Allá mide 16 px, tiene la esquina de 4 y se llena con el azul de la marca
 * al encenderse —`size-4 rounded border-gray-300`, y el componente marcado
 * pinta `bg-primary` con el gancho blanco—. Acá quedaba la del navegador:
 * 13 px, cuadrada y gris, el único control de un formulario que se ve de
 * otra aplicación. Lo preguntó Carlos el 11-09-2026.
 *
 * El gancho va como imagen de fondo y no como pseudoelemento: un `input` no
 * tiene contenido, así que `::before` no se dibuja en todos los navegadores.
 */
.form input[type=checkbox], .field input[type=checkbox] {
  appearance: none; -webkit-appearance: none;
  /* El relleno va a cero **y hace falta**: con `box-sizing: border-box` el
   * ancho no puede bajar de relleno más borde, así que los 12 px que traen
   * los demás controles dejaban la casilla de 26 px en vez de 16. */
  width: 16px; height: 16px; padding: 0; margin: 0; flex: none;
  border: 1px solid var(--border-input); border-radius: 4px;
  background: var(--card);
  cursor: pointer;
}
.form input[type=checkbox]:checked, .field input[type=checkbox]:checked {
  border-color: var(--primary);
  background: var(--primary)
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E")
    center / 11px 11px no-repeat;
}
.form input[type=checkbox]:focus-visible, .field input[type=checkbox]:focus-visible {
  outline: none; box-shadow: 0 0 0 3px var(--ring);
}
.form input[type=checkbox]:disabled, .field input[type=checkbox]:disabled {
  background-color: var(--muted); cursor: not-allowed;
}
.form input[type=radio], .field input[type=radio] { width: auto; height: auto; margin: 0; }

/* Casillas múltiples. Django las dibuja con <div>, no con listas, y cuando las
 * opciones vienen agrupadas —los permisos van por módulo— el nombre del grupo
 * es un <label> sin `for`. Esa es la única marca que las distingue.
 *
 * **Cada grupo es una tarjeta, no un tramo de una columna.**
 *
 * Con `columns: 2` el navegador repartía los sesenta permisos como un texto:
 * los grupos caían donde tocara, la altura de cada columna la decidía el corte
 * y el resultado era un muro de casillas donde no se distinguía dónde empezaba
 * «Compras» y dónde terminaba «Ventas». Con una rejilla de tarjetas, cada
 * módulo es una cosa con su borde y su título, y se pueden contar de un
 * vistazo. Lo pidió Carlos el 09-09-2026: «esos checkboxes los veo demasiado
 * retrógrados».
 *
 * `auto-fill` y no un número de columnas: en el modal caben dos, en la página
 * caben tres, y ninguna plantilla tiene que saberlo. */
.field--checkboxselectmultiple > div {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  margin-top: 6px;
}
.permgroup, .field--checkboxselectmultiple > div > div {
  padding: 12px 14px;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--card);
}
.field--checkboxselectmultiple > div label[for] {
  display: flex; align-items: center; gap: 9px;
  margin: 0; padding: 5px 0;
  font-size: 13px; font-weight: 400; color: var(--foreground); cursor: pointer;
}
.field--checkboxselectmultiple > div label[for]:hover { color: var(--primary); }
/* El nombre del módulo, arriba de su tarjeta y separado por una línea: es lo
 * que convierte una lista de casillas en «los permisos de Compras». */
.field--checkboxselectmultiple label:not([for]) {
  display: block; margin: 0 0 8px; padding-bottom: 8px;
  border-bottom: 1px solid var(--border);
  font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted-foreground);
}
/* La casilla misma. `accent-color` la pinta con el azul del sitio en vez del
 * gris del sistema operativo, que es lo que la hacía ver de otra época. */
/* Las de una lista múltiple se visten con la regla general de la casilla;
 * acá solo se las deja de no encogerse dentro de su renglón. */
.field--checkboxselectmultiple input[type=checkbox] { flex: none; }

/* La cabecera de un grupo de permisos: la casilla que enciende el módulo
 * entero, su nombre y cuántos van marcados. La arma `permisos.js`; sin él el
 * título se dibuja solo, como antes. */
.permgroup__head {
  display: flex; align-items: center; gap: 9px;
  margin-bottom: 8px; padding-bottom: 8px;
  border-bottom: 1px solid var(--border);
}
/* El título ya no lleva su propio borde: lo pone la cabecera. */
.permgroup__head label:not([for]) {
  margin: 0; padding: 0; border: 0; flex: 1 1 auto; cursor: pointer;
}
.permgroup__count {
  flex: none;
  font-size: 11px; font-variant-numeric: tabular-nums;
  color: var(--faint);
}

/* Los acuerdos comerciales.
 *
 * Son casillas múltiples como las de arriba, pero con su propio nombre de
 * widget —y por lo tanto su propia clase— porque cada una lleva debajo qué
 * significa marcarla: «No aplicar el 15 %» no se entiende sin «su precio de
 * lista se declara sin el aumento pactado», que es la frase que venía con el
 * campo cuando era una columna de la ficha.
 *
 * Dos columnas, como los permisos: siete acuerdos en una sola dejan la sección
 * más larga que todo lo demás. Y la casilla se alinea con el primer renglón y
 * no con el centro de los dos, que es donde el ojo la busca. */
.field--agreementpicker > div { columns: 2; column-gap: 32px; }
.field--agreementpicker > div > div { break-inside: avoid; }
.field--agreementpicker label.acuerdo {
  display: flex; align-items: flex-start; gap: 8px;
  margin: 0; padding: 7px 0;
  font-size: 13px; font-weight: 400; color: var(--foreground); cursor: pointer;
}
.acuerdo input[type=checkbox] { margin-top: 2px; flex: none; }
.acuerdo__texto { display: flex; flex-direction: column; gap: 2px; }
.acuerdo__hint { font-size: 11.5px; line-height: 1.45; color: var(--muted-foreground); }

/* Los días de la semana, como fichas que se encienden.
 *
 * Por dentro son las mismas casillas que arriba —se recorren con el tabulador,
 * se marcan con la barra espaciadora, un lector de pantalla las anuncia como
 * casillas— y lo único que cambia es la piel. La casilla se esconde a la vista
 * pero **no se quita del flujo**: `display:none` o `visibility:hidden` la
 * sacarían también del teclado, y entonces la semana solo se podría tocar con
 * el ratón.
 *
 * El foco se dibuja sobre la ficha, que es lo que se ve, y no sobre la casilla
 * escondida: sin eso, quien navega con el tabulador no sabe dónde está. */
.field--weekdaypicker > div, .field--weekpicker > div { display: flex; flex-wrap: wrap; gap: 8px; }
.field--weekdaypicker > div > div, .field--weekpicker > div > div { margin: 0; }
.field--weekdaypicker label[for], .field--weekpicker label[for] {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 54px; height: 36px; padding: 0 12px; margin: 0;
  border: 1px solid var(--border-input); border-radius: var(--radius-pill);
  background: var(--card); color: var(--muted-foreground);
  font-size: 13px; font-weight: 600; cursor: pointer;
  user-select: none; transition: background .12s, color .12s, border-color .12s;
}
.field--weekdaypicker label[for]:hover,
.field--weekpicker label[for]:hover { background: var(--muted); }
.field--weekdaypicker input[type=checkbox], .field--weekpicker input[type=checkbox] {
  position: absolute; opacity: 0; pointer-events: none;
  width: 1px; height: 1px; margin: 0;
}
.field--weekdaypicker label[for]:has(input:checked),
.field--weekpicker label[for]:has(input:checked) {
  background: var(--primary); border-color: var(--primary);
  color: var(--primary-foreground);
}
.field--weekdaypicker label[for]:has(input:focus-visible),
.field--weekpicker label[for]:has(input:focus-visible) {
  outline: none; box-shadow: 0 0 0 3px var(--ring);
}

/* Las opciones como botones, una sola encendida: la forma de pago del cobro
 * (Carlos, punto 26 del COMERCIAL.docx, «en botones como el figma»).
 *
 * **Copiadas del prototipo**, donde la fila de formas de pago son píldoras
 * rellenas: la escogida azul con texto blanco y las demás grises **sin
 * borde**, un poco más bajas y con la letra más liviana que las fichas de los
 * días. Los tonos son los mismos tokens que ya usa el sitio, que son los del
 * prototipo: `--muted` es su gris y `--primary` su azul.
 *
 * Por dentro son radios, no botones: se recorren con las flechas, un lector de
 * pantalla los anuncia como lo que son, y sin la hoja de estilos quedan los
 * radios y funcionan. */
.field--pillpicker > div { display: flex; flex-wrap: wrap; gap: 8px; }
.field--pillpicker > div > div { margin: 0; }
.field--pillpicker label[for] {
  display: inline-flex; align-items: center; justify-content: center;
  height: 32px; padding: 0 14px; margin: 0;
  border: 0; border-radius: var(--radius-pill);
  background: var(--muted); color: var(--muted-foreground);
  font-size: 12.5px; font-weight: 500; cursor: pointer;
  user-select: none; transition: background .12s, color .12s;
}
.field--pillpicker label[for]:hover { background: var(--border); }
.field--pillpicker input[type=radio] {
  position: absolute; opacity: 0; pointer-events: none;
  width: 1px; height: 1px; margin: 0;
}
.field--pillpicker label[for]:has(input:checked) {
  background: var(--primary); color: var(--primary-foreground);
}
.field--pillpicker label[for]:has(input:focus-visible) {
  outline: none; box-shadow: 0 0 0 3px var(--ring);
}

/* Varias casillas independientes, dibujadas como un solo grupo. Ver
 * `core.forms.agrupar_casillas`. Es la misma piel de ficha que los días de la
 * semana de arriba —se ve encendida o apagada de un vistazo— pero acá cada
 * casilla lleva su etiqueta entera y no una abreviatura de tres letras: «Maneja
 * inventario» no se puede acortar sin perder lo que dice. Por eso el grupo
 * ocupa la fila completa (`field--ancho`) y no media columna como los días. */
.pillgroup { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 4px; }
.pillgroup .pill {
  /* Posicionado porque la casilla de adentro va `absolute`: sin esto se
   * colocaría contra el ancestro posicionado que hubiera más arriba, que en un
   * modal es el diálogo entero. */
  position: relative;
  display: inline-flex; align-items: center; gap: 8px;
  height: 36px; padding: 0 14px; margin: 0;
  border: 1px solid var(--border-input); border-radius: var(--radius-pill);
  background: var(--card); color: var(--muted-foreground);
  font-size: 13px; font-weight: 600; cursor: pointer;
  user-select: none; transition: background .12s, color .12s, border-color .12s;
}
.pillgroup .pill:hover { background: var(--muted); }
.pillgroup .pill input[type=checkbox] {
  position: absolute; opacity: 0; pointer-events: none;
  width: 1px; height: 1px; margin: 0;
}
.pillgroup .pill:has(input:checked) {
  background: var(--primary); border-color: var(--primary);
  color: var(--primary-foreground);
}
.pillgroup .pill:has(input:focus-visible) {
  outline: none; box-shadow: 0 0 0 3px var(--ring);
}

.field__check { display: flex !important; align-items: center; gap: 8px; cursor: pointer; }
.field__check span { font-size: 13.5px; color: var(--foreground); }
.form .helptext, .field__hint { display: block; margin-top: 6px; font-size: 11.5px; color: var(--muted-foreground); }
/* El asterisco de los obligatorios. Pegado al rótulo y en rojo, que es como se
 * lee sin explicación. No lo anuncia un lector de pantalla —lleva
 * `aria-hidden`— porque el campo ya viaja con `required` y lo dice solo. */
.field__req { margin-left: 3px; color: var(--danger); font-weight: 700; }

/* El campo que falló, marcado en el borde además del mensaje. En un formulario
 * de treinta campos el mensaje puede quedar fuera de la pantalla; el borde
 * dice cuál es de un vistazo, y el rótulo en rojo lo dice otra vez para quien
 * no distinga bien los colores del borde. */
.field--error > label, .field--error > legend { color: var(--danger); }
/* El selector repite la clase del campo a propósito. La regla que pinta los
 * bordes normales nombra el tipo del control —`.form input[type=text]`— y eso
 * pesa más que `.field--error input`: sin repetirla, el borde rojo no se veía
 * y el mensaje quedaba solo. */
.field.field--error input, .field.field--error select,
.field.field--error textarea,
.field.field--error .ts-wrapper.single .ts-control {
  border-color: var(--danger);
}
.field.field--error input:focus, .field.field--error select:focus,
.field.field--error textarea:focus {
  border-color: var(--danger); box-shadow: 0 0 0 3px var(--danger-soft);
}

/* El aviso de un campo que falló.
 *
 * Suelto es solo texto rojo. Con recuadro propio —borde, fondo y esquinas
 * redondas, que es como estaba— a un metro de distancia se lee como **otro
 * campo, vacío**, y quien lo mira busca qué escribir ahí en vez de leer qué le
 * falta arriba. */
.form ul.errorlist, .errorlist {
  margin: 6px 0 0; padding: 0; list-style: none;
  color: var(--danger); font-size: 12.5px;
}

/* Y cuando el campo tiene caja, el aviso **va dentro de ella**: el control
 * pierde el borde y las esquinas de abajo, el aviso pierde los de arriba, y se
 * suben un píxel para que los dos bordes se junten. Queda una sola caja roja,
 * con lo que se escribe arriba y qué le falta abajo.
 *
 * Se reconoce por lo que el campo **tiene** —un control con caja— y no por una
 * lista de tipos: la lista se queda vieja al primer control nuevo, y un grupo
 * de casillas no tiene caja a la que pegarse. */
.field--error:has(> input:not([type="checkbox"])) .errorlist,
.field--error:has(> select) .errorlist,
.field--error:has(> textarea) .errorlist,
.field--error:has(> .ts-wrapper) .errorlist {
  margin: -1px 0 0; padding: 8px 12px;
  border: 1px solid var(--danger); border-top: 0;
  border-radius: 0 0 var(--radius-sm) var(--radius-sm);
  background: var(--danger-soft);
}
/* Las esquinas se cuadran **solo si hay un aviso al que pegarse**. Un campo
 * obligatorio y vacío se marca en rojo y no lleva mensaje: ahí la caja tiene
 * que quedar redonda como todas las demás, o se ve cortada por abajo contra
 * nada.
 *
 * El selector del desplegable nombra también el envoltorio: la regla que le da
 * su borde y sus esquinas —`.ts-wrapper.single .ts-control`— vive más abajo en
 * la hoja y con el mismo peso ganaba por orden. */
.field.field--error:has(> .errorlist) input,
.field.field--error:has(> .errorlist) select,
.field.field--error:has(> .errorlist) textarea,
.field.field--error:has(> .errorlist) .ts-wrapper.single .ts-control {
  border-bottom-left-radius: 0; border-bottom-right-radius: 0;
}
/* A la derecha, y Cancelar antes que la acción: la que confirma queda pegada al
 * borde, que es donde la mano la busca. */
.form__actions { display: flex; align-items: center; justify-content: flex-end; gap: 10px; margin-top: 20px; }

/* ── Barra de filtros ────────────────────────────────────────────────────── */
.filters {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  margin-bottom: 20px; padding: 12px;
  border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  background: var(--card);
}
.filters__label { font-size: 12px; font-weight: 600; color: var(--muted-foreground); margin-right: 4px; }
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  height: 32px; padding: 0 14px;
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  background: var(--card); color: var(--muted-foreground);
  font-size: 12.5px; font-weight: 500; text-decoration: none;
}
.chip:hover { color: var(--foreground); background: var(--muted); text-decoration: none; }
.chip--on { background: var(--primary); border-color: var(--primary); color: #fff; }
.chip--on:hover { background: var(--primary); color: #fff; }

/* Los campos de la barra son más bajos que los de un formulario y no ocupan el
 * ancho completo: acá conviven en una fila y cada uno pide lo suyo. */
.filters .input { width: auto; height: 32px; border-radius: var(--radius-pill); font-size: 12.5px; }
.filters .input--date { min-width: 140px; }
.filters .input--choice { min-width: 150px; max-width: 220px; padding-right: 8px; }
.filters .input--search { min-width: 200px; flex: 1 1 200px; }
.filters .btn { margin-left: auto; }
.filters .btn + .btn { margin-left: 0; }

/* ── Paginación ──────────────────────────────────────────────────────────── */
.pager {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 12px;
  padding: 12px 20px; border-top: 1px solid var(--border);
  font-size: 12.5px; color: var(--muted-foreground);
}
.pager__count { font-variant-numeric: tabular-nums; }
.pager__links { display: flex; align-items: center; gap: 10px; }
.pager__page { font-variant-numeric: tabular-nums; }
/* El botón del extremo se deja a la vista, apagado: quitarlo mueve el resto y
 * la mano pierde el sitio donde estaba haciendo clic. */
.pager [aria-disabled="true"] { opacity: .45; pointer-events: none; }

/* ── Tablas ──────────────────────────────────────────────────────────────── */
.table-wrap { overflow-x: auto; border-radius: inherit; }
/* **Mide lo que necesita y se desplaza; no se estruja.** Con `width: 100%` el
 * navegador repartía el ancho de la ventana entre todas las columnas y achicaba
 * cada una hasta su palabra más larga: con catorce columnas, «JORGE ZUÑIGA
 * ESQUIVEL» salía en tres renglones. Lo vio Carlos el 16-09-2026 en la lista de
 * pedidos, el día que se separaron las celdas.
 *
 * `min-width: 100%` la mantiene llenando su tarjeta cuando sobra sitio, que es
 * lo de siempre; `width: max-content` la deja crecer cuando falta, y entonces
 * se desplaza dentro de `.table-wrap`. */
.table {
  width: max-content; min-width: 100%;
  border-collapse: collapse; font-size: 14px;
}
/* **Sin versalitas.** El prototipo escribe la cabecera como se lee —«Código»,
 * «Nombre»— y SigoOne la ponía en mayúsculas con espaciado. Con nombres de
 * columna largos, las versalitas ocupan más y se leen peor: «DÍAS SIN COMPRA»
 * contra «Días sin compra». Ajustado contra el prototipo el 10-09-2026. */
.table thead th {
  padding: 12px 20px; text-align: left;
  background: #f9fafb; border-bottom: 1px solid var(--border);
  font-size: 12px; font-weight: 500;
  color: var(--muted-foreground); white-space: nowrap;
}
/* La celda, con la medida del prototipo: 12 px de texto y más aire a los
 * lados. La tabla de allá es más densa en tipografía y más suelta en
 * espaciado que la que tenía SigoOne —14 px apretados—, y con quince columnas
 * eso es lo que decide si la fila cabe sin desplazamiento lateral. */
.table tbody td {
  padding: 14px 20px; border-bottom: 1px solid var(--border-soft);
  font-size: 12px; color: #374151; vertical-align: top;
  white-space: nowrap;
}
/* Lo que sí envuelve: el cartel de la tabla vacía, que es una frase, y la celda
 * que alguien marque a mano porque de verdad trae texto largo. */
.table tbody td.table__empty,
.table tbody td.wrap { white-space: normal; }
.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr:hover { background: #f9fafb; }

/* ── La fila que abre el detalle ─────────────────────────────────────────────
 * Hasta ahora había que apuntarle al nombre: en una fila de seis columnas, el
 * único sitio que respondía eran dos palabras. Ahora la fila entera lleva, y
 * se nota antes de hacer clic —el cursor, el fondo y la flecha de la derecha—
 * en vez de haber que descubrirlo probando. */
.table tbody tr.table__row--link { cursor: pointer; }
.table tbody tr.table__row--link:hover { background: var(--muted); }
.table tbody tr.table__row--link:hover .person__name,
.table tbody tr.table__row--link:hover .stop__name { color: var(--primary); }
.table small { display: inline-block; color: var(--muted-foreground); font-size: 12px; }
/* Lo que va a la derecha en este sistema es un número o una fecha, y ninguno
 * de los dos se puede partir: una fecha cortada en «11-08-» «2026» ocupa dos
 * renglones y deja de leerse como una fecha. Lo vio Carlos el 16-09-2026 en la
 * lista de pedidos, el día que pasó a catorce columnas. */
.table .num, .table .right { text-align: right; white-space: nowrap; }
/* La misma regla, para una celda que no va a la derecha y tampoco se parte. */
.table .nowrap { white-space: nowrap; }
.table th.right { text-align: right; }
/* La columna de Stock que la V4 centra en vez de mandarla a la derecha
 * —Disponible, Comprometido, En tránsito, Proyectado (V4:2543-2546,
 * auditoría 25-09-2026)—. Nueva y no una variante de `.num`, que decenas de
 * tablas usan para ir a la derecha. */
.table th.center { text-align: center; }
/* Y la celda de abajo, para lo que la centra sin ser Stock —Items y Uds. de
 * Alisto, auditoría de Alisto, 27-09-2026—. `th.center` solo pintaba el
 * encabezado; el cuerpo se quedaba en lo que trajera la plantilla. */
.table td.center { text-align: center; font-variant-numeric: tabular-nums; }
/* Los botones de una fila, alineados a la derecha.
 *
 * **Va en la celda del cuerpo, nunca en la del encabezado.** `display: flex`
 * saca a la celda de la tabla —el navegador le fabrica una celda anónima
 * alrededor— y en un `<th>` vacío eso deja la fila del encabezado más corta
 * que las de abajo: se ve un escalón blanco en la esquina derecha. Estaba en
 * tres pantallas y lo reportó Carlos el 11-09-2026. Un encabezado sin texto
 * es `<th></th>` y nada más. */
.table__actions { display: flex; align-items: center; gap: 6px; justify-content: flex-end; }
/* Un rótulo con sus opciones debajo, envolviendo. No es `table__actions`:
 * aquello alinea a la derecha porque vive en el pie de una tabla. */
.picker__options { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.table__empty { padding: 40px 20px; text-align: center; color: var(--faint); }

/* La columna de marcar de la cartera (punto 29 del COMERCIAL.docx): se marcan
 * las facturas que se están cobrando y se aplican juntas.
 *
 * La casilla va envuelta en su etiqueta y la etiqueta llena la celda: así se
 * marca tocando la celda y no los trece píxeles de la casilla, que en una
 * tabla de cuarenta píxeles de alto es la diferencia entre marcar y fallar.
 * La celda vacía —una cuenta que ya no admite cobro— no ofrece nada. */
.table .check { width: 36px; padding-right: 0; }
.table .check__box {
  display: flex; align-items: center; justify-content: center;
  min-height: 24px; cursor: pointer;
}

.code {
  font-family: var(--font-mono); font-size: 11.5px;
  background: var(--muted); border-radius: 5px; padding: 1px 5px;
  white-space: nowrap;
}
/* El mismo cuadrito, pero cuando se puede escribir: la ubicación de la bodega
 * se cambia desde la fila donde se está mirando. Se colorea al pasar por
 * encima y no siempre, porque en una lista de cincuenta filas cincuenta
 * enlaces azules se leen como un error. */
a.code--link { color: inherit; text-decoration: none; }
a.code--link:hover { background: var(--border-input); color: var(--foreground); }

/* ── Avisos ──────────────────────────────────────────────────────────────── */
.notice {
  display: flex; align-items: flex-start; gap: 10px;
  margin-bottom: 16px; padding: 12px 16px;
  border: 1px solid var(--border); border-left-width: 3px; border-radius: 10px;
  background: var(--card); font-size: 12.5px;
}
.notice .icon { font-size: 16px; margin-top: 1px; }
.notice--warning { border-left-color: var(--warning); background: var(--warning-soft); color: var(--warning-text); }
.notice--danger  { border-left-color: var(--danger);  background: var(--danger-soft);  color: var(--danger-text); }
.notice--success { border-left-color: var(--success); background: var(--success-soft); color: var(--success-text); }
.notice--info    { border-left-color: var(--primary); background: var(--primary-soft); color: var(--primary-text); }
/* Igual que el tramo del riel y el campo del formulario: sin escribirlo, el
 * `display: flex` de arriba le gana al `[hidden]` del navegador y un aviso
 * apagado se sigue viendo. Pasó con el de «moviendo esta parada», que se
 * quedaba encendido después de soltarla: el atributo cambiaba y la pantalla
 * seguía diciendo lo mismo. */
.notice[hidden] { display: none !important; }

/* Mensajes de Django, abajo a la derecha, como los avisos del prototipo. */
/* ── Los avisos ──────────────────────────────────────────────────────────────
 *
 * **Arriba y al centro, no en una esquina de abajo** (Carlos, 12-09-2026). Ahí
 * abajo competían con el pie de la pantalla y con los botones de guardar, y un
 * error se leía como una nota al margen. Arriba caen donde ya está la mirada
 * después de apretar algo.
 *
 * Cada clase trae su tono y su icono. Antes el icono era siempre una palomita:
 * un error salía con la marca de que todo había ido bien y solo el color lo
 * desmentía. */
.toasts {
  position: fixed; z-index: 60;
  top: calc(var(--topbar) + 16px); left: 50%; transform: translateX(-50%);
  display: flex; flex-direction: column; gap: 10px;
  width: min(560px, calc(100vw - 32px));
  /* El contenedor está siempre puesto, vacío o no: sin esto se comería los
   * clics de media pantalla. Cada aviso sí los recibe. */
  pointer-events: none;
}
.toast {
  pointer-events: auto;
  display: flex; align-items: flex-start; gap: 12px;
  padding: 14px 14px 14px 16px;
  border: 1px solid var(--toast-borde, var(--border));
  border-left: 4px solid var(--toast-tono, var(--success));
  border-radius: var(--radius);
  background: var(--toast-fondo, var(--card));
  box-shadow: 0 18px 40px -16px rgba(11, 17, 32, .45);
  font-size: 14px; font-weight: 500; line-height: 1.4;
  color: var(--toast-texto, var(--foreground));
  animation: rise .22s ease;
}
.toast__texto { flex: 1; min-width: 0; }
.toast .icon { flex: none; width: 18px; height: 18px; color: var(--toast-tono, var(--success)); }
.toast__x {
  flex: none; padding: 0; border: 0; background: none; cursor: pointer;
  color: currentColor; opacity: .5; line-height: 0;
}
.toast__x:hover { opacity: 1; }
.toast__x .icon { width: 14px; height: 14px; color: currentColor; }
.toast--yendose { opacity: 0; transform: translateY(-6px); transition: opacity .2s ease, transform .2s ease; }

.toast--success {
  --toast-tono: var(--success); --toast-fondo: #f0fdf4;
  --toast-borde: #bbf7d0; --toast-texto: var(--success-text);
}
.toast--error {
  --toast-tono: var(--danger); --toast-fondo: #fef2f2;
  --toast-borde: #fecaca; --toast-texto: var(--danger-text);
}
.toast--warning {
  --toast-tono: var(--warning); --toast-fondo: #fffbeb;
  --toast-borde: #fde68a; --toast-texto: var(--warning-text);
}
.toast--info, .toast--debug {
  --toast-tono: var(--primary); --toast-fondo: var(--primary-soft);
  --toast-borde: #bfdbfe; --toast-texto: var(--primary-text);
}

@keyframes rise {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ── Modal ───────────────────────────────────────────────────────────────────
 * Es un `<dialog>` del navegador y no un div con posición fija: trae el velo,
 * la tecla Escape, el foco atrapado y quedar por encima de todo sin que haya
 * que escribir nada de eso.
 */
.modal {
  width: calc(100% - 32px); max-width: 520px;
  padding: 0; border: 1px solid var(--border); border-radius: var(--radius-lg);
  background: var(--card); color: var(--foreground);
  box-shadow: 0 25px 50px -12px rgba(11, 17, 32, .45);
}
.modal::backdrop { background: rgba(0, 0, 0, .4); backdrop-filter: blur(4px); }
.modal[open] { animation: rise .18s ease; }

/* El de permisos lleva cuarenta casillas en dos columnas y no cabe en 520. */
.modal:has(.field--checkboxselectmultiple) { max-width: 760px; }
/* Y el de agregar líneas lleva el catálogo entero: búsqueda, categorías,
 * tarjetas y carrito. En 520 px las tarjetas quedaban de una por fila. */
.modal:has(#catalogo) { max-width: 900px; }
/* Los grupos de adentro no se parten en dos columnas —en 520 px quedarían
 * campos de 230, donde una fecha o un nombre largo no se leen—, pero eso se
 * declara junto a las dos columnas y no acá, con el resto de los sitios
 * angostos. */

.modal__head {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 16px 20px; border-bottom: 1px solid var(--border);
}
.modal__head h3 { font-family: var(--font-display); font-size: 16px; font-weight: 700; }
.modal__x {
  display: grid; place-items: center;
  width: 32px; height: 32px; padding: 0;
  border: 0; border-radius: 8px; background: none;
  color: var(--muted-foreground); font-size: 16px; cursor: pointer;
}
.modal__x:hover { background: var(--muted); color: var(--foreground); }

/* **El alto lo manda el diálogo, no el cuerpo.**
 *
 * Antes el tope estaba en `.modal__body` con `70vh`, y `vh` en un navegador de
 * teléfono se mide contra el viewport *grande* —el que se ve con la barra de
 * direcciones escondida—, no contra el que se está viendo. Con la barra a la
 * vista el modal quedaba más alto que la pantalla: el pie caía fuera y, al
 * desplazar, aparecía un vacío debajo de los botones. Lo reportó Carlos el
 * 09-09-2026.
 *
 * Con `dvh` el tope sigue al viewport de verdad, y el reparto en columna deja
 * la cabecera y el pie fijos con el cuerpo desplazándose en medio, que es lo
 * que se espera de una ventana: los botones siempre a la vista.
 *
 * `min()` con las dos unidades porque `dvh` no está en todos lados; donde no
 * esté, se queda con `vh` y se comporta como antes. */
.modal { max-height: 90vh; max-height: min(90vh, 90dvh); }
/* **El reparto se nombra por `id`, no por `> div`.**
 *
 * Tom Select cuelga cada desplegable del `<dialog>` —`dropdownParent`, para que
 * no lo recorte el cuerpo que hace scroll ni quede detrás de la capa de
 * arriba—, así que el diálogo tiene varios `div` hijos que no son el contenido.
 * Con `.modal > div` los agarraba a todos: al abrir uno se volvía un ítem del
 * reparto, crecía con `flex: 1 1 auto` y dejaba un vacío enorme debajo de los
 * botones. Solo pasaba **después de tocar un desplegable**, que es lo que lo
 * hacía difícil de ver (Carlos, 09-09-2026). */
/* **El que se desplaza es el cuerpo, nunca el diálogo.**
 *
 * El navegador le pone `overflow: auto` a todo `<dialog>`, así que cuando algo
 * de adentro se pasa de alto **se desplaza el diálogo entero**: el pie sube y
 * debajo queda el fondo vacío de la ventana. Medido en el caso de Carlos, el
 * diálogo tenía 101 px de `scrollTop` y esos eran exactamente los que sobraban
 * abajo (09-09-2026).
 *
 * Con `hidden` el diálogo es un marco y punto; lo que se desplaza es
 * `.modal__body`, que para eso lleva su propio `overflow`. Los desplegables de
 * Tom Select no se recortan porque `flotante` los pone en `fixed` al abrirlos,
 * y lo fijo no lo recorta un ancestro. */
.modal { overflow: hidden; }
.modal, .modal > #modal-body, .modal > #modal-body > form {
  display: flex; flex-direction: column; min-height: 0;
}
.modal > #modal-body, .modal > #modal-body > form { flex: 1 1 auto; }
.modal__head, .modal__foot { flex: 0 0 auto; }
.modal__body { padding: 20px; flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.modal__body .field:last-child { margin-bottom: 0; }
.modal__foot {
  display: flex; align-items: center; justify-content: flex-end; gap: 10px;
  padding: 16px 20px; border-top: 1px solid var(--border);
}

/* Mientras la respuesta viaja. htmx pone la clase solo. */
.htmx-request.btn { opacity: .6; pointer-events: none; }

/* ── Pantallas sin sesión ────────────────────────────────────────────────── */
.gate {
  min-height: 100vh;
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
  background:
    radial-gradient(circle at 15% -10%, #162540 0%, transparent 45%),
    radial-gradient(circle at 100% 0%, #1e3a5f 0%, transparent 40%),
    var(--shell);
}
.gate__card { width: 100%; max-width: 400px; padding: 32px; border-radius: var(--radius-lg); background: var(--card); box-shadow: 0 25px 50px -12px rgba(0, 0, 0, .45); }
.gate__brand { margin-bottom: 24px; }
/* El logotipo entero manda el ancho de la tarjeta menos su margen; con `auto`
 * en el alto no se deforma si alguna vez cambia la proporción del archivo. */
.gate__logo { display: block; width: 100%; height: auto; }
.gate h1 { font-size: 19px; }
.gate p { font-size: 12.5px; color: var(--muted-foreground); }
.gate .form { max-width: none; }
.gate .btn { width: 100%; }
.gate__foot { margin-top: 20px; text-align: center; font-size: 12.5px; }

.muted { color: var(--muted-foreground); }
.small { font-size: 11.5px; }

/* El icono dice que el campo es una fecha. El nativo traía uno y al
 * reemplazarlo el campo quedaba indistinguible de cualquier otro. */
/* El selector lleva la etiqueta adelante a propósito: `.field input` declara un
 * `background` corto, que borra la imagen, y pesa más que una clase sola. */
input.input--date {
  padding-right: 36px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235c6b85' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 2v4'/%3E%3Cpath d='M16 2v4'/%3E%3Crect width='18' height='18' x='3' y='4' rx='2'/%3E%3Cpath d='M3 10h18'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 11px center;
  background-size: 15px 15px;
}

/* Un reloj para las horas sueltas, por lo mismo: sin icono, un campo de hora se
 * ve igual que uno de texto. */
input.input--time {
  padding-right: 36px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235c6b85' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpolyline points='12 6 12 12 16 14'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 11px center;
  background-size: 15px 15px;
}

/* Dentro del modal, el calendario flota anclado a la ventana.
 *
 * Antes se dibujaba en línea, ocupando lugar: empujaba los campos de abajo,
 * estiraba el modal y en un formulario con tres fechas dejaba la mitad fuera de
 * vista. Y dejar que flatpickr lo posicionara tampoco servía: calcula contra el
 * documento y el cuerpo del modal hace su propio scroll.
 *
 * `fixed` mide contra la ventana, que es lo único que no se mueve. El
 * calendario cuelga del `<dialog>` —no del cuerpo, que recorta lo que se sale—
 * y `dates.js` le pone las coordenadas y lo sigue mientras el modal se
 * desplaza. El `z-index` lo deja sobre el pie del modal. */
dialog .flatpickr-calendar {
  position: fixed;
  z-index: 10;
  box-shadow: 0 12px 28px -8px rgba(11, 17, 32, .35);
}

/* Un secreto que hay que copiar entero. Grande, en monoespaciada y con el texto
 * seleccionable de una pasada: se lee una sola vez y de ahí sale a cargarse en
 * un aparato, así que equivocarse en un carácter cuesta una visita. */
.code-block {
  margin: 0; padding: 12px 14px;
  border: 1px solid var(--border); border-radius: 10px;
  background: var(--muted); color: var(--foreground);
  font-family: var(--font-mono); font-size: 13px; line-height: 1.5;
  word-break: break-all; white-space: pre-wrap; user-select: all;
}

/* De dónde vino una marca, debajo del nombre de quien la hizo. El icono va
 * alineado con el texto y encogido: es una pista, no un elemento de la fila. */
.mark-origin { display: flex; align-items: center; gap: 4px; margin-top: 2px; }
.mark-origin .icon { flex: 0 0 auto; font-size: 12px; }

/* La coordenada, debajo de la hora. En monoespaciada para que los números de
 * filas distintas caigan alineados y se puedan comparar de un vistazo. */
.mark-place { font-family: var(--font-mono); font-size: 10.5px; line-height: 1.4;
  color: var(--muted-foreground); }
/* Sola dentro de una ficha, la coordenada es el valor del campo y se lee
 * como los demás; apretada bajo una marca, es una nota al pie. */
.mark-place--alone { font-size: 14px; font-weight: 500; color: var(--foreground); }
/* La línea puede cortar **entre** los dos números, pero nunca dentro de uno:
 * una coordenada partida a media cifra deja de ser algo que se pueda leer o
 * copiar. Sin esto, la columna se ensanchaba tanto que sacaba de la pantalla la
 * última columna de la tabla. */
.mark-place__value { white-space: nowrap; }
.mark-place__when {
  display: inline-block; min-width: 42px;
  font-family: var(--font-sans); text-transform: uppercase;
  font-size: 9px; letter-spacing: .04em;
}

/* ── Menú de acciones de una fila ────────────────────────────────────────────
 * Tres puntos al final de la fila que abren la lista de lo que se puede hacer
 * con ella, como en el prototipo. Antes eran dos o tres botones por fila, que
 * en una tabla de treinta renglones son noventa botones.
 *
 * El panel es un `popover`: el navegador lo pone en la capa de arriba —no lo
 * recorta el desplazamiento horizontal de la tabla— y lo cierra solo con un
 * clic afuera o con Escape. `nav.js` solo le calcula dónde caer, pegado al
 * botón que lo abrió.
 */
.rowmenu { display: flex; justify-content: flex-end; }
.rowmenu__btn {
  display: grid; place-items: center;
  width: 28px; height: 28px; padding: 0;
  border: 0; border-radius: 6px; background: transparent;
  color: var(--faint); font-size: 16px; cursor: pointer;
}
.rowmenu__btn:hover, .rowmenu__btn:focus-visible { background: var(--muted); color: var(--foreground); }
.rowmenu__panel {
  position: fixed; inset: auto; margin: 0;
  min-width: 176px; padding: 4px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--card); color: var(--foreground);
  box-shadow: var(--shadow-lg);
}
.rowmenu__form { display: block; margin: 0; }
.rowmenu__item {
  display: flex; align-items: center; gap: 10px;
  width: 100%; padding: 8px 12px;
  border: 0; border-radius: 6px; background: none;
  font-family: inherit; font-size: 14px; text-align: left;
  color: #374151; cursor: pointer; white-space: nowrap;
}
.rowmenu__item:hover { background: #f9fafb; color: var(--foreground); text-decoration: none; }
.rowmenu__item .icon { font-size: 15px; color: var(--faint); }
.rowmenu__item--danger { color: var(--danger-text); }
.rowmenu__item--danger:hover { background: var(--danger-soft); color: var(--danger-text); }

/* ── Volver ──────────────────────────────────────────────────────────────────
 * El enlace de arriba de una ficha o un formulario, que lleva a la lista de
 * donde se vino. Va como texto y no como botón porque no es una acción sobre
 * el dato: es el camino de regreso. */
.backlink {
  display: inline-flex; align-items: center; gap: 4px;
  margin-bottom: 16px;
  font-size: 14px; color: var(--muted-foreground);
}
.backlink:hover { color: #374151; text-decoration: none; }
.backlink .icon { font-size: 15px; }

/* ── La ficha de una persona ─────────────────────────────────────────────────
 * Copiada del prototipo: el enlace de regreso, el círculo con las iniciales,
 * el nombre con el puesto y el área, el estado a la derecha, y las pestañas.
 * Las secciones del formulario son tarjetas con los campos en dos columnas.
 */
.file-head { display: flex; align-items: center; gap: 16px; margin-bottom: 24px; }
.file-head h1 { font-size: 20px; font-weight: 600; letter-spacing: -.01em; }
.file-head p { margin: 2px 0 0; font-size: 14px; color: var(--muted-foreground); }
.file-head__status {
  margin-left: auto; display: flex; align-items: center; gap: 6px;
  font-size: 14px; color: var(--muted-foreground);
}
.file-head__status--on { color: var(--success-text); }
.avatar {
  display: grid; place-items: center; flex: 0 0 auto;
  width: 32px; height: 32px; border-radius: var(--radius-pill);
  background: #dbeafe; color: #1e40af; font-size: 11px; font-weight: 600;
}
.avatar--lg { width: 56px; height: 56px; font-size: 18px; }
.avatar--empty { background: var(--muted); color: var(--faint); font-size: 22px; }
.dot { display: inline-block; width: 8px; height: 8px; border-radius: var(--radius-pill); background: var(--faint); }
.dot--on { background: #22c55e; }
/* El azul de «En proceso» de Por alistador (V4 `BodegaPage.tsx:1585`,
 * auditoría de Alisto, 27-09-2026): `--on` ya sirve al verde de
 * «Completadas», y a este le faltaba su color. */
.dot--blue { background: var(--primary); }

.form--file { max-width: none; }
.form--file .card + .card { margin-top: 24px; }
.card__title { margin: 0 0 20px; font-size: 14px; font-weight: 600; }
/* El título de un grupo dentro de una tarjeta. Más chico que el de la tarjeta
 * y en versalitas, como los de un formulario de página: no compite con él,
 * solo dice dónde empieza otro asunto. La línea de arriba es lo que separa de
 * verdad; el texto solo la nombra. */
.card__subtitle {
  margin: 4px 0 16px; padding-top: 20px;
  border-top: 1px solid var(--border);
  font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted-foreground);
}
/* Los campos en dos columnas. Vale para las tarjetas de una ficha
 * (`.form-grid`) y para los grupos de un formulario de página
 * (`.form__section`), que antes eran una columna sola por más ancho que
 * hubiera.
 *
 * **Dentro del modal no**: ahí la ventana mide 520 px y dos columnas dejarían
 * campos de 230, donde una fecha o un nombre largo no se leen. Se anula más
 * abajo, junto al resto de lo del modal. */
.form-grid, .form__section { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0 20px; }
@media (min-width: 768px) {
  .form-grid, .form__section { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* **Un grupo que lleva una tabla no se parte.** La rejilla es para pares de
 * campos; una tabla no es un campo y en media columna no cabe. Pasó con la
 * devolución a proveedor: el título quedaba a la izquierda y la tabla en los
 * 400 px de la derecha, donde solo se veía el nombre del producto y la casilla
 * de la cantidad —lo único que hay que llenar— quedaba escondida. */
.form__section:has(> .table-wrap) { display: block; }

/* Donde el formulario es angosto, una sola columna.
 *
 * La consulta de arriba mide **la ventana**, no la caja donde vive el
 * formulario, así que en una pantalla ancha se parte en dos igual: el modal
 * mide 520 px, la tarjeta de acceso 400 y ahí dos columnas dejan campos de
 * 180. El correo y la contraseña quedaban uno al lado del otro, del ancho de
 * un nombre corto.
 *
 * Se enumeran los sitios y no se mide la caja a propósito. Una consulta de
 * contenedor obligaría a poner `container-type` en el formulario, y eso lo
 * convierte en marco de referencia de lo que se posiciona contra la ventana:
 * la lista de un desplegable y el calendario de una fecha cuelgan del campo
 * con `position: fixed` y caerían en el sitio equivocado. Cuesta menos agregar
 * un renglón acá que perseguir un panel descolocado.
 *
 * `.form--simple` es para el formulario suelto que se sabe angosto sin estar
 * en ninguno de los dos —el cambio de contraseña, que son tres campos. */
.modal .form__section, .modal .form-grid,
.gate .form__section, .gate .form-grid,
.form--simple .form__section, .form--simple .form-grid { display: block; }
.form-grid .field--textarea, .form-grid .field--checkboxselectmultiple,
.form__section .field--textarea,
.form__section .field--checkboxselectmultiple { grid-column: 1 / -1; }
/* Las fichas **no** ocupan la fila entera: siete días y cinco semanas caben en
 * media columna, y así quedan una al lado de la otra, que es como se leen. El
 * día y la semana son una sola idea partida en dos. */
/* Y los que se marcan a mano, para un texto largo que en media columna no se
 * lee: la descripción de la actividad económica. */
.form-grid .field--ancho, .form__section .field--ancho { grid-column: 1 / -1; }

/* **Un campo no se queda solo en su fila**, y quién quedó solo lo cuenta el
 * servidor: ver `core.forms.in_two_columns`, que le pone `field--ancho` al que
 * abre una fila y no llega a tener pareja.
 *
 * Estaba acá y por vecindad —el campo de antes de un párrafo, siempre— y la
 * hoja de estilos no tiene cómo saber si ese campo ya tenía pareja. Partía las
 * que estaban armadas: en la ubicación de un cliente, «Cantón» y «Distrito»
 * compartían fila hasta que la dirección de abajo estiró al segundo de lado a
 * lado y dejó al primero solo (Carlos, 09-09-2026). Contar los campos es lo
 * que hace falta, y eso se hace antes de dibujar. */

/* **Un control llena su campo, siempre.**
 *
 * Hubo un tope de 260 px para los de largo conocido —monto, fecha, hora—
 * puesto el 09-09-2026 porque «Aprueba compras hasta» salía de 750 px para
 * escribir seis dígitos. Se acotó después al campo que ocupa la fila entera,
 * y el 11-09-2026 se fue del todo: Carlos lo señaló dos veces —«los campos de
 * fechas están en tamaños diferentes»— y el prototipo no lo tiene, allá la
 * fecha mide lo que mide su columna.
 *
 * Lo que evitaba el tope lo evita ahora `core.forms.in_two_columns`, que solo
 * ensancha al campo que de verdad se quedó solo. Un campo de fila entera con
 * un control corto es raro, y cuando pasa se lee peor que un control largo:
 * la columna deja de tener una sola medida. */

/* Tres columnas donde el formulario es una barra de parámetros y no una ficha:
 * el simulador vive en una hoja al ancho del lienzo, y en dos columnas cada
 * campo medía 845 px para escribir un número de dos cifras. Es la excepción, y
 * por eso es una clase y no la regla. */
@media (min-width: 1200px) {
  .form-grid--tres { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* Una casilla no lleva rótulo encima, así que en dos columnas quedaba a la
 * altura del **rótulo** del campo de al lado y no de su caja: «Exonerado»
 * flotando junto a la palabra «Porcentaje exonerado» en vez de junto al
 * recuadro donde se escribe. El hueco de arriba le devuelve la línea.
 *
 * Se arma con las mismas medidas que el rótulo —el mismo tamaño de letra, el
 * mismo margen— para que siga cuadrando si algún día se cambian: `1lh` es la
 * altura de una línea a ese tamaño, no un número copiado a mano que se
 * quedaría viejo.
 *
 * Va dentro de la consulta de ancho porque **solo hace falta cuando hay dos
 * columnas**: apiladas en el teléfono no hay nada al lado con qué cuadrar, y
 * el hueco sería aire de más entre casilla y casilla. */
@media (min-width: 768px) {
  .form-grid .field--checkbox::before,
  .form__section .field--checkbox::before {
    content: ""; display: block;
    margin-bottom: 6px; font-size: 12.5px; height: 1lh;
  }
  /* Y donde no hay dos columnas tampoco hay con qué cuadrar: el hueco sería
   * un renglón en blanco encima de cada casilla. */
  .modal .field--checkbox::before,
  .gate .field--checkbox::before,
  .form--simple .field--checkbox::before { content: none; }
}
.file-actions { display: flex; align-items: center; justify-content: flex-end; gap: 12px; margin-top: 24px; }

/* ── El mapa de una ficha ────────────────────────────────────────────────────
 * Debajo de los campos de ubicación. El punto lo fijan tres cosas —la
 * búsqueda, «Mi ubicación» y el clic— y los dos campos numéricos siguen siendo
 * el dato: el mapa los lee y los escribe.
 */
/* Leaflet no le pone contexto de apilamiento a lo que dibuja: sus paneles
 * llegan hasta z-index 500-700 y los controles (zoom, atribución) hasta
 * 1000 —ver `static/css/leaflet.css`—. `.mapa__lienzo` no tiene `position`
 * propio, así que esos números no quedan contenidos ahí: suben sin freno
 * hasta el contexto raíz de `<body>`, el mismo donde vive `.header-stack`
 * (las dos barras fijas) con z-index 40, y ahí Leaflet gana. Por eso el
 * mapa tapaba la barra al hacer scroll, no por faltarle número a la barra.
 *
 * `isolation: isolate` sella el widget completo —mapa y su buscador— como
 * una sola capa: adentro siguen ganando los z-index de Leaflet (así el
 * listado de direcciones queda sobre las teselas, que es lo que se quiere),
 * pero ninguno de esos valores vuelve a compararse contra el resto de la
 * página. No hace falta subirle el número a nada de la escala de arriba.
 */
.mapa { margin-top: 16px; isolation: isolate; }
.mapa__barra { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 8px; }
.mapa__buscar { position: relative; flex: 1 1 260px; min-width: 0; }
.mapa__lienzo {
  height: 320px; border-radius: var(--radius); border: 1px solid var(--border);
  /* Leaflet dibuja las teselas sin recorte propio: sin esto asoman por las
   * esquinas redondeadas. */
  overflow: hidden;
  /* Las teselas tardan. Un rectángulo gris se lee como «está cargando»; uno
   * blanco, como que algo falló. */
  background: var(--muted);
}
/* Los hallazgos de la búsqueda, colgados de la caja. Van por encima del mapa,
 * que Leaflet dibuja con su propia capa. */
.mapa__hallazgos {
  position: absolute; z-index: 1000; left: 0; right: 0; top: calc(100% + 4px);
  max-height: 240px; overflow-y: auto;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--card); box-shadow: 0 10px 25px -10px rgba(11, 17, 32, .35);
}
.mapa__hallazgo {
  display: block; width: 100%; padding: 8px 12px;
  border: 0; background: none; text-align: left;
  font-family: inherit; font-size: 12.5px; color: var(--foreground);
  cursor: pointer;
}
.mapa__hallazgo:hover, .mapa__hallazgo:focus-visible { background: var(--muted); }
.mapa__hallazgo--nada { color: var(--muted-foreground); cursor: default; }
/* Leaflet trae su propia tipografía; que hable la del sitio. */
.mapa__lienzo .leaflet-container { font-family: inherit; }

/* Campos que cuelgan de una casilla —la exoneración del cliente, hoy—. Se
 * esconden con el atributo `hidden` del HTML y no con una clase nuestra: es lo
 * que ya entienden el navegador y los lectores de pantalla, y así el campo sale
 * también del recorrido con el tabulador.
 *
 * La regla va escrita igual, con `!important`, porque `.field` se dibuja dentro
 * de una rejilla y basta con que algo le dé un `display` para que el `hidden`
 * del navegador deje de valer. No es teórico: `.field__check` ya lo hace.
 */
.field[hidden] { display: none !important; }

/* Un panel de ficha que no es el que se está viendo. Se queda en la página
 * —cambiar de pestaña no le pide nada al servidor— pero fuera de la vista y
 * fuera del recorrido del tabulador, que es lo que da el `hidden` del HTML.
 * La regla va escrita por si algún día algo le pone un `display` y lo
 * resucita sin querer. */
.panel[hidden] { display: none !important; }

/*
 * **Y el que nadie escondió tampoco se ve.**
 *
 * Esconder era una orden que había que dar, así que el panel que entraba sin
 * que nadie se la diera se veía. Se reportó dos veces: «se duplica la
 * información», y el 13-09-2026 la ficha del vendedor con la pestaña
 * «Resumen» marcada y tres paneles debajo, uno tras otro.
 *
 * Con el contenedor marcado —lo marca `tabs.js` la primera vez que pinta— se
 * ve **solo** el que lleva `data-a-la-vista`. Un panel que entre por cualquier
 * otro camino nace escondido en vez de nacer visible.
 *
 * El aviso de «Cargando…» no lleva `data-panel`, así que esto no lo toca.
 *
 * Con JavaScript apagado el contenedor nunca queda marcado y el panel que
 * dibujó el servidor se ve como siempre: las pestañas son enlaces de verdad y
 * tienen que seguir sirviendo.
 */
#ficha-paneles[data-pintado] > [data-panel]:not([data-a-la-vista]) {
  display: none !important;
}

/* Y cuando aparece, que entre. Cinco campos surgiendo de golpe se leen como si
 * la pantalla se hubiera roto. El movimiento es corto a propósito: acompaña,
 * no se hace esperar. Quien pidió menos movimiento en su sistema no lo ve. */
@keyframes campo-aparece {
  from { opacity: 0; transform: translateY(-4px); }
}
.field.aparece { animation: campo-aparece 160ms ease-out; }


/* Los cuatro cuadros del saldo de vacaciones. */
.tiles { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-bottom: 24px; }
@media (min-width: 768px) { .tiles { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.tile { display: flex; flex-direction: column; gap: 4px; padding: 16px; border-radius: var(--radius); }
.tile__value { font-size: 24px; font-weight: 700; font-variant-numeric: tabular-nums; }
.tile__label { font-size: 12px; }
.tile--blue  { background: #eff6ff; color: #1d4ed8; }
.tile--gray  { background: #f9fafb; color: #374151; }
.tile--amber { background: #fffbeb; color: #b45309; }
.tile--green { background: #f0fdf4; color: #15803d; }

/* Cabecera de tarjeta con icono, título y cuenta de registros. */
.card__title-row { display: flex; align-items: center; gap: 8px; }
.card__title-row .icon { font-size: 16px; color: var(--faint); }
.card__foot { padding: 12px 20px; border-top: 1px solid var(--border); }

/* Las acciones de una fila como iconos sueltos: aprobar en verde, rechazar en
 * rojo, ver en gris. Es lo que el prototipo hace en las pestañas de la ficha. */
.iconact {
  display: inline-grid; place-items: center;
  width: 28px; height: 28px; padding: 0;
  border: 0; border-radius: 6px; background: none;
  color: var(--faint); font-size: 16px; cursor: pointer;
}
.iconact:hover { background: var(--muted); color: var(--foreground); text-decoration: none; }
.iconact--ok:hover { background: var(--success-soft); color: var(--success-text); }
.iconact--no:hover { background: var(--danger-soft); color: var(--danger-text); }

/* Una lista sin nada: el icono, el título y una línea que dice qué hacer. */
.empty { padding: 40px 20px; text-align: center; color: var(--muted-foreground); }
.empty .icon { font-size: 32px; color: var(--faint); margin-bottom: 8px; }
.empty p { margin: 4px 0; font-size: 14px; }
.empty__title { color: var(--foreground); font-weight: 500; }

/* Un concepto de planilla asignado a la persona. */
.concept { padding: 14px 16px; border: 1px solid var(--border); border-radius: var(--radius-sm); }
.concept + .concept { margin-top: 10px; }
.concept__head { display: flex; align-items: flex-start; gap: 12px; }
.concept__icon { display: grid; place-items: center; flex: 0 0 auto; width: 32px; height: 32px; border-radius: 8px; }
.concept__icon--in  { background: var(--success-soft); color: var(--success-text); }
.concept__icon--out { background: var(--danger-soft);  color: var(--danger-text); }
.concept__name { font-size: 14px; font-weight: 500; }
.concept__meta { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 2px; font-size: 12px; color: var(--muted-foreground); }
.concept__grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; margin-top: 12px; }
.concept__grid label { display: block; font-size: 12px; color: var(--muted-foreground); }
.concept__grid strong { font-weight: 500; font-variant-numeric: tabular-nums; }
.concept__head .iconact { margin-left: auto; }

/* La línea de datos sueltos —motivo, fecha, antigüedad— y el total al pie. */
.meta-line { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 16px; font-size: 13px; color: var(--muted-foreground); }
.meta-line b { font-weight: 500; color: var(--foreground); }
.total-box {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  margin-top: 16px; padding: 16px 20px;
  border: 1px solid #dbeafe; border-radius: var(--radius-sm); background: #eff6ff;
}
.total-box strong { font-size: 22px; font-variant-numeric: tabular-nums; }
.total-box small { display: block; color: var(--muted-foreground); }

/* ── La lista de personas, como el prototipo ────────────────────────────────
 * La barra de filtros va suelta, sin tarjeta: el buscador redondo, los
 * desplegables de casillas como píldoras y «Limpiar filtros» como texto. La
 * tabla es la tarjeta, con «Total N personas» arriba.
 */
.filters--bare { margin-bottom: 16px; padding: 0; border: 0; background: none; box-shadow: none; }
.filters--bare .input--search { flex: 0 1 220px; min-width: 200px; }
.search-wrap { position: relative; display: inline-flex; }
.search-wrap > .icon { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--faint); font-size: 15px; pointer-events: none; }
/* El hueco para la lupa es de la envoltura, no de la barra donde esté.
 *
 * Estaba escrito como `.filters--bare .input--search`, así que el buscador del
 * directorio de clientes —misma envoltura, otra barra— no lo heredaba y la
 * lupa caía encima del texto. Cualquier campo dentro de una `.search-wrap`
 * necesita ese hueco; de qué barra cuelgue no tiene nada que ver. */
/* El selector nombra el elemento a propósito. Dentro de un formulario manda
 * `.form input[type=text]`, que pesa más que `.search-wrap > .input` y le
 * devuelve su relleno de 12 px: la lupa vuelve a caer encima del texto. Pasó
 * con el buscador del mapa, que es el primero que vive dentro de un
 * formulario. */
.search-wrap > .input, .search-wrap > input.input { padding-left: 34px; }
/* La barra de filtros y el botón de bajarse la lista, en el mismo renglón.
 *
 * Son dos formularios y no uno: hay listados sin barra de filtros, y un
 * formulario dentro de otro no es HTML válido. El de la descarga lleva los
 * filtros vigentes en campos ocultos, así que se baja lo que se está mirando.
 */
.filters-row { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 16px; }
.filters-row .filters--bare { flex: 1 1 auto; margin-bottom: 0; }
.filters__download { flex: 0 0 auto; margin-left: auto; }
/* Anclado a la derecha: el botón vive al final del renglón, y un panel de 280
 * px abierto hacia la derecha se sale de la pantalla. */
.fdrop__panel--right { left: auto; right: 0; }

/* El encabezado que ordena. */
.th-sort {
  display: inline-flex; align-items: center; gap: 4px;
  color: inherit; text-decoration: none; white-space: nowrap;
}
.th-sort:hover { color: var(--foreground); }
.th-sort__caret { font-size: 13px; opacity: 0; transition: opacity .12s, transform .12s; }
.th-sort:hover .th-sort__caret { opacity: .45; }
/* La flecha de la columna ordenada se queda puesta; las demás solo asoman al
 * pasar por encima, o la fila de encabezados sería una hilera de flechas. */
.th-sort--asc .th-sort__caret,
.th-sort--desc .th-sort__caret { opacity: 1; }
.th-sort--asc .th-sort__caret { transform: rotate(180deg); }
.table th[aria-sort="ascending"], .table th[aria-sort="descending"] { color: var(--foreground); }

.filters__clear { font-size: 12px; color: var(--muted-foreground); text-decoration: underline; }
.filters__clear:hover { color: #374151; }

.chip--set { border-color: var(--foreground); color: var(--foreground); }
.chip__count {
  display: inline-grid; place-items: center; min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: var(--radius-pill); background: var(--foreground); color: #fff;
  font-size: 11px; font-weight: 600;
}
.chip .icon { font-size: 14px; }

.fdrop { position: relative; }
.fdrop > summary { list-style: none; cursor: pointer; }
.fdrop > summary::-webkit-details-marker { display: none; }
.fdrop__panel {
  position: absolute; left: 0; top: 100%; z-index: 60;
  width: 280px; margin-top: 8px;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--card); box-shadow: var(--shadow-lg);
}
.fdrop__head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 14px 8px; font-size: 14px; font-weight: 600;
}
.fdrop__search { position: relative; padding: 0 14px 8px; }
.fdrop__search .icon { position: absolute; left: 24px; top: 50%; transform: translateY(-50%); margin-top: -4px; color: var(--faint); font-size: 14px; pointer-events: none; }
.fdrop__search input {
  width: 100%; height: 34px; padding: 0 10px 0 30px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  font-family: inherit; font-size: 13px; outline: none;
}
.fdrop__search input:focus { border-color: var(--primary); box-shadow: 0 0 0 3px var(--ring); }
.fdrop__options { max-height: 240px; overflow-y: auto; padding: 4px 6px; }
.fdrop__option {
  display: flex; align-items: center; gap: 10px;
  padding: 8px; border-radius: 6px; font-size: 14px; cursor: pointer;
}
.fdrop__option:hover { background: #f9fafb; }
.fdrop__option input { width: 20px; height: 20px; margin: 0; accent-color: var(--foreground); }
/* Un nombre sin espacios se parte donde haga falta. Los tramos de SICOFAIN
 * juntan pueblos con barras —«PAQUERA/COBANO/MONTEZUMA/SANTA TERESA»— y sin
 * esto el renglón se salía del panel y quedaba cortado. */
.fdrop__option > span { min-width: 0; overflow-wrap: anywhere; }
.fdrop__none { padding: 12px 8px; font-size: 13px; color: var(--muted-foreground); text-align: center; }
.fdrop__foot {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 10px 14px; border-top: 1px solid var(--border);
}
.fdrop__foot .btn { border-radius: var(--radius-pill); }

/* En un teléfono el panel se ancla a la barra de filtros y no a su botón.
 *
 * Mide 280 px fijos y se abre pegado al botón; con el botón en mitad del
 * renglón esos 280 px caen fuera de la pantalla y el filtro no se puede usar.
 * Anclado a la barra entera siempre cabe, que es el mismo arreglo que el
 * megamenú usa contra la barra superior.
 *
 * **Va acá y no con el resto de lo del teléfono**: `.fdrop` se declara unas
 * líneas más arriba y en CSS, con la misma especificidad, gana la última. Puesto
 * junto a las demás reglas de móvil no hacía nada, y no se nota leyendo el
 * bloque: hay que mirar el estilo calculado. */
@media (max-width: 639px) {
  .filters { position: relative; }
  .fdrop { position: static; }
  .fdrop__panel { left: 0; right: 0; width: auto; }
  .filters-row { position: relative; flex-wrap: wrap; }
  .filters__download { margin-left: 0; }
}

.list-card__count { padding: 10px 20px; border-bottom: 1px solid var(--border-soft); font-size: 12px; color: var(--muted-foreground); }

/* ── El directorio del prototipo ─────────────────────────────────────────────
 *
 * Cuatro indicadores arriba y, debajo, **una sola tarjeta** que lleva adentro
 * su cabecera —título, buscador, píldoras y el botón—, la tabla y el contador
 * al pie. Es distinto de las listas de Personal, donde los filtros van sueltos
 * arriba y el contador arriba de la tabla: son dos formas del prototipo y cada
 * pantalla usa la suya.
 */
.kpis {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 16px; margin-bottom: 20px;
}
.kpi {
  padding: 16px 18px; border: 1px solid var(--border);
  border-radius: var(--radius); background: var(--card);
}
.kpi__label { margin: 0 0 4px; font-size: 12px; color: var(--muted-foreground); }
/* **La cifra de un indicador va en DM Sans, nunca en la monoespaciada**
 * (Carlos, 28-09-2026: «unifica estas cards, pon el cero sin el palito»). Casi
 * todas las plantillas le ponen `.num` al número, y `.num` lo pasa a DM Mono,
 * que dibuja el cero tachado. La V4 escribe los indicadores en la letra de
 * siempre (`text-2xl font-bold`); las cifras de ancho fijo se conservan con
 * `tabular-nums`. Los códigos —OC-, SKU, ALI-— siguen en monoespaciada. */
.kpi__value {
  margin: 0; font-size: 24px; font-weight: 700; line-height: 1.1;
  font-family: var(--font-sans);
  font-variant-numeric: tabular-nums; color: var(--foreground);
}
/* El indicador que todavía no tiene de dónde salir se dibuja en gris claro: el
 * hueco tiene que verse como un dato que falta, no como un cero. */
.kpi__value--empty { color: var(--border-input); font-weight: 500; }
/* La captura de Alisto (punto 6, Carlos, 26-09-2026) pinta solo el número de
 * dos de los cinco indicadores —«En alisto» en azul, «Listos hoy» en
 * verde—, sin teñir la tarjeta entera como hace `.kpi--azul`/`.kpi--verde`
 * (esas cambian fondo, borde e icono, y esta pantalla no lleva icono). */
.kpi__value--azul  { color: var(--primary); }
.kpi__value--verde { color: var(--success-text); }
/* Incidencias de Rutas de Entregas: el naranja de la captura del punto 10. */
.kpi__value--orange { color: #ca3500; }
/* `.kpi__hint` es este mismo renglón con otro nombre, y estaba **sin regla**:
 * veinte pantallas lo dibujaban a 14 px y en negro —del tamaño de un párrafo—
 * cuando es el pie de un indicador. Se descubrió el 12-09-2026 al ponerle
 * color a los del estado de cuenta. Van juntos en una sola regla para que no
 * se vuelvan a separar. */
.kpi__sub, .kpi__hint { margin: 4px 0 0; font-size: 12px; color: var(--faint); }

/* La bandeja de «Aprobaciones» (módulo de aprobaciones, fase 1,
 * 25-09-2026): una tarjeta por solicitud, con Aprobar antes de Rechazar,
 * como el V4. */
.approval-cards {
  display: grid; gap: 12px; margin-bottom: 20px;
}
.approval-card { padding: 16px 18px; }
.approval-card__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; margin-bottom: 8px;
}
.approval-card__body {
  display: flex; flex-wrap: wrap; gap: 4px 16px; align-items: center;
  color: var(--muted-foreground); font-size: 14px;
}
.approval-card__actions {
  display: flex; gap: 8px; margin-top: 12px;
}
.approval-card__badges { display: inline-flex; gap: 6px; flex-wrap: wrap; }

/* El «Flujo de aprobación» del detalle de una política, calcado de
 * `PoliticaDetailPage.tsx` del V4: una fila gris por nivel con el círculo
 * azul numerado, quién firma, «Desde monto» y «Obligatorio», y la papelera a
 * la derecha (módulo de aprobaciones, 28-09-2026). */
.policy-form > .card { margin-bottom: 16px; }
.policy-flow__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-bottom: 12px;
}
.policy-flow__head .section-title { margin: 0; }
.policy-levels { display: grid; gap: 12px; margin-top: 8px; }
.policy-level {
  display: flex; align-items: center; gap: 16px;
  padding: 14px 16px; border-radius: 10px;
  /* `bg-gray-50/80` y `border-gray-100` del V4. */
  background: rgba(249, 250, 251, .8); border: 1px solid var(--border-soft);
}
.policy-level__num {
  flex: none; width: 32px; height: 32px; border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center;
  /* `bg-blue-100` y `text-blue-700` del V4. */
  background: #dbeafe; color: var(--primary-text);
  font-size: 12px; font-weight: 600;
}
.policy-level__fields {
  flex: 1; min-width: 0; display: grid; gap: 8px 16px;
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 768px) {
  .policy-level__fields { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) auto; align-items: end; }
}
.policy-level__fields .field { margin: 0; }
.policy-level__label { display: block; font-size: 12px; color: var(--muted-foreground); margin-bottom: 4px; }
.policy-level__approver {
  display: flex; align-items: center; gap: 4px; min-height: 36px;
  font-size: 14px; color: var(--foreground);
}
.policy-level__amount { display: flex; align-items: center; gap: 6px; }
.policy-level__amount input { width: 100%; max-width: 180px; text-align: right; }
.policy-level__remove { flex: none; color: var(--danger); }
.policy-levels__hint { margin: 10px 0 0; }

.list-head {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 12px;
  padding: 14px 20px; border-bottom: 1px solid var(--border-soft);
}
.list-head__title { margin: 0; font-size: 15px; font-weight: 600; }
/* Cuando lo que titula la lista es su propio conteo —«Total 9 listas»— el
 * conteo pasa a vivir dentro de la cabecera y deja de ser su propia franja: sin
 * esto quedaban dos rellenos y dos líneas, una debajo de la otra. */
.list-head > .list-card__count { padding: 0; border-bottom: 0; }
/* La cabecera de una lista que no lleva conteo: solo sus acciones, a la
 * derecha. Sin esto la fila se reparte el ancho y el botón queda a la
 * izquierda, donde nadie lo busca. */
.list-head--solo { justify-content: flex-end; }
/* La barra de filtros dentro de la cabecera de la lista: deja de ser una fila
 * propia y sus dos piezas —el buscador con sus desplegables, y la descarga—
 * pasan a ser hijas de la cabecera, que es la que reparte el espacio. Sin
 * esto, la barra se lleva su margen de abajo y su ancho entero, y el botón de
 * agregar caía a otro renglón. */
.list-head__tools .filters-row { display: contents; }
.list-head__tools .filters--bare { margin-bottom: 0; }
.list-head__tools .filters__download { margin-left: 0; }
.list-head__tools { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; }
/* El formulario de la cabecera **no es una caja**: sus piezas —el buscador,
 * las píldoras de canal y los desplegables— se reparten en la fila de
 * herramientas como si fueran hermanas, y bajan de renglón con las demás
 * cuando no caben. Como caja flexible sin salto de renglón iban todas en una
 * sola fila, y con los cuatro filtros de la vuelta esa fila ya no cabe en
 * cualquier pantalla. */
.list-head__search { display: contents; }
/* Redondo y más bajo, como el del prototipo. El de la barra de filtros ya lo
 * era —`.filters .input`— y este se había quedado cuadrado y de 40 px: el
 * mismo control con dos formas según la pantalla. */
.list-head__search .input--search {
  min-width: 208px; height: 36px; border-radius: var(--radius-pill);
}

.pills { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.pill {
  padding: 5px 12px; border-radius: var(--radius-pill);
  background: var(--muted); color: var(--muted-foreground);
  font-size: 12px; font-weight: 500; white-space: nowrap;
}
.pill:hover { background: var(--border); color: var(--foreground); text-decoration: none; }
.pill--on { background: var(--primary); color: #fff; }
.pill--on:hover { background: var(--primary); color: #fff; }

/* Las dos series del vendedor: lo facturado y lo cobrado, mes a mes.
 *
 * Barras y no una línea: son dos magnitudes que se comparan mes contra mes, no
 * una curva que se sigue. Y con la misma escala las dos —el mes más grande de
 * las dos series— porque el punto es ver cuánto entra de lo que sale; escaladas
 * cada una a su máximo, un mes flojo se vería igual de alto que uno bueno.
 */
.serie {
  display: flex; align-items: flex-end; justify-content: flex-start;
  gap: 10px; height: 120px; margin-bottom: 16px;
}
/* Anchura tope: con dos meses de historia, `flex: 1` daba dos barras de media
 * pantalla cada una y el gráfico se leía como otra cosa. Crecen hasta 72 px y
 * de ahí se quedan quietas, alineadas a la izquierda. */
.serie__mes {
  display: flex; flex-direction: column; justify-content: flex-end;
  flex: 0 1 72px; height: 100%;
}
.serie__barras { display: flex; align-items: flex-end; gap: 3px; height: 100%; }
.serie__barra { flex: 1; border-radius: 3px 3px 0 0; min-height: 2px; }
.serie__rotulo { margin-top: 6px; text-align: center; font-size: 11px; color: var(--muted-foreground); }

/* El mix de venta: una sola barra repartida, que es como se lee una
 * participación. Los colores no significan nada por sí solos —no hay una
 * categoría «buena» y otra «mala»— así que se numeran y se repiten si hacen
 * falta más; lo que identifica cada tramo es el punto de su renglón. */
.mix { display: flex; height: 12px; border-radius: var(--radius-pill); overflow: hidden; margin-bottom: 16px; }
.mix__tramo { display: block; height: 100%; }
.mix__punto {
  display: inline-block; width: 9px; height: 9px; border-radius: 50%;
  margin-right: 7px; vertical-align: baseline;
}
.mix__tramo--1, .mix__punto.mix__tramo--1 { background: #2563eb; }
.mix__tramo--2, .mix__punto.mix__tramo--2 { background: #16a34a; }
.mix__tramo--3, .mix__punto.mix__tramo--3 { background: #f59e0b; }
.mix__tramo--4, .mix__punto.mix__tramo--4 { background: #a855f7; }
.mix__tramo--5, .mix__punto.mix__tramo--5 { background: #ec4899; }
.mix__tramo--6, .mix__punto.mix__tramo--6 { background: #0891b2; }
.mix__tramo--7, .mix__punto.mix__tramo--7 { background: #64748b; }

/* La toma de pedidos.
 *
 * La cabecera se acomoda sola: son tres campos —cliente, vendedor y condición
 * de pago— y a veces cuatro, cuando la persona tiene más de una sucursal y hay
 * que elegir. Repartirlos con la rejilla de dos columnas del formulario dejaba
 * el cuarto solo en su fila, con media pantalla vacía al lado.
 *
 * Y el catálogo pesa más que el carrito: en el catálogo se busca y se toca, el
 * carrito se mira. Partir la pantalla por la mitad le daba al que se lee el
 * mismo espacio que al que se usa.
 */
.tomapedido__cabecera {
  display: grid; gap: 12px 16px;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.tomapedido { display: grid; gap: 16px; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 900px) {
  .tomapedido { grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr); }
}

/* Los dos campos que se escriben dentro del carrito de compras.
 *
 * Van angostos y a la derecha porque son números de largo conocido —cuántos y
 * a cuánto— dentro de una tabla estrecha: a lo ancho de la celda, la columna
 * del artículo se queda sin sitio y el nombre se parte en tres renglones. */
.carrito__num { width: 100%; max-width: 110px; text-align: right; }

/* La barra de la vitrina: buscar y acotar por rubro, en una línea.
 *
 * El rubro era una fila de píldoras, una por rubro, y con 51 se comía cuatro
 * renglones y empujaba los artículos fuera de la pantalla (Carlos,
 * 10-09-2026). Un desplegable mide lo mismo con 51 que con 300.
 *
 * La caja de texto pesa el doble que el rubro porque es lo que de verdad se
 * usa: el rubro acota, el nombre encuentra. En el teléfono se apilan, que a
 * ese ancho dos columnas dejan campos donde no se lee lo que se escribe. */
.catalog-search { display: grid; gap: 0 16px; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 700px) {
  .catalog-search { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
}
/* Dentro del modal no, por lo mismo que el resto de los campos: la consulta de
 * arriba mide **la ventana** y el modal mide 520 px, así que en una pantalla
 * ancha se partiría igual y dejaría dos columnas de 230. La ventana de agregar
 * una línea lleva esta misma barra. */
.modal .catalog-search { grid-template-columns: minmax(0, 1fr); }

/* ── El tablero ejecutivo ────────────────────────────────────────────────────
 *
 * La forma es la del prototipo: cuatro indicadores arriba, una fila de dos
 * tercios y un tercio —el gráfico y «Para ti hoy»—, y abajo las tarjetas de
 * área. Ver `templates/companies/dashboard.html`.
 *
 * Las rejillas se apilan en el teléfono y no se aprietan: cuatro indicadores
 * a 320 px de ancho son cuatro renglones de texto ilegible. */
/* El radio de la tarjeta ya lo da la escala —`--radius`, 16 px, igualado al
 * prototipo el 10-09-2026—. Acá solo queda el de las cajas de adentro, que
 * allá son `rounded-xl` y valen 14: la escala del sitio no tiene ese paso. */
.board__kpis, .board__row, .board__areas { --board-box: 14px; }
.board__kpis > .card, .board__row > .card, .board__areas > .card {
  padding: 20px 24px;
}
.board__kpis {
  display: grid; gap: 16px; grid-template-columns: minmax(0, 1fr);
  margin-bottom: 16px;
}
.board__row, .board__areas {
  display: grid; gap: 16px; grid-template-columns: minmax(0, 1fr);
  margin-bottom: 16px;
}
@media (min-width: 760px) {
  .board__kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .board__areas { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1100px) {
  .board__kpis { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .board__areas { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  /* El gráfico pesa el doble: se mira, y el de al lado se usa. */
  .board__row { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
}
/* Las tarjetas van lado a lado, así que el margen entre hermanas sobra: lo
 * pone `gap`. Ver `core/tests/test_las_tarjetas_cuadran.py`. */
.board__kpis > .card + .card,
.board__row > .card + .card,
.board__areas > .card + .card { margin-top: 0; }

/* Un indicador: el icono en su cuadro de color, y al lado el número. */
.board__kpi { display: flex; align-items: flex-start; gap: 16px; }
.board__kpi-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; border-radius: var(--board-box); flex: none;
}
.board__kpi-icon .icon { width: 20px; height: 20px; }
.board__kpi-icon--azul    { background: #eff6ff; color: #2563eb; }
.board__kpi-icon--verde   { background: #ecfdf5; color: #059669; }
.board__kpi-icon--naranja { background: #fff7ed; color: #f97316; }
.board__kpi-icon--morado  { background: #faf5ff; color: #9333ea; }
.board__kpi-label { margin: 0 0 4px; font-size: 12px; color: var(--muted-foreground); }
/* Las cifras del Tablero son indicadores: DM Sans, como `.kpi__value`. */
.board__kpi-value { margin: 0; font-size: 30px; font-weight: 700; line-height: 1; font-family: var(--font-sans); }
.board__kpi-sub {
  display: flex; align-items: center; gap: 8px;
  margin: 6px 0 0; font-size: 12px; color: var(--faint);
}
.board__delta { font-weight: 600; color: var(--success, #059669); }
.board__delta--bad { color: var(--danger, #dc2626); }

/* El gráfico de barras: dos por mes, la real y la de referencia. */
.board__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
.board__eyebrow {
  margin: 0 0 4px; font-size: 11px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted-foreground);
}
.board__big { margin: 0; font-size: 22px; font-weight: 700; font-family: var(--font-sans); }
.board__big-sub { font-size: 14px; font-weight: 400; color: var(--faint); }
.board__legend { display: flex; gap: 12px; font-size: 12px; color: var(--faint); }
.board__legend span { display: inline-flex; align-items: center; gap: 6px; }
.board__swatch { width: 10px; height: 10px; border-radius: 2px; display: inline-block; }
.board__swatch--real { background: #3b82f6; }
.board__swatch--goal { background: #dbeafe; }
.board__chart {
  display: flex; align-items: flex-end; gap: 6px;
  height: 160px; margin-top: 20px;
}
.board__month { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 4px; }
.board__bars { display: flex; align-items: flex-end; gap: 2px; width: 100%; height: 130px; }
.board__bar { flex: 1; border-radius: 3px 3px 0 0; min-height: 2px; }
.board__bar--real { background: #3b82f6; }
.board__bar--goal { background: #dbeafe; }
.board__month-label { font-size: 10px; color: var(--faint); }
/* Quién está vendiendo, bajo el gráfico. Cuatro columnas en la pantalla ancha
 * y dos en el teléfono: a 320 px, cuatro dejan el nombre partido en tres. */
.board__sellers {
  display: grid; gap: 10px 16px; grid-template-columns: repeat(2, minmax(0, 1fr));
  margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--border);
}
@media (min-width: 900px) {
  .board__sellers { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
.board__seller { display: flex; align-items: center; gap: 8px; }
.board__seller .board__line-label {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.board__seller-value { font-size: 12px; font-weight: 700; }

/* «Para ti hoy»: cada aviso es una caja con su color, no un renglón de tabla. */
.board__alerts { display: flex; flex-direction: column; gap: 8px; }
.board__alert {
  display: flex; align-items: center; gap: 12px;
  padding: 12px; border: 1px solid var(--border); border-radius: var(--board-box);
  color: inherit;
}
.board__alert:hover { text-decoration: none; background: var(--muted); }
.board__alert .icon { width: 16px; height: 16px; flex: none; color: var(--muted-foreground); }
.board__alert-text { flex: 1; min-width: 0; }
.board__alert-text strong { display: block; font-size: 12px; font-weight: 600; line-height: 1.25; }
/* **El segundo renglón guarda su sitio aunque venga vacío.**
 *
 * No todos los avisos llevan detalle: los que esperan una firma no lo llevan
 * a propósito —la etiqueta ya lo dice, y repetir la misma frase en diez
 * renglones es ruido—. Sin esto, esas cajas miden un renglón menos que las
 * demás y la columna queda dispareja, que es lo que vio Carlos el
 * 10-09-2026. En el prototipo todas miden igual porque todas traen las dos
 * líneas; acá miden igual porque el hueco está reservado. */
.board__alert-text small {
  display: block; min-height: 1.45em;
  font-size: 11px; line-height: 1.45; color: var(--faint);
}
.board__alert--warning { border-color: #fcd34d; }
.board__alert--warning .icon { color: #d97706; }
.board__alert--danger { border-color: #fca5a5; background: #fef2f2; }
.board__alert--danger .icon { color: #dc2626; }

/* Las tarjetas de área: líneas con semáforo y recuadros al pie. */
.board__area { display: flex; flex-direction: column; }
.board__area-head {
  display: flex; align-items: center; gap: 8px;
  margin-bottom: 14px; font-size: 14px; font-weight: 600;
}
.board__area-head .icon { width: 16px; height: 16px; color: var(--muted-foreground); }
/* Cada área con el color de su icono, como en el prototipo: es lo que las
 * distingue de un vistazo con las cuatro tarjetas una al lado de la otra. */
.board__area-head--naranja .icon { color: #f97316; }
.board__area-head--celeste .icon { color: #0891b2; }
.board__area-head--morado  .icon { color: #9333ea; }
.board__area-head--indigo  .icon { color: #6366f1; }
.board__area-head--rojo    .icon { color: #ef4444; }
/* El acceso al sistema en la ficha del colaborador: no es un campo, es un
 * hecho con su acción al lado. Va detrás de una línea porque no lo guarda el
 * botón de abajo. */
.access {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--border);
}
.access__label { margin: 0 0 4px; font-size: 14px; font-weight: 500; color: var(--foreground-soft); }
.access__value { margin: 0; font-size: 14px; }
.access__none { margin: 0; font-size: 13px; color: var(--faint); }

/* La línea del flujo de caja, en la tarjeta de Finanzas. El trazo no se
 * escala con el lienzo —`vector-effect`— porque el `viewBox` se estira a lo
 * ancho y sin eso la línea saldría más gruesa en una tarjeta ancha que en
 * una angosta. */
.board__flow { margin: 6px 0 14px; }
.board__flow svg { width: 100%; height: 80px; display: block; }
.board__flow-line {
  fill: none; stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
.board__flow-line--in  { stroke: var(--success); }
.board__flow-line--out { stroke: var(--danger); }
.board__flow-months {
  display: flex; justify-content: space-between;
  font-size: 9px; color: var(--faint);
}
.board__flow-empty {
  margin: 6px 0 14px; font-size: 11px; color: var(--faint);
}

/* «Para ti hoy» es el único título con el icono dentro de un cuadro, y así
 * lo dibuja el prototipo: el rayo ámbar sobre fondo ámbar claro. */
.board__badge {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 10px; flex: none;
  background: #fffbeb;
}
.board__badge .icon { width: 14px; height: 14px; color: #f59e0b; }
.board__lines { display: flex; flex-direction: column; gap: 10px; }
.board__line, .board__risk { display: flex; align-items: center; gap: 10px; }
.board__light { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.board__light--verde    { background: #10b981; }
.board__light--amarillo { background: #fbbf24; }
.board__light--rojo     { background: #ef4444; }
.board__light--azul     { background: #3b82f6; }
.board__light--gris     { background: var(--border); }
.board__line-label { flex: 1; font-size: 12px; color: var(--muted-foreground); }
.board__line-value { font-size: 12px; font-weight: 700; }
.board__line-value--danger { color: var(--danger, #dc2626); }
.board__risk {
  padding: 8px 10px; border-radius: var(--radius-sm); background: var(--muted);
  font-size: 11px;
}
.board__risk--vencido { background: #fef2f2; }
.board__risk-area { font-size: 11px; color: var(--faint); flex: none; }
.board__tiles {
  display: grid; gap: 8px; grid-template-columns: repeat(2, minmax(0, 1fr));
  margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--border);
}
/* Los cuatro de «Capital humano» y los del semáforo de riesgos van en dos
 * filas de dos, no en una de cuatro: a 240 px de tarjeta, cuatro columnas
 * dejan cajas donde «Productividad» no cabe. */
/* Cuando el bloque no trae líneas —«Capital humano» son cuatro recuadros y
 * nada más— los recuadros no van detrás de una raya: no hay de qué
 * separarlos. */
.board__tiles--solos { margin-top: 0; padding-top: 0; border-top: 0; }
/* La raya que parte la cuarta tarjeta en dos bloques. */
.board__split { margin: 18px 0; border-top: 1px solid var(--border); }
.board__tile {
  padding: 12px; border-radius: var(--board-box); text-align: center;
  background: var(--muted);
}
.board__tile--danger  { background: #fef2f2; color: #b91c1c; }
.board__tile--warning { background: #fff7ed; color: #c2410c; }
.board__tile--caution { background: #fefce8; color: #a16207; }
.board__tile--ok      { background: #f0fdf4; color: #15803d; }
.board__tile-value { margin: 0; font-size: 18px; font-weight: 700; line-height: 1.1; font-family: var(--font-sans); }
.board__tile-label { margin: 3px 0 0; font-size: 10px; color: var(--faint); }
.board__tile--danger .board__tile-label,
.board__tile--warning .board__tile-label,
.board__tile--caution .board__tile-label,
.board__tile--ok .board__tile-label { color: inherit; opacity: .75; }


/* Las tarjetas del catálogo de la toma de pedidos.
 *
 * Cada una es un botón entero y no una tarjeta con un botón adentro: el gesto
 * es «toco el producto y entra al pedido», y un blanco del tamaño de la tarjeta
 * se acierta con el pulgar. La rejilla se acomoda sola —dos columnas en un
 * teléfono, más donde quepan— porque esta pantalla se usa en la calle.
 */
.cards {
  display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
}
.prodcard {
  display: flex; flex-direction: column; gap: 6px;
  padding: 12px; text-align: left; cursor: pointer;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--card); color: var(--foreground);
  transition: border-color .12s, box-shadow .12s;
}
.prodcard:hover { border-color: var(--primary); box-shadow: var(--shadow-sm); }
.prodcard:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }
.prodcard__head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.prodcard__name { font-size: 13px; font-weight: 600; }
.prodcard__foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.prodcard__price { font-size: 14px; font-weight: 700; }
.prodcard__add {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: var(--radius-pill);
  background: var(--muted); color: var(--muted-foreground);
}
.prodcard:hover .prodcard__add { background: var(--primary); color: #fff; }

/* La calificación del proveedor: cinco estrellas, las ganadas llenas. */
.stars { display: inline-flex; align-items: center; gap: 2px; }
.stars .icon { font-size: 14px; color: var(--border-input); }
.stars .stars__on { color: #eab308; fill: #eab308; }

.list-foot {
  padding: 12px 20px; border-top: 1px solid var(--border-soft);
  font-size: 12px; color: var(--faint);
}

/* La ficha del cliente lleva seis y no cuatro: es la fila del prototipo. */
/* Tres cuadros, no cuatro: la fila de promociones perdió el de la lista de
 * referencia y el hueco vacío a la derecha se leía como algo que faltaba. */
/* El cuadro que avisa: fondo y borde rojos, como en el prototipo. Se
 * enciende solo cuando hay algo que atender —con cero clientes bloqueados la
 * tarjeta roja sería una alarma permanente, y una alarma que siempre está
 * encendida se deja de ver. */
.kpi--alert { border-color: #ffa2a2; background: #fef2f2; }
.kpi--alert .kpi__label,
.kpi--alert .kpi__value,
.kpi--alert .kpi__sub { color: var(--danger-text); }

/* ── Indicadores con color e icono ───────────────────────────────────────────
 *
 * Los pidió Carlos el 12-09-2026, con las tarjetas de liquidaciones del
 * prototipo como referencia: un tono por tarjeta y un cuadro con su icono.
 *
 * **El tono dice qué es el número, no lo adorna.** Ámbar lo que se debe, azul
 * lo pactado, verde lo que queda a favor y rojo lo que hay que ir a buscar. De
 * ahí sale la regla que cada pantalla aplica: **el tono se enciende solo
 * cuando hay algo que decir**. Un «Vencido: 0» en rojo es una alarma
 * permanente, y una alarma que siempre está encendida se deja de ver —es lo
 * mismo que ya cuidaba `.kpi--alert`—.
 *
 * Los tonos se escriben como variables y no como colores sueltos porque el
 * icono, el borde, el fondo y el número son cuatro sitios del mismo tono: con
 * colores sueltos, cambiar uno dejaba los otros tres atrás. */
.kpi--tono {
  border-color: var(--kpi-borde, var(--border));
  background: var(--kpi-fondo, var(--card));
}
.kpi--tono .kpi__value { color: var(--kpi-texto, var(--foreground)); }
.kpi__head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.kpi__head .kpi__label { margin: 0; }
.kpi__icon {
  flex: none; width: 28px; height: 28px; border-radius: var(--radius-sm);
  display: grid; place-items: center;
  background: var(--kpi-icono, var(--muted));
  color: var(--kpi-texto, var(--muted-foreground));
}
.kpi__icon .icon { width: 15px; height: 15px; }

.kpi--ambar {
  --kpi-borde: #fde68a; --kpi-fondo: #fffbeb;
  --kpi-texto: var(--warning-text); --kpi-icono: #fef3c7;
}
.kpi--azul {
  --kpi-borde: #bfdbfe; --kpi-fondo: #eff6ff;
  --kpi-texto: #1d4ed8; --kpi-icono: #dbeafe;
}
.kpi--verde {
  --kpi-borde: #bbf7d0; --kpi-fondo: #f0fdf4;
  --kpi-texto: var(--success-text); --kpi-icono: #dcfce7;
}
.kpi--rojo {
  --kpi-borde: #fecaca; --kpi-fondo: #fef2f2;
  --kpi-texto: var(--danger-text); --kpi-icono: var(--danger-soft);
}
/* El resumen: gris, como la tarjeta de conteos del cuadro comparativo. */
.kpi--gris {
  --kpi-borde: #e5e7eb; --kpi-fondo: #f9fafb;
  --kpi-texto: var(--foreground); --kpi-icono: #e5e7eb;
}

/* ── Los indicadores con el estilo marcado ───────────────────────────────────
 *
 * Carlos, punto 5 del COMERCIAL.docx, sobre el cuadro comparativo de ruta que
 * puso de referencia: «a nivel gráfico y de estructura debe ser así, con
 * colores y distinciones marcadas de color».
 *
 * Tres cosas lo marcan, y ninguna cambia lo que ya estaba: se encienden con
 * `.kpis--marcados` en la fila, con `components/kpi.html` en la tarjeta y con
 * el `tono` del gráfico en la sección. Las pantallas que todavía no pasaron al
 * estilo nuevo se ven igual que ayer.
 *
 * - **El icono sólido**, en el color fuerte del tono y con el dibujo en blanco.
 * - **La píldora de la variación**: verde si el movimiento fue para bien,
 *   roja si fue para mal, gris si no cambió o no hay con qué comparar.
 * - **La franja de la sección**, del color que dice qué es: verde lo que
 *   crece, azul cómo se reparte, gris el resumen, rojo lo que se pierde.
 *
 * **Blanco sobre el tono oscuro de cada color, no sobre el vivo.** El verde y
 * el ámbar vivos no dejan leer un blanco de quince píxeles; el oscuro de la
 * misma paleta sí, y sigue siendo el mismo color a la vista. */
.kpi--azul  { --kpi-solido: var(--primary); }
.kpi--verde { --kpi-solido: var(--success-text); }
.kpi--ambar { --kpi-solido: var(--warning-text); }
.kpi--rojo  { --kpi-solido: var(--danger); }
.kpi--gris  { --kpi-solido: #6b7280; }

.kpis--marcados .kpi { padding: 18px 20px; }
.kpis--marcados .kpi__icon {
  width: 34px; height: 34px;
  background: var(--kpi-solido, var(--muted-foreground)); color: #fff;
}
.kpis--marcados .kpi__icon .icon { width: 18px; height: 18px; }
.kpis--marcados .kpi__label { font-size: 13px; font-weight: 600; color: var(--foreground); }
.kpis--marcados .kpi__value { margin-top: 6px; font-size: 28px; }

.kpi__delta {
  display: inline-flex; align-items: center; gap: 5px;
  margin: 8px 0 0; padding: 3px 10px; border-radius: var(--radius-pill);
  font-size: 12px; font-weight: 600; font-variant-numeric: tabular-nums;
}
.kpi__delta--bien  { background: var(--success-soft); color: var(--success-text); }
.kpi__delta--mal   { background: var(--danger-soft); color: var(--danger-text); }
.kpi__delta--igual { background: var(--muted); color: var(--muted-foreground); }

.grafico-caja--banda { padding-top: 0; overflow: hidden; }
.grafico-caja__banda {
  display: flex; align-items: center; gap: 10px;
  margin: 0 -20px 14px; padding: 12px 20px;
  background: var(--banda); color: #fff;
}
.grafico-caja__banda h2 { margin: 0; font-size: 15px; font-weight: 600; color: #fff; }
.grafico-caja__banda .icon { flex: none; width: 18px; height: 18px; }
.grafico-caja__pie--banda { margin: 0 0 14px; }
.grafico-caja--azul  { --banda: var(--primary); }
.grafico-caja--verde { --banda: var(--success-text); }
.grafico-caja--ambar { --banda: var(--warning-text); }
.grafico-caja--rojo  { --banda: var(--danger); }
.grafico-caja--gris  { --banda: #6b7280; }

/* ── Una tarjeta que lleva a lo que cuenta ───────────────────────────────────
 *
 * Carlos, punto 1 del COMERCIAL.docx: «las cards deben poder tocarse y llevar
 * a la info que está tomando».
 *
 * **El enlace cubre la tarjeta entera**, no solo la flecha: con el dedo uno
 * aprieta el recuadro, no un icono de quince píxeles. Va por encima con
 * `inset: 0` en vez de envolver el contenido, porque envolverlo obligaría a
 * repetir la tarjeta entera en las dos ramas del `{% if %}` que decide si hay
 * permiso para verla.
 *
 * **La flecha se dibuja siempre**, apagada, y no al pasar por encima: en una
 * pantalla táctil no hay «pasar por encima», y una señal que solo aparece con
 * el ratón no existe para quien usa el dedo. */
.kpi--tocable { position: relative; }
.kpi__ir {
  position: absolute; inset: 0; border-radius: inherit;
  display: flex; align-items: flex-start; justify-content: flex-end;
  padding: 14px 14px 0 0;
  color: var(--kpi-texto, var(--muted-foreground)); opacity: .45;
  transition: opacity .15s, background-color .15s;
}
.kpi--tocable:hover .kpi__ir { opacity: 1; background: rgba(0, 0, 0, .03); }
.kpi__ir:focus-visible { opacity: 1; outline: 2px solid var(--primary); outline-offset: 2px; }
.kpi__ir .icon { width: 16px; height: 16px; }

/* ── Una tabla que se llena, no que se lee ───────────────────────────────────
 *
 * Los productos de un convenio son un formset dentro de una tabla: cada celda
 * lleva un control, no un dato. Sin anchos, el navegador reparte las columnas
 * por lo que **hay escrito** —«1001 · COSTA VIZZIO CHOCOLATE 70G» se lleva
 * media tabla y «Alcance» queda de cuatro letras—, y como la lista desplegable
 * se dibuja del ancho de su control, «Un producto» salía partido en dos
 * renglones. Lo vio Carlos el 12-09-2026.
 *
 * Con los anchos puestos cada columna mide lo que su control necesita, el
 * nombre largo se recorta con puntos suspensivos en vez de empujar a las
 * demás, y la tabla entera se desplaza dentro de su `.table-wrap` cuando la
 * ventana no da. */
/* Una tabla que **se llena**, no una que se lee: cada celda lleva un control.
 * Lo de acá vale para cualquiera —los renglones de un convenio, los tramos de
 * una escala—; los anchos de cada columna son de cada pantalla y van abajo. */
.table--renglones { table-layout: fixed; width: 100%; }
/* Los costados de la celda se achican: en una tabla que se lee, el aire separa
 * los datos; en una que se llena, se lo quita al control. Con los veinte
 * píxeles de siempre a cada lado, «Una categoría» no cabía en su desplegable
 * y salía cortado. */
/* Con `tbody`/`thead` adentro: la regla general del sistema —`.table tbody td`—
 * pesa más que `.table--renglones td` y le ganaba. */
.table--renglones thead th, .table--renglones tbody td {
  padding-left: 8px; padding-right: 8px;
}
/* La columna de quitar, centrada: es un icono y no un dato, y alineado a la
 * izquierda quedaba pegado al número. Va por «la última» y no por un número de
 * columna, que cambia de una pantalla a otra. */
.table--renglones thead th:last-child,
.table--renglones tbody td:last-child { text-align: center; }
/* Con `tbody` adentro por lo mismo que el relleno: la regla general del
 * sistema —`.table tbody td`— alinea arriba y pesa más que `.table--renglones
 * td`. Sin esto, el botón de quitar, que es más bajo que los controles,
 * quedaba seis píxeles por encima del renglón. */
.table--renglones tbody td { vertical-align: middle; white-space: normal; }
.table--renglones td > select,
.table--renglones td > input:not([type="checkbox"]),
.table--renglones .celda-si > select,
.table--renglones .ts-wrapper { width: 100%; min-width: 0; }
/* El nombre largo se recorta dentro del control y no empuja la columna. */
.table--renglones .ts-control > .item {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Los renglones de un convenio: siete columnas, y dos de ellas con un
 * desplegable de novecientos productos. Los anchos son suyos —el ancho mínimo
 * hace que la tabla se desplace en vez de encogerse— y por eso no viven en la
 * clase general: los tramos de una escala son cuatro columnas de números y con
 * novecientos píxeles quedaban cortados dentro de su tarjeta. */
.table--renglones--convenio { min-width: 900px; }
.table--renglones--convenio thead th:nth-child(1) { width: 150px; }
.table--renglones--convenio thead th:nth-child(2) { width: 225px; }
.table--renglones--convenio thead th:nth-child(3) { width: 170px; }
/* Las tres de números caben «100.00» con sus flechas: con diez píxeles menos
 * el «Desde» de un volumen salía cortado. */
.table--renglones--convenio thead th:nth-child(4) { width: 105px; }
.table--renglones--convenio thead th:nth-child(5) { width: 110px; }
.table--renglones--convenio thead th:nth-child(6) { width: 105px; }
.table--renglones--convenio thead th:nth-child(7) { width: 55px; }

/* La celda que el alcance apaga. Se escribe su `[hidden]` porque el guion la
 * esconde con el atributo y basta una regla propia con `display` para
 * ganarle —es la avería que cuida `test_ocultar.py`—. */
.celda-si[hidden] { display: none !important; }

/* El renglón que deja pérdida, entero en rojo. Con la píldora sola el número
 * estaba marcado y el producto que lo provoca se leía como cualquier otro: hay
 * que poder barrer la tabla y ver cuáles son sin ir número por número.
 *
 * Con `tbody` adentro y en el `td`, que es donde vive el fondo de la fila: en
 * una tabla, el fondo del `<tr>` lo tapan las celdas. */
.table tbody tr.fila--perdida > td { background: #fef2f2; color: var(--danger-text); }
.table tbody tr.fila--perdida .person__name,
.table tbody tr.fila--perdida .person__role,
.table tbody tr.fila--perdida .cell-sub,
.table tbody tr.fila--perdida .cell-faint,
.table tbody tr.fila--perdida strong { color: var(--danger-text); }

/* Un número que dice cómo va, en una tabla que se barre con la vista.
 *
 * Es la hoja de precios: dos mil renglones de cifras iguales donde hay que
 * encontrar el que no rinde. «Son muy visuales», pidió Carlos el 13-09-2026.
 *
 * **El tono es el dato y no el adorno**, que es la misma regla de los
 * indicadores: verde lo que deja, ámbar lo que no deja nada —vender al costo
 * es una decisión, y verla dicha es distinto de no verla— y rojo lo que se
 * vende por debajo. Lo que no juzga —el costo— se apaga, y lo que se busca
 * —el precio con IVA— queda en la tinta más fuerte. La vista va de la entrada
 * al resultado sin que nadie se lo explique.
 */
.table .num--gana { color: var(--success-text); }
.table .num--parejo { color: var(--warning-text); }
.table .num--pierde { color: var(--danger-text); font-weight: 600; }
.table .num--entrada { color: var(--muted-foreground); }
.table .num--resultado { color: var(--foreground); font-weight: 600; }

/* Un renglón del carrito: el nombre, la cantidad y los botones, en columnas
 * fijas.
 *
 * Se dibujaba con `detail__row`, que reparte el ancho entre lo que haya, así
 * que un nombre largo empujaba la cantidad y cada renglón la dejaba en otra
 * parte: de un vistazo no se podían comparar. Acá el nombre es lo único que
 * cede —se recorta y el título completo queda en el `title`— y la cantidad
 * tiene su ancho, su alineación a la derecha y las cifras de igual paso.
 */
.carrito__linea {
  display: grid; grid-template-columns: minmax(0, 1fr) auto auto auto;
  align-items: center; gap: 12px; padding: 8px 0;
}
.carrito__linea + .carrito__linea { border-top: 1px solid var(--border); }
.carrito__nombre {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 12.5px; color: var(--muted-foreground);
}
.carrito__cantidad { min-width: 72px; text-align: right; }
/* La casilla de «se regala» no empuja: ocupa lo suyo y nada más. */
.carrito__linea .field__check { white-space: nowrap; font-size: 12.5px; }

.renglones__pie { margin-top: 12px; margin-bottom: 4px; }

/* El botón de quitar un renglón: solo el icono, y en rojo al pasar por encima
 * —quita algo, y eso se dice antes de apretarlo—. */
.btn.btn--icono {
  padding: 0; width: 28px; height: 28px; min-width: 0;
  border-color: transparent; background: none; color: var(--faint);
}
.btn.btn--icono:hover {
  border-color: var(--danger); background: var(--danger-soft);
  color: var(--danger-text);
}
.btn.btn--icono .icon { width: 15px; height: 15px; }

/* ── Agregar varios de una vez ───────────────────────────────────────────────
 *
 * Un convenio normal son diez o veinte productos con el mismo descuento. El
 * bloque va **debajo de la tabla y no encima**: lo que se viene a ver al abrir
 * la pantalla es lo que ya está pactado; agregar es lo que se hace después.
 *
 * Se dibuja como un recuadro aparte y no como una tarjeta más: es una
 * herramienta para llenar la tabla, no una sección del convenio. */
.lote {
  margin-top: 16px; padding: 14px 16px;
  border: 1px dashed var(--border); border-radius: var(--radius);
  background: var(--muted);
}
.lote__titulo {
  display: block; margin-bottom: 10px;
  font-size: 12px; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--muted-foreground);
}
/* **Alineados por arriba y no por abajo.** Los rótulos van todos en la misma
 * línea y los controles arrancan todos a la misma altura; el de productos crece
 * hacia abajo a medida que se escogen, sin mover a los demás. Alineando por
 * abajo pasaba lo contrario: al crecer, su rótulo subía y la fila quedaba
 * escalonada (Carlos, 12-09-2026). */
.lote__campos { display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap; }
.lote__campo { display: flex; flex-direction: column; gap: 4px; min-width: 0; flex: 0 0 auto; }
.lote__campo--ancho { flex: 1 1 320px; }
/* El ancho va en la columna y no en el campo: adentro, el control es un
 * elemento flexible y se encoge hasta lo que mida el rótulo —«Desde» dejaba
 * una caja de cinco letras y «Descuento %» una del doble—. */
.lote__campo--numero { width: 120px; }
/* Sin el margen de abajo que traen los rótulos del sistema: acá la separación
 * la pone el `gap` de la columna, y con los dos el rótulo del botón —que no
 * tiene margen— arrancaba seis píxeles más arriba. */
.lote__campo label, .lote__hueco {
  margin: 0; font-size: 12px; line-height: 16px; color: var(--muted-foreground);
}
.lote__campo input, .lote__campo--ancho .ts-wrapper { width: 100%; }
/* Todos del alto de un campo del sistema, que es lo que hace que empiecen y
 * terminen en la misma raya. */
.lote__campo input,
.lote__campo .btn,
.lote__campo--ancho .ts-wrapper .ts-control { min-height: 40px; }
.lote__campo .btn { height: 40px; }
/* El que admite varios trae holgura para las fichas; vacío se veía más alto
 * que los demás. Crece solo cuando hay algo escogido. */
.lote__campo--ancho .ts-wrapper.multi .ts-control { padding: 4px 28px 4px 6px; }
/* La caja de búsqueda de adentro hereda el alto de un campo del sistema, y
 * sumada al relleno del control lo dejaba diez píxeles más alto que sus
 * vecinos. Adentro no es un campo: es una línea de texto. */
.lote__campo--ancho .ts-wrapper.multi .ts-control > input {
  margin: 0; height: auto; min-height: 0;
}
.lote__aviso { margin: 10px 0 0; font-size: 12px; color: var(--success-text); }

/* Las fichas de lo ya escogido, con su equis. La biblioteca viene sin sus
 * complementos, así que el botón de quitar se dibuja acá: sin él, lo único que
 * saca algo de la lista es la tecla de borrar, y eso no se adivina. */
.ts-wrapper.multi .ts-control > .item {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 2px 6px 2px 8px; border-radius: var(--radius-pill);
  background: var(--primary-soft, #e8efff); color: var(--primary-text, #1d4ed8);
  font-size: 12px;
}
.ts-wrapper.multi .ts-control > .item .item__quitar {
  cursor: pointer; font-size: 14px; line-height: 1; opacity: .6;
}
.ts-wrapper.multi .ts-control > .item .item__quitar:hover { opacity: 1; }


.kpis--tres { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.kpis--six { grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 12px; }
.kpis--six .kpi { padding: 14px 16px; }
.kpis--six .kpi__value { font-size: 20px; }

/* La misma tira con siete: la ficha del cliente sumó el ritmo de compra
 * (punto 58). Se reparte igual que la de seis —mismo relleno, mismo cuerpo—
 * porque son la misma tira con una tarjeta más, y en los cortes se apoya en
 * los mismos anchos: siete columnas de 1400 para arriba y de ahí a tres y a
 * dos, que es donde una tarjeta deja de leerse. */
.kpis--siete { grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 12px; }
.kpis--siete .kpi { padding: 14px 16px; }
.kpis--siete .kpi__value { font-size: 20px; }

/* Cinco celdas, no seis (auditoría del 25-09-2026, punto 19.2 de COMPRAS): la
 * V4 trae `md:grid-cols-5` en la ficha de la orden de compra, y el estado de
 * pago va en su propio renglón debajo, no como sexta celda. Vivía en
 * `parciales/nav.css` y `core/tests/test_los_indicadores_tienen_su_regla.py`
 * solo lee esta hoja, así que se mudó acá con sus vecinas `--six`/`--siete`. */
.kpis--five { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 12px; }
.kpis--five .kpi { padding: 14px 16px; }
.kpis--five .kpi__value { font-size: 20px; }

@media (max-width: 1400px) {
  .kpis--six { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .kpis--siete { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .kpis--five { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 1100px) {
  .kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .kpis--six { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .kpis--siete { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .kpis--five { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* La persona de una lista: retrato, nombre y puesto en una línea. No confundir
 * con `.contactcard`, que era la tarjeta de un contacto del cliente: se llamaban
 * igual y el 06-09-2026 la tarjeta le rompió el nombre a esta, que quedó
 * dentro de una caja blanca y alineado a la derecha. */
.person { display: flex; align-items: center; gap: 12px; min-width: 0; }
.person__name { font-size: 14px; font-weight: 500; color: var(--foreground); }
.person__name:hover { color: var(--primary-text); }
/* El número de un documento —un pedido, una factura, una nota de crédito— no
 * es un nombre: es un identificador que alguien lee dígito por dígito, dicta
 * por teléfono y compara contra un papel. Va monoespaciado, y así los ceros y
 * los unos no se confunden ni las cifras bailan de fila en fila.
 *
 * **Y va en azul**, como en el Figma: es la puerta al documento y se ve que lo
 * es. La fila entera también abre —lo hace `filas.js`— pero eso hay que
 * descubrirlo pasando el ratón, y el número lo dice de entrada.
 *
 * Sin subrayarse al pasar por encima, que es la regla de la casa: acá casi todo
 * lo que responde es una fila o una tarjeta, y subrayarlas se lee como un error
 * de dibujo. Lo que dice que responde es el color.
 *
 * No lleva el fondo gris de `.code`: eso es para un código incrustado en medio
 * de otra cosa, y este es el título del renglón. (Carlos, 08-09-2026)
 */
.person__name--doc {
  font-family: var(--font-mono); font-weight: 600; color: var(--primary-text);
  white-space: nowrap;
}
.person__role { font-size: 12px; color: var(--muted-foreground); }
.avatar--1 { background: #dbeafe; color: #1e40af; }
.avatar--2 { background: #fce7f3; color: #9d174d; }
.avatar--3 { background: #dcfce7; color: #166534; }
.avatar--4 { background: #fef3c7; color: #92400e; }
.avatar--5 { background: #ede9fe; color: #5b21b6; }
.avatar--6 { background: #ccfbf1; color: #115e59; }
.status-dot { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted-foreground); }
.status-dot--on { color: var(--success-text); }
/* El segmentado S1/S2 de la barra de Horarios: dice en cuál semana de la
 * rotación está uno parado y salta a la otra. */
.cycle { display: inline-flex; gap: 2px; padding: 2px; border-radius: 8px; background: var(--muted); }
/* Sirve para un enlace y para un botón de verdad. Nació para dos enlaces —las
 * dos semanas de la rotación— y en las rutas hace falta que sean botones, que
 * es lo que deja elegir el día sin JavaScript. Sin este reinicio el navegador
 * les pone su propio borde y su propio fondo, y la tira se ve como siete
 * botones sueltos en vez de un control. */
.cycle__btn {
  padding: 3px 10px; border: 0; border-radius: 6px; background: none;
  font-family: inherit; font-size: 11px; font-weight: 600; line-height: 20px;
  color: var(--muted-foreground); cursor: pointer;
}
.cycle__btn:hover { color: var(--foreground); text-decoration: none; }
.cycle__btn--on { background: var(--primary); color: #fff; }
.cycle__btn--on:hover { color: #fff; }

/* ── El selector de una ruta ─────────────────────────────────────────────────
 * Dos grupos con su rótulo, uno debajo del otro: el día y la semana. Con los
 * dos en la misma línea y sin rótulo —como salió la primera vez— se leían como
 * once botones de lo mismo, y «S1» al lado de «Dom» no dice qué es.
 *
 * Cada grupo es su propio formulario para que el botón mande su valor y
 * arrastre el otro; por eso el rótulo va dentro y no envolviendo a los dos. */
.picker { display: flex; flex-direction: column; gap: 10px; }
.picker__row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.picker__label {
  min-width: 62px; font-size: 11px; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--faint);
}
.picker__hint { margin: 0; font-size: 12px; color: var(--muted-foreground); }

/* ── La barra de una ruta ────────────────────────────────────────────────────
 * El selector a la izquierda y la cuenta a la derecha, en la misma tarjeta.
 * Separados eran dos tarjetas anchas con tres renglones cortos cada una y
 * media pantalla en blanco al lado; juntos, la banda se llena y se lee de una
 * pasada: qué día estoy mirando y cuántos son. */
.routebar { display: flex; align-items: flex-start; gap: 32px; flex-wrap: wrap; }
.routebar .picker { flex: 1 1 380px; }
.routebar__counts { display: flex; gap: 28px; margin-left: auto; }
.routebar__count { text-align: right; }
.routebar__count strong {
  display: block; font-size: 28px; font-weight: 700; line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.routebar__count span { font-size: 12px; color: var(--muted-foreground); }
@media (max-width: 720px) {
  .routebar__counts { margin-left: 0; gap: 20px; }
  .routebar__count { text-align: left; }
}

/* ── El recorrido ────────────────────────────────────────────────────────────
 * Una parada por fila, con su número grande a la izquierda. Es una secuencia
 * —quien maneja va de la uno a la última—, y una tabla de seis columnas cortas
 * la contaba como si fuera un listado alfabético, con la mitad del ancho
 * vacío. */
.stops { display: flex; flex-direction: column; }
.stop {
  display: flex; align-items: center; gap: 16px;
  padding: 12px 16px; border-top: 1px solid var(--border);
}
.stop:first-child { border-top: 0; }
.stop__n {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  min-width: 34px; height: 34px; padding: 0 8px; border-radius: 8px;
  background: var(--muted); color: var(--muted-foreground);
  font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums;
}
/* Sin número no hay recorrido: se dice apagado y no se finge un orden. */
.stop__n--none { background: none; border: 1px dashed var(--border); color: var(--faint); font-weight: 400; }
.stop__who { flex: 1 1 260px; min-width: 0; }
.stop__name { font-weight: 600; }
.stop__where { margin-top: 2px; font-size: 12px; color: var(--muted-foreground); }
.stop__side { flex: 0 1 300px; text-align: right; font-size: 12px; }
.stop__side .num { font-variant-numeric: tabular-nums; }
@media (max-width: 720px) {
  .stop { flex-wrap: wrap; }
  .stop__side { flex-basis: 100%; text-align: left; margin-left: 50px; }
}

/* ── Ordenar arrastrando ─────────────────────────────────────────────────────
 * Es la única manera de ordenar desde el 07-09-2026: la casilla del número se
 * fue. Por eso la fila tiene que **decir sola** que se arrastra, y lo dice con
 * tres cosas a la vez —el agarre, el cursor y el realce al pasar por encima—:
 * con una sola había que descubrirlo probando. */
.stop--drag { cursor: grab; }
/* Se realza entera y no solo el agarre: lo que se mueve es la fila, y realzar
 * un cuadrito de 28 px invita a arrastrar solo ese cuadrito. */
.stop--drag:hover { background: var(--muted); }
.stop--drag:hover .stop__grip { color: var(--muted-foreground); }
.stop__grip {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 32px; border: 0; background: none; border-radius: 6px;
  color: var(--border); cursor: grab; transition: color .12s, background .12s;
}
.stop__grip:hover, .stop__grip:focus-visible { color: var(--foreground); background: var(--muted); }
.stop__grip:active { cursor: grabbing; }
/* La que va en el aire: se apaga para que se vea el hueco donde va a caer. */
.stop--moviendo { opacity: .4; background: var(--muted); }
/* Mientras se arrastra, el puntero lo dice sobre la lista entera. Sin esto
 * vuelve a «grab» en cuanto se sale de la fila y parece que se soltó. */
.stops--moviendo, .stops--moviendo .stop, .stops--moviendo .stop__grip { cursor: grabbing; }

/* ── Tomar y colocar ─────────────────────────────────────────────────────────
 * La manera que hace manejable una ruta larga: arrastrar obliga a sostener el
 * botón mientras se busca un sitio que casi nunca está a la vista.
 *
 * La parada levantada se queda en su puesto y **se marca**, en vez de sacarse
 * de la lista: viéndola donde estaba se entiende de dónde viene, y si se
 * cancela no hay nada que reaparezca de la nada. */
.stop--tomada {
  background: var(--muted);
  outline: 2px dashed var(--primary); outline-offset: -2px;
}
.stop--tomada .stop__grip { color: var(--primary); }
/* Con una parada en el aire, todo lo demás es un sitio donde dejarla. */
.stops--colocando, .stops--colocando .stop { cursor: pointer; }
.stops--colocando .stop__name { pointer-events: none; }
/* La raya de dónde va a caer. Va en el borde y no como fondo: un fondo dice
 * «sobre esta fila», y lo que se elige es el hueco entre dos. */
.stop--hueco { box-shadow: inset 0 2px 0 0 var(--primary); }
.stop--hueco-abajo { box-shadow: inset 0 -2px 0 0 var(--primary); }
/* En pantalla angosta el rótulo se pone encima y la tira usa el ancho entero,
 * en vez de partirse en dos filas desalineadas. */
@media (max-width: 560px) {
  .picker__row { flex-direction: column; align-items: flex-start; gap: 4px; }
  .picker__label { min-width: 0; }
  .cycle { flex-wrap: wrap; }
}
.table .cell-sub { font-size: 12px; color: #4b5563; }
/* La segunda línea de la celda: qué semana de la rotación rige esta. */
.table .cell-sub--rot { display: block; margin-top: 2px; }
/* Un renglón de detalle debajo del dato de la celda: lo que le pasó a una
 * factura —cada cobro, cada nota de crédito—, o para cuándo se espera el pago
 * de un documento a trámite. */
.table .cell-sub--debajo { display: block; margin-top: 2px; }
.table .cell-faint { font-size: 12px; color: var(--faint); }

/* La paginación del prototipo: chiquita, a la derecha, con la página en un
 * cuadrito negro. La cuenta queda a la izquierda, en gris. */
.pager { border-top: 0; padding: 8px 4px 0; font-size: 12px; }
.pager__links { gap: 4px; }
.pager__links .btn { height: auto; padding: 2px 4px; border: 0; background: none; color: var(--faint); font-size: 12px; font-weight: 400; }
.pager__links .btn:hover { color: #4b5563; filter: none; }
.pager__page {
  display: inline-grid; place-items: center; min-width: 18px; height: 18px; padding: 0 4px;
  border-radius: 4px; background: #1f2937; color: #fff; font-size: 11px;
}

/* La cuenta de pendientes en una pestaña, el pie de página completo y las
 * ventanas de detalle e historial. */
.tabs__badge {
  display: inline-grid; place-items: center; margin-left: 6px;
  min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: var(--radius-pill); background: var(--warning-soft); color: var(--warning-text);
  font-size: 11px; font-weight: 600;
}
/*
La paginación completa va **dentro** de la tarjeta, pegada al pie de la
tabla, y recupera la línea y el margen que `.pager` suelta no necesita. Son
dos paginaciones distintas porque el prototipo tiene dos: la de Personal
cuelga suelta bajo la tarjeta, chiquita y sin nada que la separe; la de
Marcas vive adentro, con «Mostrar: N» a la izquierda y las páginas numeradas
a la derecha, y ahí la línea sí separa de la última fila.
*/
.pager--full {
  align-items: center;
  padding: 12px 20px; border-top: 1px solid var(--border-soft);
}
/* «1–20 de 123 | Mostrar: N» es una sola frase y se parte en dos renglones:
 * el desplegable es más alto que el texto y la caja lo mandaba abajo, con la
 * palabra «Mostrar:» colgando sola. En fila y sin cortes se lee de corrido. */
.pager--full .pager__count { display: inline-flex; align-items: center; white-space: nowrap; }
.pager--full .pager__count label { display: inline-flex; align-items: center; gap: 6px; }
.pager__sep { margin: 0 8px; color: var(--border-input); }
.pager__size { height: 26px; padding: 0 6px; border: 1px solid var(--border); border-radius: 6px; background: var(--card); font-family: inherit; font-size: 12px; }
.pager--full .pager__page { width: 32px; height: 32px; border-radius: 6px; font-size: 13px; }
.pager__num { display: inline-grid; place-items: center; width: 32px; height: 32px; border-radius: 6px; font-size: 13px; color: #4b5563; }
.pager__num:hover { background: var(--muted); text-decoration: none; }
.pager__gap { color: var(--faint); padding: 0 4px; }
/* La lista de adjuntos de una ficha: nombre, tamaño y la equis para quitarlo.
 * Cada fila es una línea entera para que el nombre largo no empuje al tamaño
 * fuera de la caja. */
/* El campo de archivo: el botón que dibuja el navegador se viste como los
 * botones del sitio, y el nombre del archivo se lee al lado. Sin esto es el
 * único control de un formulario que parece de otra aplicación.
 *
 * **El alto es automático y no los 40 px de los demás controles.** El botón lo
 * dibuja el navegador y mide lo que su tipografía mida —unos 27 px—; encerrado
 * en los 22 px que dejaban 40 de alto con 8 de relleno, salía recortado arriba
 * y abajo y se veía torcido dentro de la caja. Como el corte depende del
 * navegador, del sistema y del idioma del botón, se veía en unas pantallas y
 * en otras no: lo reportó Carlos el 11-09-2026 y así lo dijo. `min-height`
 * conserva la altura de los demás campos cuando el botón cabe de sobra. */
input[type="file"] {
  height: auto; min-height: 40px; padding: 5px 10px;
  font-family: inherit; font-size: 13px;
}
input[type="file"]::file-selector-button {
  margin-right: 10px; padding: 5px 12px; border: 1px solid var(--border);
  border-radius: 6px; background: var(--card); color: var(--foreground);
  font-family: inherit; font-size: 12.5px; font-weight: 500; cursor: pointer;
}
input[type="file"]::file-selector-button:hover { background: var(--muted); }
.filelist { list-style: none; margin: 4px 0 0; padding: 0; }
.filelist__item { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--border-soft); font-size: 13px; }
.filelist__item:last-child { border-bottom: 0; }
.filelist__item > a:first-child { display: inline-flex; align-items: center; gap: 8px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.filelist__size { margin-left: auto; color: var(--muted-foreground); font-size: 12px; }
.filelist__x { display: inline-grid; place-items: center; width: 24px; height: 24px; border-radius: 6px; color: var(--muted-foreground); }
.filelist__x:hover { background: var(--muted); color: var(--danger); }
.filelist__none { padding: 8px 0; color: var(--muted-foreground); font-size: 13px; }
.modal__lead { margin: 0 0 16px; font-size: 13px; color: var(--muted-foreground); }
.modal__grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px 20px; }
.modal__grid p { margin: 0; }
.modal__grid .label { font-size: 12px; color: var(--muted-foreground); }
.modal__grid .value { font-size: 14px; font-weight: 500; }
/* El dato que se viene a leer de la ficha, entre los que lo acompañan. */
.modal__grid .value--strong { font-weight: 700; }
.history { display: flex; flex-direction: column; gap: 8px; }
.history__row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 12px; border: 1px solid var(--border); border-radius: var(--radius-sm); }
.history__delta { font-size: 14px; font-weight: 600; }
.history__delta--minus { color: var(--danger-text); }
.history__delta--plus { color: var(--success-text); }
.history__who { font-size: 12px; color: var(--muted-foreground); }
.history__when { font-size: 12px; color: var(--faint); white-space: nowrap; }

.status-row { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.badge .icon { font-size: 12px; }
.concept__icon--warn { background: var(--warning-soft); color: var(--warning-text); }
.notice-card { border-color: #fde68a; background: #fffbeb; }
.notice-card .history__row { background: var(--card); }

/* ── Horarios: la cuadrícula semanal ─────────────────────────────────────────
 * Una celda por día con la entrada y la salida, en azul cuando sigue la
 * plantilla y en gris cuando es libre; el puntito de la esquina dice cómo le
 * fue contra las marcas. La semana se recorre con las flechas y «Hoy». */
.week-nav { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; flex-wrap: wrap; }
.week-nav__range { font-size: 14px; font-weight: 500; }
.week-nav__num { font-size: 12px; color: var(--muted-foreground); margin-left: 6px; }
.week-nav .filters--bare { margin: 0 0 0 auto; }
.legend { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 12px; font-size: 12px; color: var(--muted-foreground); }
.legend__item { display: inline-flex; align-items: center; gap: 6px; }
.legend__box { width: 12px; height: 12px; border-radius: 3px; }
.legend__box--tpl { background: #eff6ff; border: 1px solid #bfdbfe; }
.legend__box--free { background: var(--muted); border: 1px solid var(--border); }
.legend__dot { width: 8px; height: 8px; border-radius: 999px; }
/*
Un color por estado, y los mismos cuatro en la leyenda, en la celda y en el
detalle: coincide verde, discrepancia amarillo, pendiente naranja, sin marca
rojo. Estaban al revés —la discrepancia en rojo y la falta de marca en ámbar—,
que le daba el color más fuerte al problema más leve.
*/
.legend__dot--ok { background: #22c55e; }
.legend__dot--discrepancia { background: #eab308; }
.legend__dot--pendiente { background: #f97316; }
.legend__dot--sin_marca { background: #ef4444; }
/* Las tres primeras columnas de la cuadrícula, como en el prototipo: a la
   izquierda, sin partirse en dos líneas y con el nombre acotado para que las
   filas midan todas lo mismo. */
.grid-table .col-id {
  text-align: left; white-space: nowrap;
  font-family: var(--font-mono); font-size: 11.5px; color: var(--faint);
}
.grid-table .col-name { min-width: 180px; }
.grid-table td.col-name a {
  display: block; max-width: 220px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
.grid-table .col-area { white-space: nowrap; }
.grid-table th.is-today, .grid-table td.is-today { background: #fffbeb; }
.grid-table th.is-weekend { color: var(--faint); }
.grid-table .day-head { display: block; font-size: 11px; }
.grid-table .day-num { display: block; font-size: 12px; font-weight: 600; color: var(--foreground); }
.cell {
  position: relative; display: inline-block; min-width: 92px; padding: 6px 8px;
  border-radius: 6px; font-size: 12px; font-variant-numeric: tabular-nums; white-space: nowrap;
  background: #eff6ff; color: #1d4ed8; border: 1px solid #dbeafe;
}
.cell--free { background: var(--muted); color: var(--faint); border-color: var(--border); }
.cell__dot { position: absolute; top: -4px; right: -4px; width: 9px; height: 9px; border-radius: 999px; border: 2px solid #fff; }
.cell__dot--discrepancia { background: #eab308; }
.cell__dot--pendiente { background: #f97316; }
.cell__dot--sin_marca { background: #ef4444; }
/* El resumen de la fila. Es un enlace: abre la validación de esa persona. */
.issue-pill {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 8px; border: 1px solid #fde68a; border-radius: 999px;
  background: #fffbeb; color: #b45309; font-size: 12px; font-weight: 500;
}
a.issue-pill:hover { background: #fef3c7; color: #92400e; text-decoration: none; }
.ok-check {
  display: inline-flex; align-items: center; padding: 2px 8px;
  border: 1px solid #bbf7d0; border-radius: 999px;
  background: #f0fdf4; color: var(--success-text);
}
a.ok-check:hover { background: var(--success-soft); text-decoration: none; }

/* ── Planilla: cuadros de resumen y tarjetas de historial ─────────────────── */
.summary { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; margin-bottom: 16px; }
@media (max-width: 767px) { .summary { grid-template-columns: minmax(0, 1fr); } }
.summary__card { padding: 16px 20px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow-sm); }
.summary__card--highlight { background: #eff6ff; border-color: #dbeafe; }
.summary__label { margin: 0; font-size: 12px; color: var(--muted-foreground); }
.summary__value { margin: 4px 0 0; font-size: 22px; font-weight: 700; font-variant-numeric: tabular-nums; }
.summary__card--highlight .summary__value { color: #1d4ed8; }
.summary__sub { margin: 2px 0 0; font-size: 12px; color: var(--faint); }

/* Una celda suelta: la etiqueta arriba en gris y su valor debajo.
 *
 * **Hay que escribirlas.** Sin estas reglas, `.summary` solo repartía en tres
 * columnas y el navegador dibujaba el `dt` y el `dd` con sus valores de fábrica
 * —el `dd` con su sangría de 40 px—: cada valor caía a una distancia distinta
 * de su etiqueta, sin color que separara el rótulo del dato. Se veía
 * «desorganizado y en el aire» (Carlos, 07-09-2026), y estuvo así en el
 * tablero, el simulador de margen y el detalle de la promoción.
 *
 * Es la variante sin tarjeta de `.summary__card`, y por eso comparte con él el
 * tamaño y el gris de la etiqueta: son el mismo dato en dos pesos distintos.
 *
 * Lo que va **adentro** del valor —el porcentaje entre paréntesis, el detalle
 * de una cifra— vuelve al peso normal: es una acotación, no el dato. El
 * distintivo no hace falta nombrarlo, que trae el suyo.
 */
.summary > div { min-width: 0; }
.summary dt { margin: 0; font-size: 12px; color: var(--muted-foreground); }
.summary dd { margin: 3px 0 0; font-size: 14px; font-weight: 600; }
.summary dd .muted, .summary dd .small { font-weight: 400; }
/* Una sola columna, para el dato que no cabe repartido: la clave del
 * comprobante son cincuenta dígitos. */
.summary--una { grid-template-columns: minmax(0, 1fr); }
/* Que corte donde caiga y no se salga de la tarjeta. */
.clave { word-break: break-all; }

.stack-list { display: flex; flex-direction: column; gap: 10px; }
.hcard {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 16px 20px; border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--card); box-shadow: var(--shadow-sm); color: var(--foreground);
}
.hcard:hover { border-color: #bfdbfe; text-decoration: none; background: #fbfdff; }
.hcard__left { display: flex; align-items: center; gap: 14px; min-width: 0; }
.hcard__icon { display: grid; place-items: center; flex: 0 0 auto; width: 40px; height: 40px; border-radius: 10px; background: #eff6ff; color: #1d4ed8; font-size: 18px; }
.hcard__title { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 14px; font-weight: 600; }
.hcard__meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 4px; font-size: 12px; color: var(--muted-foreground); }
.hcard__meta .icon { font-size: 13px; }
.hcard__right { display: flex; align-items: center; gap: 12px; flex: 0 0 auto; text-align: right; }
.hcard__right .label { display: block; font-size: 11px; color: var(--muted-foreground); }
.hcard__right .value { display: block; font-size: 16px; font-weight: 700; font-variant-numeric: tabular-nums; }
.hcard__chevron { color: var(--faint); font-size: 18px; }
.table tfoot td { padding: 12px 16px; border-top: 2px solid var(--border); background: #f9fafb; font-weight: 600; }
.section-head { display: flex; align-items: center; gap: 8px; margin: 24px 0 10px; }
.section-head__bar { width: 4px; height: 18px; border-radius: 2px; background: var(--primary); }
.section-head__bar--red { background: var(--danger); }

/* Los botones de la cabecera de página van redondos, como en el prototipo,
 * y los desplegables de una opción de la barra de filtros son píldoras. */
.page-head__actions .btn { border-radius: var(--radius-pill); }
.filters--bare .input--choice, .filters--bare .input--date { height: 34px; border-radius: var(--radius-pill); font-size: 13px; }
.filters--bare .ts-wrapper { width: auto; }

/* ── Menos movimiento ────────────────────────────────────────────────────────
 * Quien lo pide en su sistema suele hacerlo por mareo o por sensibilidad al
 * movimiento, no por gusto. Las transiciones se apagan; nada más cambia.
 */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* ── Calendario ──────────────────────────────────────────────────────────────
 * flatpickr trae su maquetación —la rejilla de días, las flechas, el
 * desplegable del mes— y acá se le cambia la piel para que hable el mismo
 * idioma que el resto: los mismos colores, la misma tipografía, los mismos
 * radios. Lo que se toca es cómo se ve, no cómo funciona.
 *
 * El campo visible lo crea flatpickr (`.form-control.input`), así que hereda
 * los estilos de `.input` sin hacer nada.
 */
.flatpickr-calendar {
  font-family: var(--font-sans);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--card);
  box-shadow: 0 12px 32px -12px rgba(11, 17, 32, .35);
}
.flatpickr-calendar.arrowTop::before, .flatpickr-calendar.arrowTop::after,
.flatpickr-calendar.arrowBottom::before, .flatpickr-calendar.arrowBottom::after { display: none; }

.flatpickr-months { padding: 6px 4px 0; }
.flatpickr-months .flatpickr-month { color: var(--foreground); height: 38px; }
.flatpickr-current-month {
  font-family: var(--font-display); font-size: 14px; font-weight: 700;
  padding-top: 6px;
}
.flatpickr-current-month .flatpickr-monthDropdown-months { font-family: var(--font-display); font-weight: 700; }
.flatpickr-current-month .flatpickr-monthDropdown-months:hover { background: var(--muted); }
.flatpickr-current-month input.cur-year { font-family: var(--font-mono); font-weight: 500; }

.flatpickr-months .flatpickr-prev-month,
.flatpickr-months .flatpickr-next-month {
  padding: 10px; border-radius: 8px; fill: var(--muted-foreground);
}
.flatpickr-months .flatpickr-prev-month:hover svg,
.flatpickr-months .flatpickr-next-month:hover svg { fill: var(--primary); }

span.flatpickr-weekday {
  font-size: 10.5px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted-foreground);
}

.flatpickr-day {
  border-radius: 8px; font-size: 12.5px; color: var(--foreground);
  border-color: transparent;
}
.flatpickr-day:hover { background: var(--muted); border-color: transparent; }
.flatpickr-day.today { border-color: var(--border); font-weight: 600; }
.flatpickr-day.today:hover { background: var(--muted); }
.flatpickr-day.selected,
.flatpickr-day.selected:hover,
.flatpickr-day.startRange, .flatpickr-day.endRange {
  background: var(--primary); border-color: var(--primary); color: var(--primary-foreground);
  font-weight: 600;
}
.flatpickr-day.inRange { background: var(--primary-soft); border-color: var(--primary-soft); box-shadow: none; }
.flatpickr-day.prevMonthDay, .flatpickr-day.nextMonthDay { color: var(--muted-foreground); opacity: .5; }
.flatpickr-day.flatpickr-disabled, .flatpickr-day.flatpickr-disabled:hover {
  color: var(--muted-foreground); opacity: .3; background: none;
}

/* Pestañas de una ficha ----------------------------------------------------
   La línea de abajo va como sombra interior y no como `border-bottom`, y las
   pestañas no llevan margen negativo. Con `overflow-x: auto` el otro eje deja
   de ser `visible` y pasa a `auto`, así que un solo píxel asomando por abajo
   —el que hacía que el subrayado tapara la línea— convertía la barra en una
   caja desplazable y sacaba un desplazamiento horizontal en la página. La
   sombra pinta sin ocupar caja, así que el subrayado de 2 px queda encima sin
   que nada sobresalga. */
.tabs {
  display: flex;
  gap: 2px;
  margin: 0 0 16px;
  box-shadow: inset 0 -1px 0 var(--border);
  overflow-x: auto;
  overscroll-behavior-x: contain;
}
.tabs__link {
  padding: 10px 16px;
  font-size: 14px; font-weight: 500;
  color: var(--muted-foreground);
  white-space: nowrap;
  border-bottom: 2px solid transparent;
}
/* **Sin subrayado al pasar.** Los enlaces del sitio se subrayan —lo hace la
 * regla general de más arriba— y en una pestaña eso choca: la pestaña ya dice
 * dónde está uno con la línea de abajo, así que el subrayado agrega una segunda
 * raya que se lee como un error, sobre todo al lado de la pestaña abierta, que
 * lleva la suya de verdad. Lo que cambia al pasar es el color. */
.tabs__link:hover { color: var(--foreground); text-decoration: none; }
.tabs__link--on {
  color: var(--foreground);
  font-weight: 500;
  border-bottom-color: var(--foreground);
}

/* El panel que todavía viene en camino. Ocupa el sitio del contenido para que
   la página no salte cuando llegue, y lo dice con palabras: una pestaña
   marcada sobre un vacío se lee como que se rompió. */
.panel-loading {
  padding: 40px 20px; text-align: center;
  color: var(--muted-foreground); font-size: 13px;
}

/* Grupos dentro de un formulario ------------------------------------------- */
.form__section + .form__section { margin-top: 22px; }
.form__section > .form__section-title { grid-column: 1 / -1; }
.form__section-title {
  margin: 0 0 12px;
  padding-bottom: 6px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--muted-foreground);
  border-bottom: 1px solid var(--border);
}

/* Casillas múltiples dentro de un formulario -------------------------------
   Van en un `fieldset`, que trae borde y sangría propios del navegador: se le
   quitan para que se vea como cualquier otro campo. Django dibuja las opciones
   como una lista; sin esto salen con viñeta y en columna, ocupando media
   pantalla para cinco días de la semana. */
.field--checkboxselectmultiple,
.field--weekdaypicker, .field--weekpicker,
.field--pillgroup,
.field--pillpicker,
.field--radioselect { border: 0; padding: 0; margin: 0; min-width: 0; }
/* Menos los de abajo, que **sí llevan el margen de un campo**. La rejilla del
 * formulario no separa con `gap`: cada campo se separa del siguiente con su
 * propio `margin-bottom`, así que el `margin: 0` de arriba dejaba el grupo
 * pegado a lo que viniera después. Se veía en el alta del artículo: la ayuda
 * de «Maneja inventario» quedaba tocando el rótulo de «Notas».
 *
 * **Y en los permisos del rol**, con la misma causa y sin que nadie lo
 * relacionara: «Aprueba compras hasta» salía pegado a la última casilla y
 * parecía uno de los permisos (Carlos, 09-09-2026). Se arregló el de las
 * píldoras y se dejó este, que es el mismo caso.
 *
 * **Y los de radio**, que no tenían quien los usara hasta que aprobar un
 * crédito pasó a elegirse entre tres (Carlos, 11-09-2026): «Otros números»
 * quedaba tocando el rótulo de «Límite aprobado». Es el tercer caso de lo
 * mismo y por eso van los tres en la misma regla.
 *
 * **Y los días y las semanas** (Carlos, punto 11 del COMERCIAL.docx,
 * 19-09-2026: «dejar espacio en los campos de atiende desde y atiende
 * hasta»). En la ficha del cliente, «Atiende desde» y «Recibe desde» salían
 * pegados a las fichas de los días. Cuarto caso, y el que hizo falta para
 * cerrarlo: `test_form_grid` exige ahora que todo grupo al que la regla de
 * arriba le quita el margen lo recupere acá, así que el quinto no pasa. */
.field--pillgroup, .field--checkboxselectmultiple,
.field--radioselect, .field--pillpicker,
.field--weekdaypicker, .field--weekpicker { margin-bottom: 16px; }
.field--checkboxselectmultiple legend,
.field--weekdaypicker legend, .field--weekpicker legend,
.field--pillgroup legend,
.field--radioselect legend { padding: 0; }
.field--checkboxselectmultiple ul,
.field--radioselect ul {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 16px;
  margin: 4px 0 0;
  padding: 0;
}
.field--checkboxselectmultiple li label,
.field--radioselect li label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  font-weight: 400;
  color: var(--foreground);
  cursor: pointer;
}

/* La antigüedad de la cartera, con la forma del prototipo.
 *
 * Cada tramo es una píldora con su color, cuántos documentos, cuánto suman y
 * qué parte del total son, con una barra debajo. El color lo pone la fila en
 * `--tramo`: lo decide el servicio —ámbar lo que recién se atrasó, naranja lo
 * que ya preocupa, rojo lo que hay que ir a buscar— y así el mismo color va
 * en la píldora, en el monto y en la barra sin repetirlo tres veces acá.
 *
 * La barra se pinta con opacidad y no con un color más claro: así el tramo
 * sigue siendo el mismo color, más suave, y no hay tres tonos que mantener.
 *
 * `--tramo` va siempre con respaldo: no está declarada en esta hoja —la pone
 * la fila— y una variable que no llega hace que el navegador descarte la
 * declaración entera sin avisar. Con respaldo, el peor caso es un gris. */
.aging { display: flex; flex-direction: column; gap: 14px; margin-top: 14px; }
.aging__head {
  display: flex; align-items: center; gap: 10px; margin-bottom: 6px;
}
.aging__pill {
  padding: 2px 8px; border-radius: 5px;
  background: color-mix(in srgb, var(--tramo, var(--muted-foreground)) 12%, transparent);
  color: var(--tramo, var(--muted-foreground));
  font-size: 11px; font-weight: 700; white-space: nowrap;
}
.aging__count { font-size: 12px; color: var(--muted-foreground); }
/* El monto y su porcentaje cierran la fila por la derecha, como en la tabla
 * de al lado: los montos de la pantalla se leen todos por el mismo borde. */
.aging__amount { margin-left: auto; font-weight: 700; color: var(--tramo, var(--foreground)); }
.aging__share { font-size: 12px; color: var(--muted-foreground); min-width: 38px; text-align: right; }
.aging__bar {
  height: 5px; border-radius: var(--radius-pill);
  background: rgba(0, 0, 0, .05); overflow: hidden;
}
.aging__fill {
  display: block; height: 100%; border-radius: var(--radius-pill);
  background: var(--tramo, var(--muted-foreground)); opacity: .75;
}

/* ── Listas de selección ─────────────────────────────────────────────────────
 * Tom Select trae su maquetación —el desplegable, el resaltado, el recorte del
 * texto largo— y acá se le pone la piel del proyecto: los mismos bordes, la
 * misma altura y el mismo foco que cualquier otro campo, para que un `<select>`
 * y una casilla de texto no se vean como de dos aplicaciones distintas.
 *
 * El original queda escondido y su `id` viaja al que se ve, así que las reglas
 * de `.field select` ya no lo alcanzan: hay que repetir la forma acá. */
/* El hueco de la derecha, el de la flecha, lo fija Tom Select con
 * `!important` a partir de esta variable. Es la manera prevista de cambiarlo:
 * escribirle un `padding-right` encima no gana, y ganarle con otro
 * `!important` deja una pelea que el próximo que lea la hoja no entiende. */
.ts-wrapper { width: 100%; --ts-pr-caret: 28px; }
/* Tom Select le copia al envoltorio las clases del `<select>` original
 * —`input`, `input--choice`—, así que las reglas de los campos le pintan **su**
 * borde y su fondo encima de los que ya tiene el control de adentro: dos bordes
 * concéntricos pegados, que se ven como una línea doble.
 *
 * El envoltorio es solo la caja que ocupa el lugar; la apariencia la lleva el
 * control. */
.ts-wrapper.input {
  border: 0; background: none; padding: 0; height: auto; border-radius: 0;
}
.ts-wrapper.single .ts-control,
.ts-wrapper.single.input-active .ts-control {
  /* Alto fijo, el mismo de `.input`: es una sola línea siempre —el texto largo
   * se recorta, no envuelve— y con `min-height` quedaba dos píxeles más alto
   * que el campo de al lado, que en un formulario se ve como un renglón torcido. */
  height: 40px; padding: 0 0 0 12px;
  border: 1px solid var(--border-input); border-radius: var(--radius-sm);
  background: var(--card); color: var(--foreground);
  font-family: inherit; font-size: 14px;
  box-shadow: none;
}
.ts-wrapper.single .ts-control {
  display: flex; align-items: center; flex-wrap: nowrap; cursor: pointer;
}
/* El texto largo se recorta en vez de envolver: un nombre de cuarenta letras no
 * puede volver el campo de dos renglones y correr todo lo de abajo. */
.ts-wrapper.single .ts-control > .item {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* La caja de texto cede todo el espacio cuando hay algo elegido. La biblioteca
 * le reserva un ancho mínimo de 112 px, y con eso un nombre que cabía de sobra
 * salía cortado con puntos suspensivos al lado de un hueco vacío. No le hace
 * falta: mientras se escribe, lo elegido se esconde y la caja se queda con el
 * campo entero. */
.ts-wrapper.single .ts-control > input { flex: 1 1 0; min-width: 0; }
/* Mientras se **escribe**, lo elegido se esconde. Si no, la caja crece a dos
 * renglones —lo elegido arriba, lo que se busca abajo— y el formulario entero
 * da un salto en cuanto se pulsa la primera tecla.
 *
 * La clase la pone el guion al teclear, y no se usa `input-active`, que es la
 * que trae la biblioteca: esa significa «con el foco puesto», no «escribiendo».
 * Con ella, elegir a alguien dejaba el campo **en blanco** —el foco se queda
 * ahí después de elegir, así que el nombre nunca volvía a aparecer—. */
/* **Solo el de una opción.** En el que admite varios, esconder lo elegido
 * mientras se escribe vacía la caja entera: con seis productos escogidos el
 * control pasaba de 208 px a 58 px en cuanto se pulsaba una tecla, el
 * formulario pegaba un salto de siglo y medio de píxeles y quien escribía
 * perdía de vista lo que ya llevaba puesto (Carlos, 20-09-2026). Ahí las
 * fichas **son** el contenido del campo, no un rótulo que estorba. */
.ts-wrapper.single.escribiendo .ts-control > .item { display: none; }
/* La opción vacía es un rótulo, no una respuesta: se lee como el «Elegir» de
 * un campo sin llenar y no como algo que alguien decidió. */
.ts-wrapper .ts-control > .item[data-value=""] { color: var(--muted-foreground); }
/* El que admite varios.
 *
 * Tenía las fichas maquetadas pero **el control no**: fuera de la edición por
 * lotes y de las tablas de renglones nadie le daba borde, radio ni alto, así
 * que en un formulario salía con el borde gris del navegador y esquinas de
 * 3 px al lado de campos con las del sitio. Se veía como una caja rota, no
 * como un campo (Carlos, 20-09-2026).
 *
 * **El tope de alto es lo que sostiene el formulario.** Las fichas envuelven,
 * y sin él seis productos median 208 px: el campo de al lado quedaba con un
 * hueco de dos dedos y todo lo de abajo se corría. Con tope, lo que crece es
 * el scroll de adentro y la rejilla no se entera. Noventa y seis píxeles son
 * tres renglones de fichas, que es lo que se alcanza a leer de un vistazo. */
.ts-wrapper.multi .ts-control {
  display: flex; flex-wrap: wrap; align-content: flex-start; gap: 4px;
  min-height: 40px; max-height: 96px; overflow-y: auto;
  padding: 7px 10px;
  border: 1px solid var(--border-input); border-radius: var(--radius-sm);
  background: var(--card); color: var(--foreground);
  font-family: inherit; font-size: 14px;
  box-shadow: none; cursor: text;
}
/* **La caja de escribir va arriba, no al final.**
 *
 * Tom Select la pone después de las fichas, y ahí no se lee como un campo:
 * queda de último renglón bajo una pila de píldoras y parece la ayuda gris de
 * abajo. Carlos, que conoce el sistema, no dio con cómo escribir
 * (20-09-2026). Puesta primera y de ancho completo es lo primero que se ve al
 * abrir el campo, con su texto de ejemplo, y las fichas se leen por lo que
 * son: lo que ya se escogió.
 *
 * Se mueve con `order` y no en el marcado porque el marcado lo genera la
 * biblioteca. */
.ts-wrapper.multi .ts-control > input {
  order: -1; flex: 1 1 100%; min-width: 100%;
  margin: 0; padding: 0; height: 24px; font-size: 14px;
}
.ts-wrapper.multi .ts-control > input::placeholder { color: var(--muted-foreground); }
/* Se probó separar con una raya lo que se escribe de lo ya escogido y **no se
 * puede desde acá**: la biblioteca le pone `padding: 0 !important` a ese
 * input, así que la raya quedaba pegada al texto. Un `!important` encima para
 * ganarle a otro es una pelea que el próximo que lea esto no va a entender, y
 * el hueco entre el renglón de escribir y la primera ficha ya los separa. */
.ts-wrapper.focus .ts-control {
  border-color: var(--primary); box-shadow: 0 0 0 3px var(--ring);
}
.ts-wrapper.disabled .ts-control { background: var(--muted); opacity: .7; }

/* La flecha, dibujada acá y no heredada: la de la biblioteca es un borde
 * girado que no acompaña al resto de los iconos del sitio. */
.ts-wrapper.single .ts-control::after {
  content: ""; position: absolute; right: 12px; top: 50%;
  width: 8px; height: 8px; margin-top: -6px;
  border-right: 1.6px solid var(--muted-foreground);
  border-bottom: 1.6px solid var(--muted-foreground);
  transform: rotate(45deg); pointer-events: none;
}
.ts-wrapper.single.dropdown-active .ts-control::after {
  margin-top: -2px; transform: rotate(225deg);
}

.ts-dropdown {
  margin-top: 4px; padding: 4px;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--card); color: var(--foreground);
  box-shadow: 0 12px 28px -8px rgba(11, 17, 32, .35);
  font-size: 13.5px;
}
.ts-dropdown .option {
  padding: 8px 10px; border-radius: 8px; cursor: pointer;
}
.ts-dropdown .active { background: var(--muted); color: var(--foreground); }
.ts-dropdown .selected, .ts-dropdown .active.selected {
  background: var(--primary-soft); color: var(--primary); font-weight: 600;
}
.ts-dropdown .no-results { padding: 8px 10px; color: var(--muted-foreground); }
/* La caja de búsqueda del desplegable, cuando la hay. */
.ts-dropdown .dropdown-input-wrap { padding: 4px 4px 8px; }
.ts-dropdown .dropdown-input {
  height: 34px; padding: 0 10px;
  border: 1px solid var(--border); border-radius: 8px;
  background: var(--card); color: var(--foreground); font-family: inherit;
}
.ts-dropdown .dropdown-input:focus {
  border-color: var(--primary); box-shadow: 0 0 0 3px var(--ring);
}

/* En la barra de filtros los campos son más bajos y redondos que en un
 * formulario, y la lista tiene que seguirlos. */
.filters .ts-wrapper { width: auto; min-width: 150px; max-width: 220px; }
.filters .ts-wrapper.single .ts-control {
  min-height: 32px; height: 32px; border-radius: var(--radius-pill);
  font-size: 12.5px;
}

/* Dentro del modal, por encima de todo y sin que el cuerpo la recorte. */
dialog .ts-dropdown { z-index: 10; }

/* -- El lienzo de la firma ------------------------------------------------- */
/*
 * Donde se firma una letra de cambio con el dedo. El recuadro es alto a
 * propósito: una firma cabe en una línea, pero quien firma con el dedo necesita
 * sitio para el movimiento del brazo, y en una franja baja el trazo sale
 * apretado y no se parece al de la cédula.
 *
 * `touch-action: none` es lo que hace que el dedo dibuje en vez de desplazar la
 * pantalla. Sin él, el primer trazo mueve la página entera y el lienzo queda en
 * blanco, que fue exactamente lo que pasó al probarlo en el teléfono.
 */
.firma-caja {
  margin: 20px 0 0;
  padding: 12px;
  border: 1px dashed var(--border);
  border-radius: var(--radius-md);
  background: var(--muted);
}
.firma-caja__rotulo {
  display: block; margin-bottom: 8px;
  font-size: 12px; color: var(--muted-foreground);
}
.firma-caja__nota {
  margin: 10px 0 0;
  font-size: 12px; color: var(--muted-foreground);
}
.firma-lienzo {
  display: block; width: 100%; height: 200px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--card);
  touch-action: none; cursor: crosshair;
}
/* Con trazo puesto el borde se marca: es la señal de que ya hay algo que
   enviar, sin tener que mirar si el botón se encendió. */
.firma-lienzo--puesta { border-color: var(--primary); }

/* -- El logotipo de la empresa --------------------------------------------- */
/*
 * Cómo se ve el membrete en «Datos de la empresa». No es una miniatura suelta:
 * se enseña **como va a salir en el documento** —la imagen a la izquierda, la
 * razón social y la identificación a la derecha— porque lo que se está
 * decidiendo es el encabezado de una constancia, no un archivo.
 */
.logo-membrete {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 24px; padding: 20px;
  border-bottom: 2px solid var(--primary);
  margin: 0 20px 4px;
}
.logo-membrete__imagen { height: 54px; width: auto; max-width: 60%; }
.logo-membrete__quien { text-align: right; }
.logo-membrete__quien strong { display: block; font-size: 13px; font-weight: 600; }
.logo-membrete__quien span {
  display: block; margin-top: 2px;
  color: var(--muted-foreground); font-size: 12px;
}
/* Sin logotipo la línea del membrete sobra: lo que hay que leer es por qué no
   hay imagen, no un encabezado a medias. */
.logo-membrete:has(.logo-membrete__hueco) { border-bottom: 0; padding-bottom: 0; }
.logo-membrete__hueco p {
  margin: 0; color: var(--muted-foreground); font-size: 13px;
}

/* ── Las dos series del vendedor ─────────────────────────────────────────────
 *
 * Le pidió color Carlos el 13-09-2026. Las barras eran gris y azul, y el gris
 * se lee como un dato que falta y no como una serie: la columna más alta del
 * gráfico —lo facturado— parecía un hueco.
 *
 * **El color dice qué es cada una**, con el mismo significado que el resto de
 * los indicadores: azul lo facturado, que es lo pactado, y verde lo cobrado,
 * que es lo que entró. Y por eso lleva clave: dos barras de distinto color no
 * dicen nada sin ella.
 */
.serie__barra--facturado { background: #93c5fd; }
.serie__barra--cobrado { background: var(--success); }

.serie-clave { display: flex; gap: 14px; flex: none; }
.serie-clave__item {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12px; color: var(--muted-foreground);
}
.serie-punto {
  width: 10px; height: 10px; border-radius: 3px; flex: none;
}
.serie-punto--facturado { background: #93c5fd; }
.serie-punto--cobrado { background: var(--success); }

/* ── La foto del producto ────────────────────────────────────────────────────
 *
 * La pidió Carlos el 13-09-2026: quien toma un pedido delante de un cliente
 * reconoce el envase antes que el código.
 *
 * En la tarjeta va arriba y del ancho completo, con alto fijo: con alto libre,
 * una foto apaisada y una vertical dejan dos tarjetas de distinta altura y la
 * rejilla se ve rota. `contain` y no `cover` porque lo que importa es el
 * envase entero: recortarlo para llenar el recuadro le corta la etiqueta, que
 * es justo por donde se reconoce.
 */
.prodcard__foto {
  display: flex; align-items: center; justify-content: center;
  height: 96px; margin: -2px 0 2px;
  border-radius: var(--radius-sm); background: var(--muted);
  overflow: hidden;
}
.prodcard__foto img {
  width: 100%; height: 100%; object-fit: contain; display: block;
}
/* El hueco de la que todavía no tiene. Se reserva igual: con unas tarjetas
   con imagen y otras sin ella, dentro de una misma fila el nombre y el precio
   quedan a distinta altura y la rejilla se lee como rota. */
.prodcard__sinfoto { width: 26px; height: 26px; color: var(--border-input); }

/* En la ficha, la foto y sus datos lado a lado. */
.fotoficha {
  display: flex; align-items: flex-start; gap: 20px; padding: 20px;
}
.fotoficha__imagen {
  width: 220px; height: 220px; flex: none; object-fit: contain;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--muted);
}
.fotoficha__datos { flex: 1; min-width: 0; }
.fotoficha__datos .detail { padding: 0; }
/* Sin foto, el renglón se queda solo en la tarjeta y una franja fina pegada a
   los bordes se lee apretada dentro de una tarjeta de este tamaño. Lo llena el
   «todavía no hay nada» del sistema —`.empty`, la misma pieza que el convenio
   sin líneas y la escala sin tramos— en vez de un relleno propio: así el aire
   lo pone la medida que ya tienen todos los demás y no un número escrito acá
   (Carlos, 14-09-2026). */
.fotoficha .empty { flex: 1; }

@media (max-width: 700px) {
  .fotoficha { flex-direction: column; }
  .fotoficha__imagen { width: 100%; height: 200px; }
}

/* ── El aviso de lo que se muere ─────────────────────────────────────────── */
/* Una tira de tarjetas sobre la tabla de existencias, con la forma del
 * prototipo: lo que vence esta semana no es un dato de consulta sino algo que
 * hay que colocar antes de perderlo, y en una columna de la tabla se pierde
 * entre dos mil filas.
 *
 * Las cajas se reparten con `gap` y se envuelven solas: son entre una y seis,
 * y una rejilla de seis columnas dejaría cinco huecos el día que haya una. */
/* Naranja y no amarillo (auditoría 25-09-2026): el Figma y la captura 21
   usan `bg-orange-50`/`border-orange-200`, no el ámbar de `--warning-soft`
   que traía esta tarjeta. Es su propio color y no una redefinición de
   `--warning`, que el resto de la aplicación sigue usando igual. */
.aviso-vence {
  padding: 16px 20px;
  border-color: #fed7aa; background: #fff7ed;
}
.aviso-vence__titulo {
  display: flex; align-items: center; gap: 8px; margin: 0 0 12px;
  font-size: 13px; font-weight: 600; color: #7c2d12;
}
.aviso-vence__titulo .icon { color: #ea580c; }
.aviso-vence__resto { color: var(--muted-foreground); font-weight: 400; }
.aviso-vence__tira { display: flex; flex-wrap: wrap; gap: 10px; }
.aviso-vence__caja {
  display: block; flex: 1 1 260px; min-width: 0; padding: 10px 12px;
  border: 1px solid #fed7aa; border-radius: var(--radius-sm);
  background: var(--card); text-decoration: none;
}
.aviso-vence__caja:hover { border-color: #ea580c; }
.aviso-vence__caja .person__role {
  display: block; margin-top: 2px; color: #c2410c;
}
/* El «Gestionar» de la tarjeta: píldora naranja sólida, como la captura 21
   (`bg-orange-600 text-white`). */
.aviso-vence__btn { background: #ea580c; color: #fff; }
.aviso-vence__btn:hover { background: #c2410c; }

/* ── Los paneles de la pantalla de lotes ─────────────────────────────────── */
/* Van en la columna angosta de `board__row`, uno debajo del otro. No se les
 * pone `display: flex`: la separación entre los dos ya la pone `.card + .card`,
 * y volverlos un contenedor con `gap` obligaría a apagar ese margen a mano. */
.panel-lote__head {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px;
}
.panel-lote__sub { font-size: 12px; color: var(--muted-foreground); }
.panel-lote__vacio {
  margin: 12px 0 0; font-size: 13px; color: var(--muted-foreground);
}
.lote-paneles .detail { padding: 12px 0 0; }
/* La fecha no se parte en dos renglones: a dos tercios de ancho y con
 * ocho columnas, «12-09-2026» partido se lee como dos datos. */
.table td.fecha { white-space: nowrap; }

/* ── El lector de códigos ────────────────────────────────────────────────── */
/* El visor solo existe mientras la cámara está encendida. Se le fija una
 * proporción para que la ventana no salte al abrirlo y volver a cerrarse. */
.escaner__visor {
  margin: 10px 0 0; border: 1px solid var(--border);
  border-radius: var(--radius-sm); overflow: hidden; background: #000;
}
/* Los dos nacen escondidos y los enciende el guion. Sin esto, el `display` que
 * fija el botón —y el que fija el vídeo de adentro del visor— le gana al
 * `hidden` del navegador, y la cámara se quedaría dibujada apagada. Es la
 * quinta vez que pasa en el proyecto; la cuida `core/tests/test_ocultar.py`. */
.escaner__visor[hidden] { display: none !important; }
.btn[hidden] { display: none !important; }
.escaner__visor video {
  display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover;
}
.escaner__pista {
  margin: 0; padding: 6px 10px; font-size: 12px;
  color: var(--card); background: rgba(0, 0, 0, .55);
}
.escaner__aviso {
  margin: 8px 0 0; font-size: 13px; color: var(--warning-text);
}

/* ── El panel de alisto ──────────────────────────────────────────────────── */
/* La lista de lo que hay que juntar, en la columna angosta de `board__row`.
 * Cada renglón es un artículo: a la izquierda qué es y de qué estante, a la
 * derecha cuánto. La cantidad va grande y a la derecha porque es el único
 * número que se compara contra lo que uno lleva en la mano. */
.panel-alisto__head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 10px;
}
.panel-alisto__head .badge { flex: none; }
/* El cliente, en 16 px como el `text-base` de la V4 (`BodegaPage.tsx`,
 * panel de Alisto), pegado al ID de arriba. */
.panel-alisto__cliente { display: block; margin-top: 2px; font-size: 16px; }
/* El ID de la orden de alisto, arriba del cliente y en gris monoespaciado —
 * calco de la captura del punto 6 (Carlos, 26-09-2026)—. */
.panel-alisto__id {
  margin: 0; font-family: var(--font-mono); font-size: 12px;
  color: var(--faint);
}
.panel-alisto__sub {
  margin: 2px 0 0; font-size: 12px; color: var(--muted-foreground);
}
/* Las dos tarjetas grises del panel: Alistador y Duración. */
/* Medidas y tonos de la V4: gris 50 de fondo (el mismo #f9fafb que se lee
 * en `image7.png`), rótulo gris 400 y valor de 14 px en seminegrita. */
.panel-alisto__tarjetas {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px; margin: 16px 0;
}
.panel-alisto__tarjeta {
  min-width: 0; padding: 10px 12px;
  border-radius: var(--radius-sm); background: #f9fafb;
}
.panel-alisto__tarjeta-etiqueta {
  margin: 0; font-size: 10px; color: var(--faint);
}
.panel-alisto__tarjeta-valor {
  margin: 2px 0 0; font-size: 14px; font-weight: 600; color: #1e2939;
  overflow-wrap: anywhere;
}
/* El aviso de pausa del panel: un recuadro ámbar de una columna. `.notice`
 * reparte sus hijos en fila y en la columna angosta partía «En pausa.» en
 * dos renglones al lado del motivo (Carlos, 28-09-2026). */
.panel-alisto__pausa {
  margin: 0 0 16px; padding: 10px 12px;
  border: 1px solid #fef9c2; border-radius: var(--radius-sm);
  background: var(--warning-soft); color: var(--warning-text); font-size: 12px;
}
.panel-alisto__pausa-desde { margin-top: 2px; color: var(--muted-foreground); }
/* «Asignar» dentro de la tarjeta Alistador (punto 6, Carlos, 27-09-2026): la
 * lista ya no ofrece el enlace en su celda —muestra «—», como la captura—,
 * así que el único sitio para asignar es acá. */
.panel-alisto__tarjeta-valor--link {
  display: block; color: var(--primary); text-decoration: none; cursor: pointer;
}
.panel-alisto__tarjeta-valor--link:hover { color: var(--primary-text); }
/* El ID en la lista, en negrita —a diferencia del panel, que lo pinta
 * apagado—: en la tabla es lo que identifica la fila. */
.alisto-lista__id {
  font-family: var(--font-mono); font-weight: 700; font-size: 12.5px;
}

/* ── La tabla de Alisto ──────────────────────────────────────────────────
 *
 * Once columnas con el panel abierto al lado (captura del punto 6, Carlos,
 * 27-09-2026). El relleno de siempre —`.table tbody td { padding: 14px
 * 20px }`— mide bien para una tabla que se lee sola; con el panel ocupando
 * un tercio de la tarjeta, «Estado» y «Ver» quedaban fuera y solo se veían
 * corriendo la barra. La captura los trae siempre visibles, así que acá se
 * aprieta: el ID y el pedido parten después del guion en dos renglones, el
 * cliente se recorta con puntos suspensivos y el alistador envuelve por
 * nombre y apellido —los tres son los que sobran ancho si no se acotan—.
 *
 * **Con el ancho exacto, no `auto`.** Con `table-layout: fixed`, `width:
 * auto` no dice «mide lo que ocupan tus columnas»: la especificación manda
 * usar el mayor entre eso y el ancho del contenedor, así que la tabla
 * igual se estiraba a llenar la tarjeta y el cliente se comía el sobrante.
 * El número de acá es la suma exacta de las once columnas de abajo.
 */
.table--alisto { table-layout: fixed; width: 732px; }
.table--alisto thead th, .table--alisto tbody td {
  padding-left: 8px; padding-right: 8px;
}
.table--alisto thead th:nth-child(1),
.table--alisto tbody td:nth-child(1) { width: 44px; }  /* ID */
.table--alisto thead th:nth-child(2),
.table--alisto tbody td:nth-child(2) { width: 50px; }  /* Pedido */
.table--alisto thead th:nth-child(3),
.table--alisto tbody td:nth-child(3) { width: 100px; } /* Cliente */
.table--alisto thead th:nth-child(4),
.table--alisto tbody td:nth-child(4) { width: 36px; }  /* Items */
.table--alisto thead th:nth-child(5),
.table--alisto tbody td:nth-child(5) { width: 42px; }  /* Uds. */
.table--alisto thead th:nth-child(6),
.table--alisto tbody td:nth-child(6) { width: 70px; }  /* Prioridad */
.table--alisto thead th:nth-child(7),
.table--alisto tbody td:nth-child(7) { width: 66px; }  /* Alistador */
.table--alisto thead th:nth-child(8),
.table--alisto tbody td:nth-child(8) { width: 44px; }  /* Inicio */
.table--alisto thead th:nth-child(9),
.table--alisto tbody td:nth-child(9) { width: 56px; }  /* Duración */
.table--alisto thead th:nth-child(10),
.table--alisto tbody td:nth-child(10) { width: 92px; } /* Estado */
.table--alisto thead th:nth-child(11),
.table--alisto tbody td:nth-child(11) { width: 132px; } /* Ver / Alistar */
.table--alisto .alisto-lista__id,
.table--alisto .alisto-lista__pedido {
  display: inline-block; white-space: normal; word-break: break-word;
}
.table--alisto .alisto-lista__cliente {
  display: block; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap;
}
.table--alisto .alisto-lista__alistador { white-space: normal; }
/* La fila cuya orden está abierta en el panel de la derecha (detalle 3,
 * auditoría de Alisto, 27-09-2026, `image7.png`: ALI-0081 resaltada). */
.table tbody tr.alisto-lista__row--active { background: var(--primary-soft); }
.table tbody tr.alisto-lista__row--active:hover { background: var(--primary-soft); }
/* «Completadas» y «En proceso» de «Por alistador», con el punto de color de
 * la V4 (`BodegaPage.tsx:1581-1590`, auditoría de Alisto, 27-09-2026): el
 * texto va del mismo tono que el punto, en negrita, no en la fuente
 * monoespaciada de `.num` —esas dos celdas no son una tabla de cifras que
 * se compare en columna, son un estado con un número adentro—. */
.alisto-alistadores__cifra {
  display: inline-flex; align-items: center; gap: 6px; font-weight: 600;
}
.alisto-alistadores__cifra--verde { color: #15803d; }
.alisto-alistadores__cifra--azul { color: var(--primary-text); }
.panel-alisto__rotulo {
  margin: 0 0 8px; font-size: 12px; font-weight: 600; color: #364153;
}
.panel-alisto__vacio {
  margin: 12px 0 0; font-size: 13px; color: var(--muted-foreground);
}
.alisto { display: flex; flex-direction: column; gap: 6px; }
/* **El renglón, como la V4** (Carlos, 28-09-2026, `carlos_123.png`): el
 * artículo ocupa todo lo que sobra a la izquierda y a la derecha van juntos
 * el contador, el menú y el punto. Con `space-between` y cuatro hijos, el
 * navegador repartía el hueco entre todos y el campo, el «/ 3», los tres
 * puntos y el punto de color quedaban sueltos a lo largo de la fila. */
.alisto__item {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 12px; border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.alisto__que { flex: 1 1 auto; min-width: 0; }
.alisto__que .person__role { display: block; margin-top: 1px; }
/* El nombre en un renglón, cortado con puntos suspensivos como el
 * `truncate` de la V4; entero en el `title`. En el teléfono envuelve: ahí
 * no hay puntero para leer el `title` y el recorte se comería media
 * descripción. */
.alisto__nombre {
  display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.alisto__nombre:hover { color: var(--foreground); }
@media (max-width: 640px) {
  .alisto__nombre { white-space: normal; overflow-wrap: anywhere; }
}
.alisto__item .rowmenu { flex: none; }
.alisto__cuanto {
  flex: none; font-size: 13px; font-weight: 700; color: var(--foreground);
  font-variant-numeric: tabular-nums;
}
.alisto__cuanto .alisto__meta { font-weight: 400; }
/* Lo que no tiene estante se señala: es lo que hay que ir a preguntar, y
 * dejarlo igual que el resto lo esconde justo al final de la lista. */
.alisto__sinsitio { color: var(--warning-text); }

/* El contador de cada renglón del alisto: lo juntado sobre lo pedido. El
 * campo va angosto —son dos o tres dígitos— y el «/ 12» apagado al lado,
 * porque lo que cambia es el primero. */
/* El número va en la letra del resto de la pantalla, no en la
 * monoespaciada, y sin las flechas del navegador: en 56 px se comían el
 * dígito. Se escribe igual y el teclado del teléfono sigue siendo el
 * numérico. */
.alisto__contador { display: flex; align-items: center; gap: 4px; flex: none; }
.alisto__campo {
  width: 56px; height: 28px; padding: 0 8px; text-align: right;
  border: 1px solid var(--border-input); border-radius: 8px;
  background: var(--card); color: var(--foreground);
  font-family: inherit; font-size: 13px; font-weight: 600;
  font-variant-numeric: tabular-nums;
  -moz-appearance: textfield; appearance: textfield;
}
.alisto__campo::-webkit-outer-spin-button,
.alisto__campo::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.alisto__meta { font-size: 12px; color: var(--faint); font-variant-numeric: tabular-nums; }
/* Con ancho mínimo, para que los campos de todos los renglones caigan en la
 * misma columna aunque uno diga «/2» y otro «/48». */
.alisto__contador .alisto__meta { min-width: 30px; }
/* **El color del renglón, como en la captura del punto 6** (Carlos,
 * 26-09-2026): verde lo cubierto —alistado o sustituido—, ámbar lo que
 * todavía se está juntando y rojo lo que se dio formalmente por faltante.
 * Los mismos tonos suaves que ya usan los distintivos de estado. */
/* Los tonos exactos de la V4 —fondo 50 al 50 % y borde 100— que son los que
 * se miden en `image7.png` (#f9fefa, #fefef5, #fef9f9). Los `-soft` del
 * sistema, que se usaban antes, son el tono 100 entero y cargaban la lista. */
.alisto__item--completo { background: #f8fefa; border-color: #dcfce7; }
.alisto__item--parcial  { background: #fffef4; border-color: #fef9c2; }
.alisto__item--falta    { background: #fff9f9; border-color: #ffe2e2; }
/* El punto de color al final del renglón, que repite el tono del fondo para
 * quien no distingue el verde del amarillo. */
.alisto__punto { width: 8px; height: 8px; border-radius: var(--radius-pill); flex: none; }
.alisto__punto--completo { background: #00c950; }
.alisto__punto--parcial  { background: #fdc700; }
.alisto__punto--falta    { background: #fb2c36; }
.alisto__lote { display: block; color: var(--muted-foreground); }
.alisto__cerrar { margin-top: 16px; display: flex; justify-content: flex-end; }
/* «Marcar como listo» e «Iniciar alisto» van a todo lo ancho, calco de la
 * captura del punto 6 (Carlos, 26-09-2026): son el gesto que cierra el paso,
 * y no compiten por espacio con nada más en esa línea. */
.alisto__cerrar--ancho { justify-content: stretch; }
.alisto__cerrar--ancho .btn { width: 100%; height: 40px; justify-content: center; }
/* «Dejar en pausa» / «Retomar el alisto» y «Cancelar alisto», debajo del
 * botón verde en dos mitades iguales con el texto al centro (Carlos,
 * 28-09-2026). La V4 no los trae; si solo hay uno, ocupa la fila. */
.alisto__acciones {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr);
  gap: 8px; margin-top: 8px;
}
.alisto + .alisto__acciones { margin-top: 16px; }
.alisto__acciones > * { min-width: 0; margin: 0; }
.alisto__acciones .btn { width: 100%; padding: 0 10px; justify-content: center; }

/* Cinco indicadores en fila, que es lo que pide la pantalla de alisto: cuatro
 * estados y el tiempo. Con la rejilla de cuatro, el quinto se quedaba solo en
 * la segunda fila y se leía como si sobrara. */
@media (min-width: 1100px) {
  .kpis--cinco { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}
/* La unidad va pegada al número y más chica: «31 min» es un dato, no dos. */
.kpi__unidad { font-size: 14px; font-weight: 500; color: var(--muted-foreground); }

/* El avance de una hoja de ruta: cuántas paradas se atendieron de las que
 * lleva. No es `.mix` —esa reparte un 100 % entre tramos que suman— sino un
 * relleno contra un riel, que es lo que pide una cuenta parcial. Va angosto
 * porque vive dentro de una celda, con el porcentaje al lado: la barra se ve
 * de un vistazo y el número es el que se lee cuando dos hojas van parecidas. */
.avance { display: flex; align-items: center; gap: 8px; }
.avance__riel {
  width: 80px; height: 6px; flex: none;
  border-radius: var(--radius-pill); background: var(--muted); overflow: hidden;
}
.avance__parte { display: block; height: 100%; background: var(--primary); }
/* La que ya cerró deja de ser «lo que falta» y pasa a ser «lo que está». */
.avance__parte--completo { background: var(--success-text); }
.avance__pct {
  font-size: 12px; color: var(--muted-foreground);
  font-variant-numeric: tabular-nums;
}

/* Los gráficos de los indicadores.
 *
 * El lienzo lleva alto fijo porque Chart.js con `maintainAspectRatio: false`
 * toma el del contenedor, y un contenedor que se mide por su contenido —el
 * lienzo— no tiene alto hasta que lo tenga el lienzo: sin esto el gráfico
 * crece sin parar en cada repintado. Es el error clásico de la librería.
 *
 * La tabla de abajo no se esconde cuando el dibujo carga: en un tablero la
 * cifra exacta es tan útil como la forma, y esconderla obliga a pasar el
 * puntero por cada barra para leerla. */
.grafico-caja__head { margin-bottom: 14px; }
.grafico-caja__head h2 { margin: 0; }
.grafico-caja__pie { margin: 2px 0 0; font-size: 13px; color: var(--muted-foreground); }
/* El pie a la derecha del título, en la misma línea —«Últimos 30 días» de
 * la evolución de stock, calco de la V4 (auditoría 25-09-2026)—. Modificador
 * de `grafico.pie_inline`, que solo lo pide esa pantalla; el resto sigue
 * con el pie debajo. */
.grafico-caja__head--fila { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.grafico-caja__head--fila .grafico-caja__pie { margin: 0; }
/* «Mínimo» (`grafico.minimal`, auditoría 27-09-2026): el título chico en
 * semibold y el dibujo de 180 px de la ficha del artículo, calco de la V4
 * (`text-sm font-semibold`, `height={180}`). Solo lo pide esa pantalla. */
.grafico-caja--minimal .grafico-caja__head { margin-bottom: 16px; }
.grafico-caja--minimal .grafico-caja__head h2 { font-size: 14px; font-weight: 600; }
.grafico-caja--minimal .grafico-caja__pie { font-size: 12px; color: var(--faint); }
.grafico-caja--minimal .grafico { height: 180px; margin-bottom: 0; }
.grafico { position: relative; height: 260px; margin-bottom: 18px; }
.grafico canvas { max-width: 100%; }
.grafico-caja__tabla { margin: 0 -20px -20px; }
@media (max-width: 720px) {
  .grafico { height: 220px; }
}

/* El deslizador de la simulación. Va del ancho de su columna, con su número
 * a la par y los topes debajo, como el resto de los controles del formulario. */
.rango { display: flex; flex-direction: column; gap: 4px; }
.rango__fila { display: flex; align-items: center; gap: 10px; }
.rango__control { flex: 1; accent-color: var(--primary); }
.rango__valor {
  min-width: 56px; text-align: right; font-weight: 600; color: var(--primary);
  font-variant-numeric: tabular-nums;
}
.rango__topes {
  display: flex; justify-content: space-between;
  font-size: 12px; color: var(--muted-foreground);
}

/*
 * Lo que se esconde a la vista y no del oído.
 *
 * La tabla que acompaña a cada gráfico se oculta cuando el dibujo logra
 * cargar —el número queda sobre cada barra— pero no se borra: quien usa un
 * lector de pantalla no ve el gráfico, y sin la tabla se quedaría sin los
 * datos. `display: none` la sacaría también del lector, que es justo lo que
 * no se quiere.
 */
.solo-para-lectores {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}

/* ── Desempeño comercial ─────────────────────────────────────────────────────
 *
 * Los indicadores de Gestión comercial, copiados del prototipo V4
 * (`ComercialIndicadoresView` en `ComercialPage.tsx`) a pedido de Carlos el
 * 22-09-2026: «idéntica al del prototipo de Figma».
 *
 * **Las medidas son las suyas, tomadas del navegador**, no a ojo: los
 * tamaños, los radios —allá `rounded-lg` son 14 px y `rounded-xl` 18, porque
 * su tema los estira— y los colores. Donde el prototipo usa un color del tema
 * (el azul principal) va el token de SigoOne; donde usa uno de la paleta de
 * Tailwind (esmeralda, ámbar, violeta, cian, índigo, los grises) va el valor
 * medido, que es el suyo y no uno parecido.
 *
 * No se usa `.card`: su `.card + .card` le pondría margen a la segunda caja
 * de cada fila y las descuadraría (ver `test_las_tarjetas_cuadran`). Las
 * filas separan con `gap`.
 */
.desempeno {
  display: flex; flex-direction: column; gap: 20px;
  padding-bottom: 32px;
  color: var(--foreground);
}
.desempeno h2 { margin: 0; font-size: 16px; line-height: 24px; font-weight: 600; color: var(--foreground); }

/* Las cajas: blancas, filo gris, esquina de 16 y la sombra corta del
 * prototipo (`shadow-sm` de Tailwind 4, que son dos capas). */
.desempeno__hero,
.desempeno__cifras,
.desempeno__caja {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 16px;
  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .1), 0 1px 2px -1px rgba(0, 0, 0, .1);
}
.desempeno__caja { padding: 24px; min-width: 0; }

/* ── La cabecera */
/* **Sin `overflow: hidden`**, aunque el prototipo lo tenga: acá cuelga de la
 * cabecera el desplegable de canales, y la caja lo cortaba a la mitad (Carlos,
 * 22-09-2026, «el menú está solapado»). Allá no pasa porque su filtro es de
 * adorno y no abre nada. El degradado lleva sus propias esquinas para no
 * salirse de la caja, que era lo único que el recorte hacía. */
.desempeno__hero { border-color: #dbeafe; }
.desempeno__hero-top {
  display: flex; flex-direction: column; gap: 20px;
  padding: 20px 24px;
  border-radius: 15px 15px 0 0;
  border-bottom: 1px solid #dbeafe;
  background: linear-gradient(to right, var(--primary-soft) 0%, #fff 50%, #fff 100%);
}
.desempeno__eyebrow {
  margin: 0;
  display: flex; align-items: center; gap: 8px;
  font-size: 11px; line-height: 16.5px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .16em;
  color: var(--primary-text);
}
.desempeno__punto {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--primary);
  box-shadow: 0 0 0 4px rgba(37, 99, 235, .10);
}
.desempeno__titulo {
  margin: 12px 0 0;
  font-size: 24px; line-height: 32px; font-weight: 700;
  letter-spacing: -.02em; color: #030712;
}
.desempeno__corte {
  display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px;
  margin: 8px 0 0;
  font-size: 14px; line-height: 20px; color: var(--muted-foreground);
}
.desempeno__sep { color: #d1d5dc; }
.desempeno__vivo {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12px; line-height: 16px; color: #007a55;
}
.desempeno__vivo i { width: 6px; height: 6px; border-radius: 50%; background: #00bc7d; }

.desempeno__controles { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.desempeno__segmento {
  display: flex; padding: 4px;
  background: #fff; border: 1px solid var(--border); border-radius: 18px;
  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .1), 0 1px 2px -1px rgba(0, 0, 0, .1);
}
.desempeno__opcion {
  padding: 6px 12px; border-radius: 14px;
  font-size: 12px; line-height: 16px; font-weight: 600;
  color: var(--muted-foreground); text-decoration: none;
}
.desempeno__opcion:hover { color: var(--foreground); }
.desempeno__opcion--activa,
.desempeno__opcion--activa:hover {
  background: var(--primary); color: var(--primary-foreground);
  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .1), 0 1px 2px -1px rgba(0, 0, 0, .1);
}
.desempeno__canal > summary {
  display: flex; align-items: center; gap: 6px;
  padding: 8px 10px;
  background: #fff; border: 1px solid var(--border); border-radius: 18px;
  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .1), 0 1px 2px -1px rgba(0, 0, 0, .1);
  font-size: 12px; line-height: 16px; font-weight: 600; color: #364153;
}
.desempeno__canal > summary .icon { font-size: 14px; color: var(--faint); }
.desempeno__canal .fdrop__options { padding: 6px; }
.desempeno__canal .fdrop__option { color: var(--foreground); text-decoration: none; }
.desempeno__canal-elegido { font-weight: 600; background: var(--primary-soft); }

.desempeno__franja {
  display: grid; gap: 12px;
  padding: 14px 20px;
  font-size: 12px; line-height: 16px; color: var(--foreground-soft);
}
.desempeno__dato { display: flex; align-items: center; gap: 8px; }
.desempeno__dato strong { font-weight: 600; color: var(--foreground); }

/* El cuadrito de color con su icono: el mismo en la franja y en las cifras. */
.desempeno__ficha {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 24px; height: 24px; border-radius: 14px;
}
.desempeno__ficha .icon { font-size: 14px; }
.desempeno__ficha--grande { width: 32px; height: 32px; }
.desempeno__ficha--grande .icon { font-size: 16px; }
.desempeno__ficha--azul { background: var(--primary-soft); color: var(--primary); }
.desempeno__ficha--verde { background: #ecfdf5; color: #009966; }
.desempeno__ficha--violeta { background: #f5f3ff; color: #7f22fe; }
.desempeno__ficha--cian { background: #ecfeff; color: #0092b8; }
.desempeno__ficha--ambar { background: #fffbeb; color: #e17100; }
.desempeno__ficha--gris { background: var(--muted); color: var(--faint); }

/* ── Las cuatro cifras: una caja con divisiones, no cuatro tarjetas */
.desempeno__cifras {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  overflow: hidden;
}
.desempeno__cifra { padding: 16px; border-color: var(--border-soft); border-style: solid; border-width: 0; }
.desempeno__cifra:nth-child(even) { border-left-width: 1px; }
.desempeno__cifra:nth-child(n+3) { border-top-width: 1px; }
.desempeno__cifra-cabeza { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.desempeno__rotulo {
  margin: 0;
  font-size: 11px; line-height: 16.5px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .08em; color: var(--faint);
}
.desempeno__valor {
  margin: 8px 0 0;
  font-size: 24px; line-height: 32px; font-weight: 700; letter-spacing: -.03em;
  color: var(--foreground); font-variant-numeric: tabular-nums;
}
.desempeno__valor--vacio { color: var(--faint); }
.desempeno__valor--bien { color: #007a55; }
.desempeno__valor--lejos { color: #bb4d00; }
.desempeno__nota { margin: 4px 0 0; font-size: 11px; line-height: 16.5px; color: var(--muted-foreground); }

/* ── Las dos filas de abajo */
.desempeno__fila { display: grid; gap: 20px; }
.desempeno__caja-cabeza { display: flex; flex-direction: column; gap: 12px; }
/* Con un icono a la derecha la cabecera va en fila también en el teléfono:
 * apilado, el icono quedaría suelto debajo del título. */
.desempeno__caja-cabeza--fila { flex-direction: row; align-items: flex-start; justify-content: space-between; }
.desempeno__caja-cabeza--medio { align-items: center; }
.desempeno__titulo-caja { display: flex; align-items: center; gap: 8px; }
.desempeno__chip {
  padding: 2px 8px; border-radius: 999px;
  background: var(--primary-soft); color: var(--primary-text);
  font-size: 11px; line-height: 16.5px; font-weight: 500;
}
.desempeno__bajada { margin: 4px 0 0; font-size: 14px; line-height: 20px; color: var(--muted-foreground); }
.desempeno__leyenda { display: flex; align-items: center; gap: 12px; font-size: 12px; line-height: 16px; color: var(--muted-foreground); }
.desempeno__leyenda span { display: inline-flex; align-items: center; gap: 6px; }
.desempeno__muestra { width: 8px; height: 8px; border-radius: 50%; }
.desempeno__muestra--ventas { background: var(--primary); }
.desempeno__muestra--meta { background: #dbeafe; }

/* El gráfico: seis columnas de 224 px con el monto arriba y el mes abajo,
 * las dos cosas colgadas de la columna y no de la barra, como allá. */
.desempeno__grafico {
  display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); align-items: end; gap: 12px;
  height: 224px; margin-top: 24px; padding: 0 8px 28px;
  border-bottom: 1px solid var(--border-soft);
}
.desempeno__mes {
  position: relative; height: 100%; min-width: 0;
  display: flex; align-items: flex-end; justify-content: center; gap: 4px;
}
.desempeno__monto {
  position: absolute; top: -20px;
  font-size: 10px; line-height: 15px; font-weight: 600; color: var(--foreground-soft);
  white-space: nowrap; font-variant-numeric: tabular-nums;
}
.desempeno__barra {
  flex: 1; max-width: 32px;
  border-radius: 12px 12px 0 0;
  background: var(--primary-bright);
}
.desempeno__barra--meta { background: #dbeafe; }
.desempeno__barra--ultima { background: var(--primary); }
.desempeno__mes-rotulo {
  position: absolute; bottom: -24px;
  font-size: 11px; line-height: 16.5px; color: var(--faint);
}
.desempeno__pie { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); margin-top: 28px; }
.desempeno__pie > div { padding: 0 12px; }
.desempeno__pie > div:first-child { padding-left: 0; }
.desempeno__pie > div + div { border-left: 1px solid var(--border-soft); }
.desempeno__pie p:first-child { margin: 0; font-size: 12px; line-height: 16px; color: var(--faint); }
.desempeno__pie p + p {
  margin: 4px 0 0;
  font-size: 14px; line-height: 20px; font-weight: 600; color: var(--foreground);
  font-variant-numeric: tabular-nums;
}
.desempeno__pie .desempeno__bueno { color: #007a55; }

/* ── Prioridades */
.desempeno__prioridades { display: flex; flex-direction: column; gap: 12px; margin-top: 20px; }
.desempeno__prioridad {
  display: flex; gap: 8px; padding: 12px;
  border: 1px solid var(--border-soft); border-radius: 18px;
  background: #fff; color: inherit; text-decoration: none;
}
.desempeno__prioridad:hover { border-color: #dbeafe; }
.desempeno__prioridad--activa { border-color: #8ec5ff; background: rgba(239, 246, 255, .5); }
.desempeno__prioridad strong { display: block; font-size: 12px; line-height: 20px; font-weight: 600; color: var(--foreground); }
.desempeno__prioridad small { display: block; margin-top: 2px; font-size: 11px; line-height: 16px; color: var(--muted-foreground); }
.desempeno__nivel {
  flex: none; align-self: flex-start; margin-top: 2px;
  padding: 2px 6px; border: 1px solid; border-radius: 12px;
  font-size: 10px; line-height: 15px; font-weight: 700;
}
.desempeno__nivel--atencion { background: #fffbeb; color: #973c00; border-color: #fef3c6; }
.desempeno__nivel--proximo { background: var(--primary-soft); color: #193cb8; border-color: #dbeafe; }
.desempeno__nivel--critico { background: #fef2f2; color: #9f0712; border-color: var(--danger-soft); }
.desempeno__vacio { margin: 0; padding: 24px 0; font-size: 13px; color: var(--muted-foreground); text-align: center; }

/* ── Cumplimiento por vendedor */
.desempeno__caja--tabla { padding: 0; overflow: hidden; }
.desempeno__caja-cabeza--tabla { padding: 16px 20px; border-bottom: 1px solid var(--border-soft); }
.desempeno__aparte { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; line-height: 16px; color: var(--muted-foreground); }
.desempeno__aparte .icon { font-size: 14px; }
.desempeno__tabla { overflow-x: auto; }
.desempeno__tabla table { width: 100%; min-width: 620px; border-collapse: collapse; font-size: 14px; line-height: 20px; }
.desempeno__tabla thead tr { background: rgba(249, 250, 251, .7); }
.desempeno__tabla th {
  padding: 12px 20px; text-align: left;
  font-size: 11px; line-height: 16.5px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .08em; color: var(--faint);
}
.desempeno__tabla td { padding: 14px 20px; border-top: 1px solid var(--border-soft); }
.desempeno__tabla tbody tr:hover { background: rgba(249, 250, 251, .7); }
.desempeno__tabla .desempeno__centro { text-align: center; }
.desempeno__quien { font-weight: 500; color: var(--foreground); }
.desempeno__suave { color: var(--muted-foreground); }
.desempeno__fuerte { font-weight: 600; color: var(--foreground); font-variant-numeric: tabular-nums; white-space: nowrap; }
.desempeno__avance { display: flex; align-items: center; gap: 8px; }
.desempeno__riel {
  flex: 1; min-width: 80px; height: 6px;
  border-radius: 999px; background: var(--muted); overflow: hidden;
}
.desempeno__riel > div { height: 100%; border-radius: 999px; }
.desempeno__pct { width: 36px; text-align: right; font-size: 12px; line-height: 16px; font-weight: 700; color: var(--faint); }
/* Los cortes del prototipo: desde 100 verde, desde 90 azul, abajo ámbar. */
.desempeno__relleno--bien { background: #00bc7d; }
.desempeno__relleno--cerca { background: var(--primary-bright); }
.desempeno__relleno--lejos { background: #fe9a00; }
.desempeno__pct--bien { color: #007a55; }
.desempeno__pct--cerca { color: var(--primary-text); }
.desempeno__pct--lejos { color: #bb4d00; }

/* ── Mix por canal */
.desempeno__icono-azul { font-size: 20px; color: var(--primary); }
.desempeno__mix { display: flex; flex-direction: column; gap: 20px; margin-top: 24px; }
.desempeno__canal-fila { padding: 8px; border-radius: 18px; }
.desempeno__canal-fila--elegido { background: rgba(239, 246, 255, .8); box-shadow: 0 0 0 1px #dbeafe; }
.desempeno__canal-cabeza { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.desempeno__canal-nombre { margin: 0; display: flex; align-items: center; gap: 8px; font-size: 14px; line-height: 20px; font-weight: 600; color: #1e2939; }
.desempeno__punto-canal { width: 8px; height: 8px; border-radius: 50%; }
.desempeno__canal-sub { margin: 2px 0 0; font-size: 12px; line-height: 16px; color: var(--muted-foreground); }
.desempeno__canal-cifras { text-align: right; }
.desempeno__canal-cifras p:first-child { margin: 0; font-size: 14px; line-height: 20px; font-weight: 600; color: var(--foreground); font-variant-numeric: tabular-nums; }
.desempeno__canal-cifras p + p { margin: 2px 0 0; font-size: 12px; line-height: 16px; color: var(--faint); }
.desempeno__riel--canal { flex: none; height: 8px; margin-top: 8px; }
.desempeno__tono--azul { background: var(--primary-bright); }
.desempeno__tono--indigo { background: #615fff; }
.desempeno__tono--cian { background: #00b8db; }
.desempeno__tono--violeta { background: #8e51ff; }

/* Los mismos cortes del prototipo: `sm` a 640, `md` a 768 y `xl` a 1280. */
@media (min-width: 640px) {
  .desempeno__caja-cabeza { flex-direction: row; align-items: flex-start; justify-content: space-between; }
  .desempeno__caja-cabeza--tabla { align-items: center; }
}
@media (min-width: 768px) {
  .desempeno__franja { grid-template-columns: repeat(3, minmax(0, 1fr)); padding: 14px 24px; }
  .desempeno__cifras { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .desempeno__cifra { padding: 16px 20px; border-width: 0; }
  .desempeno__cifra + .desempeno__cifra { border-left-width: 1px; }
}
@media (min-width: 1280px) {
  .desempeno__hero-top { flex-direction: row; align-items: flex-end; justify-content: space-between; }
  .desempeno__fila--ventas { grid-template-columns: minmax(0, 1.55fr) minmax(320px, .72fr); }
  .desempeno__fila--detalle { grid-template-columns: minmax(0, 1.4fr) minmax(320px, .86fr); }
}
@media (max-width: 767px) {
  .desempeno__caja { padding: 20px; }
  .desempeno__caja--tabla { padding: 0; }
}

/* ── Metas de venta ──────────────────────────────────────────────────────────
 * Un monto por fila. El campo va angosto y a la derecha, que es donde se leen
 * los montos en el resto del sistema, y la fila centrada: con el campo de
 * 32 px y el nombre arriba, el nombre parecía de la fila anterior. */
/* Cada tarjeta de su alto: la de canales son tres filas y la de vendedores
 * cuarenta, y estiradas a la par la de canales quedaba como una caja vacía. */
.metas .grid { align-items: start; }
.metas .table td { vertical-align: middle; }
.metas .table .input { width: 100%; max-width: 200px; margin-left: auto; }

/* Estilos en curso de la pantalla «ventas». Se integran a sigoone.css al cerrar. */

/* ── «Control de ventas» (Indicadores de Ventas y pedidos) ──────────────────
 *
 * La pantalla del prototipo V4 (`IndicadoresView` en `VentasPage.tsx`),
 * copiada bloque por bloque a pedido de Carlos el 22-09-2026, con el mismo
 * lenguaje que «Desempeño comercial»: la cabecera, la caja, la tabla y las
 * prioridades son `.desempeno__*` (`static/css/sigoone.css`) tal cual, sin
 * tocarlas. Acá solo van los dos bloques que el prototipo dibuja distinto:
 * la franja de **seis** cifras (allá son cuatro) y el gráfico de ritmo de
 * **cinco** semanas sin barra de meta (allá son seis meses con dos barras).
 *
 * Prefijo `.ventas-ind__`, para no chocar con las otras pantallas que se
 * arman en paralelo.
 */

/* La cabecera de Ventas no lleva la franja de abajo que sí tiene la de
 * Desempeño comercial —acá no hay meta semanal ni canal que anunciar—, así
 * que `.desempeno__hero-top` termina siendo todo el alto de la caja. Sus
 * esquinas de abajo están pensadas cuadradas a propósito, para que la franja
 * cuelgue de ellas sin escalón; sin la franja, esas esquinas cuadradas
 * asoman por fuera del borde redondeado de `.desempeno__hero` (16 px) y la
 * caja se ve mal cerrada en las dos puntas de abajo. El selector compuesto
 * pesa más que `.desempeno__hero-top` sola, así que gana sin importar en qué
 * orden cargue esta hoja contra `sigoone.css`. */
.desempeno__hero > .desempeno__hero-top:only-child {
  border-radius: 15px;
}

/* La franja de seis cifras: la misma caja de `.desempeno__cifras` — blanca,
 * filo gris, esquina de 16 — pero con su propia grilla (2 → 3 → 6 columnas,
 * como el prototipo) y sus propios separadores. No se reusa
 * `.desempeno__cifra`: sus `nth-child` están pensados para una grilla de
 * 2 y 4 columnas, y forzarla a 2/3/6 pelearía consigo misma en cada corte. */
.ventas-ind__cifras {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  overflow: hidden;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 16px;
  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .1), 0 1px 2px -1px rgba(0, 0, 0, .1);
}
.ventas-ind__cifra {
  padding: 16px;
  border-color: var(--border-soft); border-style: solid; border-width: 0;
}
/* `:where()` para que las reglas de los cortes más anchos, con la misma
 * especificidad, las puedan apagar sin pelear por el orden del archivo. */
.ventas-ind__cifra:where(:nth-child(even)) { border-left-width: 1px; }
.ventas-ind__cifra:where(:nth-child(n+3)) { border-top-width: 1px; }

@media (min-width: 768px) {
  .ventas-ind__cifras { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .ventas-ind__cifra {
    padding: 16px 20px;
    border-left-width: 0; border-top-width: 0;
  }
  .ventas-ind__cifra:where(:not(:nth-child(3n+1))) { border-left-width: 1px; }
  .ventas-ind__cifra:where(:nth-child(n+4)) { border-top-width: 1px; }
}
@media (min-width: 1280px) {
  .ventas-ind__cifras { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .ventas-ind__cifra { border-left-width: 0; border-top-width: 0; }
  .ventas-ind__cifra:where(:not(:first-child)) { border-left-width: 1px; }
}

/* El gráfico de ritmo: cinco columnas con el monto o el número arriba y la
 * semana abajo, sin la segunda barra de meta que sí tiene «Ventas vs meta»
 * —acá no hay meta semanal, la meta es del período entero—. Las medidas son
 * las de `.desempeno__grafico` para que las dos pantallas respiren igual. */
.ventas-ind__ritmo {
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); align-items: end; gap: 12px;
  height: 224px; margin-top: 24px; padding: 0 8px 28px;
  border-bottom: 1px solid var(--border-soft);
}
.ventas-ind__semana {
  position: relative; height: 100%; min-width: 0;
  display: flex; align-items: flex-end; justify-content: center;
}
.ventas-ind__monto {
  position: absolute; top: -20px;
  font-size: 10px; line-height: 15px; font-weight: 600; color: var(--foreground-soft);
  white-space: nowrap; font-variant-numeric: tabular-nums;
}
.ventas-ind__barra {
  width: 100%; max-width: 56px;
  border-radius: 12px 12px 0 0;
  background: var(--primary-bright);
}
.ventas-ind__barra--actual { background: var(--primary); }
.ventas-ind__semana-rotulo {
  position: absolute; bottom: -24px;
  font-size: 11px; line-height: 16.5px; color: var(--faint);
}
.ventas-ind__semana-rotulo--actual { color: var(--primary-text); font-weight: 600; }

/* ── Compras › Indicadores ────────────────────────────────────────────────
 * «Indicadores» de Compras, copiada bloque por bloque de `function
 * Indicadores()` en `ComprasPage.tsx` del prototipo V4, a pedido de Carlos
 * el 22-09-2026: la misma regla que ya se aplicó en Desempeño comercial.
 *
 * **No se repite lo que Desempeño comercial ya resolvió.** La cabecera, la
 * caja blanca, la franja, el riel de avance, las prioridades y su tabla de
 * niveles son las mismas reglas de `.desempeno__*` en `sigoone.css` —esas
 * clases son selectores sueltos, no encerrados bajo `.desempeno`, así que se
 * pueden usar tal cual—. Acá solo va lo que el prototipo de Compras trae
 * distinto: seis tarjetas sueltas en vez de una franja con divisiones, el
 * pie de tres columnas bajo el gráfico de Chart.js, y los proveedores en
 * tarjetas de barra en vez de una tabla.
 */

/* ── La cabecera: el detalle bajo el titular ------------------------------ */
.compras-ind__detalle {
  margin: 4px 0 0;
  font-size: 14px; line-height: 20px; color: var(--muted-foreground);
}

/* ── Las seis tarjetas ------------------------------------------------------
 * `.desempeno__caja` da el blanco, el filo y la sombra; acá solo se ajusta el
 * relleno —24 px es de una caja grande, esta es una tarjeta chica— y se pone
 * en columna con gap, que es como reparte KpiCard el rótulo, el valor y la
 * nota en el prototipo. */
.compras-ind__tarjetas {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px;
}
.compras-ind__tarjeta {
  display: flex; flex-direction: column; gap: 8px;
  padding: 18px 20px;
}
.compras-ind__tarjeta-cabeza {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
}
.compras-ind__tarjeta-cabeza .icon { font-size: 16px; color: var(--faint); }

/* La tendencia de «Compras del mes»: más o menos comprado no es en sí bueno
 * ni malo —es comprar más—, así que su único tono es gris (Carlos ya fijó
 * esta misma regla para «órdenes» en `purchasing_indicators`, `Variacion`
 * con `sube_es_bueno=None`). Se dejan los otros dos tonos escritos para que
 * el día que otra tarjeta sí tenga un sentido bueno/malo no haga falta
 * inventar la clase. */
.compras-ind__tendencia {
  display: flex; align-items: center; gap: 4px; margin: 0;
  font-size: 11px; line-height: 16.5px; font-weight: 600;
}
.compras-ind__tendencia .icon { font-size: 13px; }
.compras-ind__tendencia--igual { color: var(--muted-foreground); }
.compras-ind__tendencia--bien { color: #007a55; }
.compras-ind__tendencia--mal { color: #9f0712; }

/* ── Evolución de compras + Atención esta semana --------------------------- */
.compras-ind__fila-evolucion { display: grid; gap: 20px; }
.compras-ind__evolucion { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.compras-ind__evolucion-cabeza {
  display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: 12px;
}

/* El pie de tres columnas, bajo la caja del gráfico de Chart.js: el mismo
 * `.desempeno__pie` de Desempeño comercial, en su propia caja chica porque
 * `components/grafico.html` cierra la suya y no hay dónde meterlo dentro. */
.compras-ind__pie-caja { padding: 16px 24px; }
.compras-ind__pie-caja .desempeno__pie { margin-top: 0; }
.compras-ind__variacion--igual { color: var(--foreground); }
.compras-ind__variacion--bien { color: #007a55; }
.compras-ind__variacion--mal { color: #9f0712; }

/* El enlace «Ver todos» / «Ver seguimiento de órdenes», igual en las dos
 * cajas que lo usan. */
.compras-ind__ver {
  display: inline-flex; align-items: center; gap: 4px; margin-top: 16px;
  font-size: 12px; line-height: 16px; font-weight: 600;
  color: var(--primary-text); text-decoration: none;
}
/* Ningún enlace se subraya al pasar por encima (Carlos, 06-09-2026): lo
 * que dice que responde es el color. */
.compras-ind__ver:hover { color: var(--primary); }
.compras-ind__ver .icon { font-size: 14px; }

/* ── Desempeño de proveedores, en tarjetas y no en tabla -------------------- */
.compras-ind__proveedores {
  display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; margin-top: 20px;
}
.compras-ind__proveedor {
  padding: 16px; border: 1px solid var(--border-soft); border-radius: 14px;
}
.compras-ind__proveedor-cabeza {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
}
.compras-ind__proveedor-nombre { margin: 0; font-size: 14px; line-height: 20px; font-weight: 600; color: var(--foreground); }
.compras-ind__proveedor-monto {
  margin: 4px 0 0; font-size: 12px; line-height: 16px; color: var(--faint);
  font-variant-numeric: tabular-nums;
}
.compras-ind__badge {
  flex: none; padding: 4px 8px; border-radius: 999px;
  font-size: 11px; line-height: 16px; font-weight: 600; white-space: nowrap;
}
.compras-ind__badge--bien { background: #ecfdf5; color: #007a55; }
.compras-ind__badge--cerca { background: var(--primary-soft); color: var(--primary-text); }
.compras-ind__badge--lejos { background: #fffbeb; color: #bb4d00; }
.compras-ind__badge--vacio { background: var(--muted); color: var(--faint); }
.compras-ind__riel { margin-top: 12px; }

@media (min-width: 640px) {
  .compras-ind__tarjetas { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
  .compras-ind__proveedores { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1280px) {
  .compras-ind__tarjetas { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .compras-ind__fila-evolucion { grid-template-columns: minmax(0, 1.72fr) minmax(300px, .8fr); }
}

/* ── Inventario › Indicadores y Planificación › Indicadores ─────────────────
 * Las dos pantallas del prototipo V4 —`IndicadoresView` de `BodegaPage.tsx`
 * y `function Indicadores()` de `PlanificacionPage.tsx`— (Carlos,
 * 22-09-2026). El prefijo `inventario-ind__` es de estas dos pantallas; lo
 * que es igual a Desempeño comercial reusa `.card`, `.page-head` y `.notice`,
 * que son del sistema y no de una pantalla.
 */

.inventario-ind__kpis {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
  margin-bottom: 20px;
}
/* La tarjeta, el rótulo y la cifra son los de `.kpi`, la de todas las
 * pantallas de Bodega (Carlos, 28-09-2026: «unifica estas cards»): mismo
 * borde sin sombra, cifra de 24 px en DM Sans y el hueco «—» en gris claro.
 * Lo único propio es el icono a la derecha del rótulo, que ya solo lleva
 * Planificación: Inventario › Indicadores usa `.kpi` tal cual, sin icono. */
.inventario-ind__kpi {
  padding: 16px 18px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.inventario-ind__kpi-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
.inventario-ind__kpi-label {
  margin: 0; font-size: 12px; line-height: 16px; color: var(--muted-foreground);
}
.inventario-ind__kpi-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 999px; flex: none;
}
.inventario-ind__kpi-icon .icon { font-size: 14px; }
.inventario-ind__kpi-icon--azul { background: var(--primary-soft); color: var(--primary); }
.inventario-ind__kpi-icon--verde { background: var(--success-soft); color: var(--success-text); }
.inventario-ind__kpi-icon--ambar { background: var(--warning-soft); color: var(--warning-text); }
.inventario-ind__kpi-icon--violeta { background: #f5f3ff; color: #7f22fe; }
.inventario-ind__kpi-icon--rojo { background: var(--danger-soft); color: var(--danger-text); }
.inventario-ind__kpi-value {
  margin: 4px 0 0; font-family: var(--font-sans); font-size: 24px; line-height: 1.1;
  font-weight: 700; color: var(--foreground); font-variant-numeric: tabular-nums;
}
.inventario-ind__kpi-value--vacio { color: var(--border-input); font-weight: 500; }
.inventario-ind__kpi-sub { margin: 4px 0 0; font-size: 12px; color: var(--faint); }

@media (min-width: 640px) {
  .inventario-ind__kpis--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@media (min-width: 900px) {
  .inventario-ind__kpis--6 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 1280px) {
  .inventario-ind__kpis--6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }
}

/* ── La tabla por categoría (rotación, cobertura, obsolescencia) ── */
.inventario-ind__tabla-caja { padding: 0; overflow: hidden; margin-bottom: 20px; }
.inventario-ind__tabla-cabeza { padding: 16px 20px; border-bottom: 1px solid var(--border-soft); }
.inventario-ind__tabla-cabeza h2 { margin: 0; font-size: 16px; }
.inventario-ind__tabla { overflow-x: auto; }
.inventario-ind__tabla table { width: 100%; min-width: 720px; border-collapse: collapse; font-size: 14px; }
.inventario-ind__tabla thead tr { background: rgba(249, 250, 251, .7); }
.inventario-ind__tabla th {
  padding: 12px 20px; text-align: left; font-size: 11px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .08em; color: var(--faint);
}
.inventario-ind__tabla td { padding: 14px 20px; border-top: 1px solid var(--border-soft); }
.inventario-ind__tabla tbody tr:hover { background: rgba(249, 250, 251, .7); }
.inventario-ind__categoria { font-weight: 600; color: var(--foreground); }
.inventario-ind__con-barra { display: flex; align-items: center; gap: 8px; }
.inventario-ind__mini-riel {
  width: 64px; height: 6px; border-radius: 999px; background: var(--muted); overflow: hidden; flex: none;
}
.inventario-ind__mini-riel > div { height: 100%; border-radius: 999px; }
.inventario-ind__mini-riel--dias > div { background: var(--warning); }
.inventario-ind__mini-riel--cobertura > div { background: var(--primary-bright); }
.inventario-ind__cifra { font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
.inventario-ind__cifra--bien { color: var(--success-text); }
.inventario-ind__cifra--mal { color: var(--danger-text); }
.inventario-ind__cifra--regular { color: var(--warning-text); }
.inventario-ind__vacio-celda { color: var(--faint); }
.inventario-ind__nivel {
  display: inline-block; padding: 2px 10px; border: 1px solid; border-radius: 12px;
  font-size: 11px; font-weight: 700;
}
.inventario-ind__nivel--alto { background: var(--danger-soft); color: var(--danger-text); border-color: #ffc9c9; }
.inventario-ind__nivel--medio { background: var(--warning-soft); color: var(--warning-text); border-color: #fde68a; }
.inventario-ind__nivel--bajo { background: var(--success-soft); color: var(--success-text); border-color: #bbf7d0; }
.inventario-ind__tabla-vacia { padding: 24px 0; text-align: center; color: var(--muted-foreground); }

/* ── La fila de dos cajas: tendencia + distribución (o crítico + rotación) ── */
.inventario-ind__fila-2 {
  display: grid; gap: 20px; grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 1024px) {
  .inventario-ind__fila-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.inventario-ind__caja h3 { margin: 0 0 16px; font-size: 15px; font-weight: 600; }
.inventario-ind__caja .faint { margin-top: 4px; font-size: 12px; }
.inventario-ind__caja--suelta { margin-bottom: 20px; }
.inventario-ind__aviso { margin-top: 20px; }

/* ── Barras con el valor encima (punto 5 de Carlos) ── */
.inventario-ind__barras { display: flex; align-items: flex-end; gap: 12px; height: 130px; }
.inventario-ind__mes { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 4px; height: 100%; }
.inventario-ind__monto { font-size: 11px; font-weight: 600; color: var(--muted-foreground); white-space: nowrap; }
.inventario-ind__pista { flex: 1; width: 100%; display: flex; align-items: flex-end; }
.inventario-ind__barra {
  width: 100%; min-height: 2px; border-radius: 6px 6px 0 0; background: var(--primary-soft);
}
.inventario-ind__barra--ultima { background: var(--primary); }
.inventario-ind__mes-rotulo { font-size: 11px; color: var(--faint); }
.inventario-ind__barras--dos { height: 96px; }
.inventario-ind__pista--fill .inventario-ind__barra { background: var(--success-soft); }
.inventario-ind__barra--fill-ultima { background: var(--success); }
.inventario-ind__subtitulo-barras {
  margin: 4px 0 10px; font-size: 11px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .06em; color: var(--faint);
}

/* ── Distribución de inventario por estado ── */
.inventario-ind__distribucion { display: flex; flex-direction: column; gap: 14px; }
.inventario-ind__estado-fila { display: flex; align-items: center; gap: 10px; }
.inventario-ind__punto { width: 10px; height: 10px; border-radius: 50%; flex: none; }
.inventario-ind__punto--disponible { background: var(--primary-bright); }
.inventario-ind__punto--comprometido { background: var(--warning); }
.inventario-ind__punto--transito { background: #8e51ff; }
.inventario-ind__punto--cuarentena { background: var(--danger); }
.inventario-ind__estado-nombre { flex: 1; font-size: 14px; color: var(--foreground-soft); }
.inventario-ind__estado-monto { font-size: 14px; font-weight: 600; color: var(--foreground); font-variant-numeric: tabular-nums; }
.inventario-ind__estado-riel { width: 64px; height: 6px; border-radius: 999px; background: var(--muted); overflow: hidden; }
.inventario-ind__estado-riel > div { height: 100%; }
.inventario-ind__estado-riel--disponible > div { background: var(--primary-bright); }
.inventario-ind__estado-riel--comprometido > div { background: var(--warning); }
.inventario-ind__estado-riel--transito > div { background: #8e51ff; }
.inventario-ind__estado-riel--cuarentena > div { background: var(--danger); }
.inventario-ind__estado-pct { width: 34px; text-align: right; font-size: 11px; color: var(--faint); }

/* ── SKU en estado crítico ── */
.inventario-ind__criticos { display: flex; flex-direction: column; gap: 10px; }
.inventario-ind__critico-fila { display: flex; align-items: center; gap: 10px; }
.inventario-ind__foco { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.inventario-ind__foco--agotado { background: var(--danger); }
.inventario-ind__foco--critico { background: var(--warning); }
.inventario-ind__critico-nombre { flex: 1; font-size: 13px; color: var(--foreground-soft); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.inventario-ind__critico-cifra { font-size: 11px; font-weight: 600; color: var(--muted-foreground); white-space: nowrap; }

/* ── Categorías con mayor rotación ── */
.inventario-ind__rotacion-lista { display: flex; flex-direction: column; gap: 12px; }
.inventario-ind__rotacion-fila { display: flex; align-items: center; gap: 10px; }
.inventario-ind__rotacion-nombre { width: 100px; flex: none; font-size: 13px; color: var(--foreground-soft); }
.inventario-ind__rotacion-riel { flex: 1; height: 8px; border-radius: 999px; background: var(--muted); overflow: hidden; }
.inventario-ind__rotacion-riel > div { height: 100%; background: var(--primary-bright); }
.inventario-ind__rotacion-cifra { width: 48px; text-align: right; font-size: 13px; font-weight: 700; color: var(--foreground); }

/* Las dos cajas de `.inventario-ind__fila-2` van lado a lado, y el `gap` ya
 * las separa: sin esto, la regla general «.card + .card» de sigoone.css le
 * pondría 16 px de margen arriba a la segunda y la correría hacia abajo,
 * descuadrando la fila (ver `core/tests/test_las_tarjetas_cuadran.py`, que
 * no alcanza a ver esta rejilla porque sus tarjetas son `<section>` y no
 * `<div>`). */
.inventario-ind__fila-2 > .card + .card { margin-top: 0; }

/* ── Indicadores de Logística (Rutas y Devoluciones) ─────────────────────────
 *
 * Las dos pantallas de indicadores «al estilo V4» de Logística —Rutas y
 * distribución, Devoluciones—, copiadas del prototipo bloque por bloque
 * (Carlos, 22-09-2026: «idéntica al del prototipo de Figma»). Reusan
 * `.desempeno__caja`, `.desempeno__caja-cabeza`, `.desempeno__leyenda`,
 * `.desempeno__muestra`, `.desempeno__riel` y `.desempeno__vacio` de
 * «Desempeño comercial» para lo que es igual —la caja blanca, la cabecera con
 * leyenda, el punto de la leyenda, el riel de una barra de progreso y el
 * aviso de «no hay nada»—; lo que sigue es lo propio de estas dos pantallas.
 *
 * **Las seis cifras van en tarjetas sueltas**, no en una caja dividida como
 * `.desempeno__cifras`: así las dibuja el prototipo acá (`KpiCard`, sin el
 * cuadrito de icono de «Desempeño comercial»), y por eso `.logistica-ind__kpi`
 * se monta sobre `.desempeno__caja` —la misma caja blanca, filo y sombra— en
 * vez de repetir esas tres declaraciones.
 */
.logistica-ind {
  display: flex; flex-direction: column; gap: 20px;
  padding-bottom: 32px;
  color: var(--foreground);
}
.logistica-ind h2 { margin: 0; font-size: 16px; line-height: 24px; font-weight: 600; color: var(--foreground); }

/* ── Las seis cifras */
.logistica-ind__cifras {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px;
}
/* **Doble clase y no una sola**: `.desempeno__caja` trae `padding: 24px`, y
 * acá hace falta el de 16 —el `p-4` del prototipo, más chico que el `p-6` de
 * la caja del gráfico—. Con una sola clase la especificidad sería la misma y
 * ganaría la que aparezca después en la hoja; con las dos juntas manda esta,
 * sin depender de en qué orden queden las secciones el día que alguien las
 * mueva. */
.logistica-ind__kpi.desempeno__caja { padding: 16px; min-width: 0; }
.logistica-ind__rotulo {
  margin: 0 0 4px;
  font-size: 12px; line-height: 16px; color: var(--muted-foreground);
}
.logistica-ind__valor {
  margin: 0;
  font-size: 24px; line-height: 32px; font-weight: 700; letter-spacing: -.02em;
  color: var(--foreground); font-variant-numeric: tabular-nums;
}
.logistica-ind__unidad { font-size: 16px; font-weight: 600; }
.logistica-ind__nota { margin: 4px 0 0; font-size: 12px; line-height: 16px; color: var(--faint); }

@media (min-width: 768px) {
  .logistica-ind__cifras { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 1280px) {
  .logistica-ind__cifras { grid-template-columns: repeat(6, minmax(0, 1fr)); }
}

/* ── Entregas planificadas vs realizadas — semana (Rutas) */
.logistica-ind__semana { padding: 24px; }
.logistica-ind__grafico {
  display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); align-items: end; gap: 12px;
  height: 224px; margin-top: 24px; padding: 0 8px 28px;
  border-bottom: 1px solid var(--border-soft);
}
.logistica-ind__dia {
  position: relative; height: 100%; min-width: 0;
  display: flex; flex-direction: column; justify-content: flex-end; align-items: center;
}
.logistica-ind__barras {
  display: flex; align-items: flex-end; justify-content: center; gap: 4px;
  width: 100%; height: 100%;
}
.logistica-ind__col { position: relative; flex: 1; max-width: 28px; height: 100%; display: flex; align-items: flex-end; }
.logistica-ind__monto {
  position: absolute; top: -20px; left: 50%; transform: translateX(-50%);
  font-size: 10px; line-height: 15px; font-weight: 600; color: var(--foreground-soft);
  white-space: nowrap; font-variant-numeric: tabular-nums;
}
.logistica-ind__barra { width: 100%; border-radius: 8px 8px 0 0; }
.logistica-ind__barra--planificadas { background: #cbd5e1; }
.logistica-ind__barra--realizadas { background: var(--primary); }
.logistica-ind__muestra--planificadas { background: #cbd5e1; }
.logistica-ind__muestra--realizadas { background: var(--primary); }
.logistica-ind__dia-rotulo {
  position: absolute; bottom: -24px;
  font-size: 11px; line-height: 16.5px; color: var(--faint);
}

/* ── Devoluciones por motivo y disposición (Devoluciones) */
.logistica-ind__fila { display: grid; gap: 20px; }
@media (min-width: 1024px) {
  .logistica-ind__fila { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* La dona se dibuja con `conic-gradient` —el fondo lo arma el servicio, ya con
 * los cortes de cada motivo sumados— y no con Chart.js: es un reparto fijo de
 * la apertura de la pantalla, no algo que haya que animar ni tocar. El agujero
 * del centro guarda la proporción del prototipo (`innerRadius` 60 de 95). */
.logistica-ind__dona-wrap { display: flex; align-items: center; gap: 24px; margin-top: 20px; flex-wrap: wrap; }
.logistica-ind__dona { width: 180px; height: 180px; flex: none; border-radius: 50%; position: relative; }
.logistica-ind__dona::after {
  content: ""; position: absolute; inset: 33px; border-radius: 50%; background: var(--card);
}
.logistica-ind__leyenda-dona {
  margin: 0; padding: 0; list-style: none;
  display: flex; flex-direction: column; gap: 8px;
  font-size: 12px; line-height: 16px; color: var(--foreground-soft);
}
.logistica-ind__leyenda-dona li { display: flex; align-items: center; gap: 8px; }
.logistica-ind__leyenda-dona i { width: 10px; height: 10px; border-radius: 50%; flex: none; }
.logistica-ind__leyenda-dona b { margin-left: auto; font-weight: 600; color: var(--foreground); font-variant-numeric: tabular-nums; }

.logistica-ind__disposicion { display: flex; flex-direction: column; gap: 14px; margin-top: 20px; }
.logistica-ind__disposicion-cab {
  display: flex; justify-content: space-between; gap: 8px; margin-bottom: 6px;
  font-size: 12px; line-height: 16px; color: var(--muted-foreground);
}
.logistica-ind__disposicion-cab span:last-child { font-weight: 600; color: var(--foreground); }

/* ── Indicadores de crédito ──────────────────────────────────────────────────
 *
 * «Indicadores» de Crédito y cobro, del prototipo V4 (`IndicadoresView` de
 * `CreditoCobroPage.tsx`, línea ~641), copiada a pedido de Carlos el
 * 22-09-2026. Medidas tomadas del navegador sobre ese componente, no a ojo.
 *
 * **Sin `shadow-sm`.** A diferencia de «Desempeño comercial» —cuyas cajas sí
 * la llevan—, las de esta pantalla del prototipo son solo `border`, sin
 * sombra: son dos pantallas distintas del mismo Figma y no comparten esa
 * medida.
 */
.credito-ind {
  display: flex; flex-direction: column; gap: 20px;
  padding-bottom: 32px;
  color: var(--foreground);
}
.credito-ind h3 {
  margin: 0 0 16px;
  font-size: 16px; line-height: 24px; font-weight: 600; color: var(--foreground);
}

.credito-ind__caja {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 20px;
  min-width: 0;
}

/* ── Las cuatro cifras: `rounded-2xl border p-4`, sin ficha de icono —el
 * prototipo no le pone una a esta tarjeta— y sin sombra. */
.credito-ind__kpis {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px;
}
.credito-ind__kpi {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 16px;
}
.credito-ind__kpi-label { margin: 0 0 4px; font-size: 12px; line-height: 16px; color: var(--muted-foreground); }
.credito-ind__kpi-value {
  margin: 0;
  font-size: 24px; line-height: 32px; font-weight: 700;
  color: var(--foreground); font-variant-numeric: tabular-nums;
}
.credito-ind__kpi-sub { margin: 2px 0 0; font-size: 12px; line-height: 16px; color: var(--faint); }
.credito-ind__kpi-trend {
  display: flex; align-items: center; gap: 4px;
  margin: 8px 0 0; font-size: 12px; line-height: 16px; font-weight: 500;
}
.credito-ind__kpi-trend .icon { font-size: 14px; }
.credito-ind__kpi-trend--bien { color: var(--success-text); }
.credito-ind__kpi-trend--mal { color: var(--danger-text); }
.credito-ind__kpi-trend--igual { color: var(--muted-foreground); }
.credito-ind__kpi-empty { margin: 8px 0 0; font-size: 12px; line-height: 16px; color: var(--faint); }

/* ── Las dos cajas de la fila del medio */
.credito-ind__fila { display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; }
@media (min-width: 1024px) {
  .credito-ind__fila { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Tendencia DSO: seis columnas de 112 px de alto, con el valor colgado
 * arriba y el mes debajo, igual que las mide el prototipo (`h-28`, `h-20`
 * para la caja de la barra, `gap-3`). */
.credito-ind__dso {
  display: flex; align-items: flex-end; gap: 12px; height: 112px;
}
.credito-ind__dso-mes {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column; align-items: center; gap: 4px;
}
.credito-ind__dso-valor {
  font-size: 10px; line-height: 15px; font-weight: 500; color: var(--foreground-soft);
  font-variant-numeric: tabular-nums;
}
.credito-ind__dso-barra-caja {
  width: 100%; height: 80px;
  display: flex; align-items: flex-end;
}
.credito-ind__dso-barra {
  width: 100%; border-radius: 14px 14px 0 0;
  background: var(--primary-soft);
}
.credito-ind__dso-barra--ultima { background: var(--primary); }
.credito-ind__dso-rotulo { font-size: 10px; line-height: 15px; color: var(--faint); }

.credito-ind__dso-nota {
  display: flex; align-items: center; gap: 4px;
  margin: 8px 0 0; font-size: 12px; line-height: 16px; font-weight: 500;
}
.credito-ind__dso-nota .icon { font-size: 14px; }
.credito-ind__dso-nota--bien { color: var(--success-text); }
.credito-ind__dso-nota--mal { color: var(--danger-text); }
.credito-ind__dso-nota--igual { color: var(--muted-foreground); }

/* Morosidad por canal: una fila por canal, con el nombre a la izquierda, el
 * riel al centro y el porcentaje y la cartera a la derecha. */
.credito-ind__canales { display: flex; flex-direction: column; gap: 12px; }
.credito-ind__canal-fila { display: flex; align-items: center; gap: 12px; }
.credito-ind__canal-nombre {
  flex: none; width: 80px;
  font-size: 14px; line-height: 20px; color: var(--muted-foreground);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.credito-ind__canal-riel {
  flex: 1; height: 8px; border-radius: 999px;
  background: var(--muted); overflow: hidden;
}
.credito-ind__canal-relleno { height: 100%; border-radius: 999px; }
.credito-ind__canal-pct {
  flex: none; width: 40px; text-align: right;
  font-size: 12px; line-height: 16px; font-weight: 700;
}
.credito-ind__canal-cartera {
  flex: none; width: 96px; text-align: right;
  font-size: 12px; line-height: 16px; color: var(--faint);
  font-variant-numeric: tabular-nums;
}
.credito-ind__vacio { margin: 0; padding: 24px 0; font-size: 13px; color: var(--muted-foreground); text-align: center; }

/* Los tres tonos de mora, compartidos por el riel y por el texto: los
 * mismos cortes del prototipo (más de 20 % rojo, más de 10 ámbar). */
.credito-ind__tono--rojo { background: var(--danger); }
.credito-ind__tono--ambar { background: var(--warning); }
.credito-ind__tono--verde { background: var(--success); }
.credito-ind__tono-texto--rojo { color: var(--danger-text); }
.credito-ind__tono-texto--ambar { color: var(--warning-text); }
.credito-ind__tono-texto--verde { color: var(--success-text); }

/* ── Conciliación cobros / depósitos / CxC */
.credito-ind__caja--conciliacion { padding: 0; overflow: hidden; }
.credito-ind__conciliacion-cabeza {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 16px 20px; border-bottom: 1px solid var(--border-soft);
}
.credito-ind__conciliacion-cabeza h3 { margin: 0; }
.credito-ind__exportar {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 16px; border: 1px solid var(--border); border-radius: 999px;
  font-size: 14px; line-height: 20px; font-weight: 500;
  color: var(--muted-foreground); text-decoration: none;
}
.credito-ind__exportar:hover { background: var(--muted); }
.credito-ind__exportar .icon { font-size: 16px; }

.credito-ind__conciliacion-filas { display: flex; flex-direction: column; gap: 8px; padding: 20px; }
.credito-ind__conciliacion-fila {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px 16px; border-radius: 12px;
  background: var(--muted);
}
.credito-ind__conciliacion-fila--pendiente {
  background: var(--warning-soft);
  border: 1px solid #fef3c6;
}
.credito-ind__conciliacion-concepto {
  display: flex; align-items: center; gap: 8px;
  font-size: 14px; line-height: 20px; color: var(--foreground-soft);
}
.credito-ind__conciliacion-concepto .icon { font-size: 16px; flex: none; }
.credito-ind__conciliacion-fila--pendiente .icon { color: var(--warning-text); }
.credito-ind__conciliacion-fila--ok .icon,
.credito-ind__conciliacion-fila--aplicado .icon { color: var(--success-text); }
.credito-ind__conciliacion-derecha { display: flex; align-items: center; gap: 12px; }
.credito-ind__conciliacion-monto {
  font-size: 14px; line-height: 20px; font-weight: 700; color: var(--foreground);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}

@media (max-width: 767px) {
  .credito-ind__kpis { grid-template-columns: 1fr; }
  .credito-ind__caja { padding: 16px; }
  .credito-ind__conciliacion-filas { padding: 16px; }
}

/* ── Devoluciones y notas de crédito (punto 23, 23-09-2026) ───────────────
 * La pestaña «Devoluciones» del vendedor en el prototipo V4, medida por
 * medida, y la de «Notas de crédito» del cliente con el mismo molde: tres
 * tarjetas blancas con el número de color arriba y el rótulo abajo, la franja
 * de meses y la lista en tarjetas con borde rojizo. */
.devol { display: flex; flex-direction: column; gap: 16px; }
.devol__tarjetas { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
.devol__tarjeta,
.devol__item {
  background: var(--card);
  border: 1px solid var(--border); border-radius: 16px;
  box-shadow: var(--shadow-sm);
  padding: 16px;
}
.devol__valor {
  margin: 0;
  font-size: 24px; line-height: 32px; font-weight: 700; color: var(--foreground);
  font-variant-numeric: tabular-nums;
}
/* El monto va un punto más chico, como en el prototipo (`text-xl` contra el
 * `text-2xl` de las otras dos). La V4 lo escribía en `font-mono`, pero es la
 * cifra de un indicador y esas van en DM Sans, sin el cero tachado (Carlos,
 * 28-09-2026). */
.devol__valor--acreditado {
  font-size: 20px; line-height: 28px; color: #fb2c36;
}
.devol__valor--tasa { color: #fe9a00; }
.devol__rotulo { margin: 4px 0 0; font-size: 12px; line-height: 16px; color: var(--muted-foreground); }

.devol__meses { display: flex; flex-wrap: wrap; gap: 8px; }
.devol__mes {
  padding: 6px 12px; border-radius: var(--radius-pill);
  background: var(--muted); color: var(--foreground-soft);
  font-size: 14px; line-height: 20px; font-weight: 500;
  transition: background .12s, color .12s;
}
.devol__mes:hover { background: var(--border); color: var(--foreground-soft); text-decoration: none; }
.devol__mes--on,
.devol__mes--on:hover { background: var(--primary); color: #fff; }

.devol__cuenta {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin: 0; font-size: 14px; line-height: 20px; color: var(--muted-foreground);
}

.devol__lista { display: flex; flex-direction: column; gap: 12px; }
.devol__item { border-color: #ffe2e2; transition: box-shadow .15s; }
.devol__item:hover { box-shadow: var(--shadow-lg); }
.devol__item-fila { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.devol__item-titulo { display: flex; align-items: center; gap: 8px; }
.devol__codigo { font-weight: 700; color: #e7000b; }
a.devol__codigo:hover { color: #c10007; }
.devol__monto {
  font-family: var(--font-mono); font-weight: 700; color: #fb2c36;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.devol__cliente { font-size: 13px; color: var(--foreground-soft); text-align: right; }
.devol__sub { margin: 4px 0 0; font-size: 12px; line-height: 16px; color: var(--muted-foreground); }
.devol__etiquetas { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 8px; }
.devol__motivo {
  padding: 2px 8px; border-radius: var(--radius-pill);
  background: #fef2f2; color: #e7000b;
  font-size: 12px; line-height: 16px;
}
.devol__vacio { margin: 0; padding: 48px 0; text-align: center; font-size: 14px; color: #99a1af; }

[data-theme="dark"] .devol__item { border-color: #4c1d1d; }
[data-theme="dark"] .devol__motivo { background: #3b1414; color: #fca5a5; }

@media (max-width: 767px) {
  .devol__tarjetas { grid-template-columns: 1fr; }
}

/* ── Cobro de varias facturas: Total o Parcial (punto 29, 23-09-2026) ─────────────────────────── */
/* ── «Aplicar a» ──────────────────────────────────────────────────────────
 * La tabla del punto 29 del COMERCIAL.docx (Carlos, 23-09-2026): cada
 * documento con su saldo, un selector de Total o Parcial -la misma píldora
 * de «Medio de pago», que ya trae su estilo en `.field--pillpicker`- y el
 * monto que se le aplica.
 *
 * Reusa `.card`, `.table`, `.table-wrap`, `.field`, `.field--pillpicker` y
 * `.field--error` de `sigoone.css` -son la misma pieza en cualquier otra
 * pantalla- y acá solo se ajusta lo que cambia por ir **dentro de una
 * celda** en vez de la rejilla de dos columnas de un formulario suelto.
 *
 * **Todo lo que compite con una regla de `sigoone.css` va con las dos
 * clases juntas** (`.field.cobro-apl__monto`, no `.cobro-apl__monto` a
 * secas): esta hoja se funde **antes** que esa -`@import` es la primera
 * línea del archivo-, así que a igual especificidad gana la de
 * `sigoone.css`, que queda después. Con las dos clases, la de acá pesa más y
 * gana siempre, sin depender del orden. */
.cobro-apl { margin-bottom: 16px; }

/* El `.field` de siempre lleva `margin-bottom: 16px` porque separa un campo
 * del que sigue en una columna; acá el que sigue es el borde de la celda, y
 * ese margen solo baja el número sin motivo. */
.cobro-apl__monto-celda .field { margin: 0; }
.field--pillpicker.cobro-apl__modo { margin: 0; }

/* El monto, del ancho justo para «000.000,00» y pegado al borde derecho de
 * su celda, como cualquier otro monto de la tabla. Sin esto hereda el
 * `width: 100%` del formulario suelto y se estira a lo ancho de la columna
 * entera, que en una tabla angosta se ve desproporcionado. */
.field.cobro-apl__monto input {
  width: 110px; margin-left: auto; text-align: right;
}

/* El `fieldset` de la píldora no lleva ayuda ni rótulo visible en la tabla
 * -la columna «Aplica» ya lo dice-, así que su `legend` es solo para quien
 * usa un lector de pantalla: oculto para el ojo con la técnica de siempre,
 * sin `display: none` -que también lo esconde de la lectura-. */
.cobro-apl__oculta {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* **El modal se ensancha cuando lleva el reparto.** A 520 px la tabla no
 * cabía: el selector «Total | Parcial» y el monto quedaban escondidos a la
 * derecha, con una barra de desplazamiento que nadie ve (23-09-2026). Mismo
 * recurso que el catálogo y las casillas múltiples, más arriba en la hoja. */
.modal:has(.cobro-apl) { max-width: 760px; }

/* ── Precios del cliente: lo que compra y lo que no (punto 28, 23-09-2026) ─────────────────────────── */
/*
 * Punto 28 del COMERCIAL.docx, la parte que quedó en rojo (Carlos,
 * 23-09-2026): «cuáles compra y cuáles no, a nivel de tabla y con
 * identificador visual». La columna ya usa `.badge`/`.badge--success` del
 * sistema; lo único propio de esta pantalla es el tinte de la fila de lo que
 * no compra, sutil a propósito —no es un error, es información— y con más
 * especificidad que `.table tbody tr:hover` para que no desaparezca al pasar
 * el mouse.
 */
.table tbody tr.precios-cli__row--no-compra > td {
  background: var(--muted);
}
.table tbody tr.precios-cli__row--no-compra:hover > td {
  background: var(--muted);
}

/* La etiqueta «No compra» es gris como el tinte de su fila: sin esto se
 * funde con el fondo y se lee como texto suelto. */
.precios-cli__row--no-compra .badge {
  background: var(--card);
  box-shadow: inset 0 0 0 1px var(--border);
}

/* ── Referencias comerciales de la solicitud de crédito ─────────────────────
 *
 * Ajuste 2, Carlos, 23-09-2026: «Referencias comerciales» pasó de un cuadro
 * de texto libre a renglones de contacto, con el mismo mecanismo de agregar y
 * quitar que ya usan los convenios de precio (`static/js/renglones.js`).
 *
 * **La primera versión era una tabla ancha de ocho columnas y se salía del
 * formulario** —Carlos lo vio el mismo día: 818 px de contenedor contra
 * 1.005 px de tabla, con el monto, el plazo y el botón de quitar fuera de
 * vista y una barra de desplazamiento que nadie notaba—. Acá cada referencia
 * es una tarjeta de dos filas, no una fila ancha.
 *
 * **Sigue siendo `<table>`/`<tbody>`/`<tr>`**, y eso no es cosmético: el
 * guion cuenta los renglones con `tabla.tBodies[0]` y `cuerpo.rows`, que son
 * del elemento HTML y no de cómo se dibuja. Lo único que cambia es la
 * presentación —`display: grid` en cada `<tr>`— y eso el guion no lo mira.
 *
 * **El ancho nunca se fija en píxeles.** `.table` de por sí mide
 * `width: max-content`, que es exactamente lo que desbordó la primera
 * versión; acá se pasa a bloque con `width: 100%` y las columnas de la
 * rejilla usan `minmax(0, 1fr)` —el mínimo en cero, no en «lo que pida el
 * contenido»— para que un campo con un valor largo achique su columna en vez
 * de empujar el formulario entero hacia la derecha. */
.refs-com__ayuda {
  margin: 0 0 12px; font-size: 12px; color: var(--muted-foreground);
}
.refs-com__tabla.table {
  display: block; width: 100%; min-width: 0;
}
.refs-com__tabla.table tbody {
  display: flex; flex-direction: column; gap: 12px;
}
/* Dos filas de cuatro: empresa · contacto · teléfono · correo, y debajo
 * relación · monto · plazo · quitar. El botón de quitar cae solo en la
 * última casilla de la rejilla —abajo a la derecha— por el orden normal en
 * que las ocho celdas se acomodan; no hace falta posicionarlo aparte. */
.refs-com__tabla.table tbody tr {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px 14px;
  width: 100%; box-sizing: border-box;
  padding: 14px 16px;
  margin: 0; border: 1px solid var(--border); border-radius: var(--radius-md);
  background: var(--card);
}
.refs-com__tabla.table tbody tr:hover { background: var(--card); }
.refs-com__tabla.table tbody td {
  display: block; min-width: 0;
  padding: 0; border: 0; white-space: normal; vertical-align: top;
}
.refs-com__etiqueta {
  display: block; margin-bottom: 4px;
  font-size: 11px; font-weight: 600; color: var(--muted-foreground);
}
/* El botón de quitar —lo dibuja `renglones.js` dentro de esta celda, ver
 * `ponerLosBotonesDeQuitar`— al ras del borde inferior derecho de la
 * tarjeta, como pidió Carlos, y no arriba junto a su etiqueta. */
.refs-com__quitar {
  display: flex; align-items: flex-end; justify-content: flex-end;
}

/* Una columna sola en el teléfono: cuatro campos por fila no caben en 360 px
 * sin partir palabras, y es la misma medida a la que el resto del sitio pasa
 * a una columna. */
@media (max-width: 640px) {
  .refs-com__tabla.table tbody tr { grid-template-columns: 1fr; }
  .refs-com__quitar { justify-content: flex-start; }
}

/* El renglón al que le falta el nombre o el teléfono: el obligatorio de
 * siempre del sitio, solo el borde y sin mensaje (el texto de la queja va
 * vacío a propósito, ver `credit.forms.references`). Repite el selector del
 * campo de texto —`.form input[type=text]`— para poder ganarle: por pesar
 * menos, el borde rojo no se veía y solo quedaba la celda con más aire. */
.refs-com__tabla .refs-com__celda--error input[type=text] {
  border-color: var(--danger);
}
.refs-com__tabla .refs-com__celda--error input[type=text]:focus {
  border-color: var(--danger); box-shadow: 0 0 0 3px var(--danger-soft);
}

/* ── Facturas: doce columnas en escritorio (Carlos, 23-09-2026) ─────────────
 *
 * `ventas_fac_facturas.html` midió el `.table-wrap` en 1750 px contra una
 * tabla de 1789 —y de verdad, con Chromium sin cabeza, en 1825—: «Estado de
 * cobro» quedaba cortado a los 1600 px de un escritorio normal. El relleno
 * de siempre —20 px por lado en cada celda— eran 480 de esos 1825 solo en
 * aire, y el nombre del cliente sin partirse se llevaba otros 400.
 *
 * Local a esta pantalla y con su propio prefijo, como pidió Carlos: el
 * agente de Pedidos aprieta la suya por su cuenta en `.pedidos-lista__`. */
/* **Con `.table` en el mismo selector y no solo `.facturas-lista`**: contra
 * `.table thead th`/`.table tbody td`, que ya traen dos elementos en su
 * especificidad, una clase sola perdía la cascada aunque viniera después en
 * el archivo. */
.table.facturas-lista thead th,
.table.facturas-lista tbody td { padding: 8px 10px; }
/* **El cliente se parte en dos líneas**, como ya hacen otras listas con
 * nombres largos, en vez de estirar la columna hasta la razón social más
 * larga. Sigue siendo un solo dato en la celda —el nombre entero, solo que
 * envuelto— y no dos apilados. */
.table.facturas-lista tbody td.facturas-lista__cliente {
  white-space: normal; max-width: 170px;
}
/* Las insignias de esta tabla no necesitan el aire de una página con menos
 * columnas: menos relleno y un punto más chico, sin perder el color que
 * distingue el tono. */
.facturas-lista .badge { padding: 2px 6px; }
.facturas-lista .badge .dot { width: 5px; height: 5px; }

/* ── Lista de pedidos: 13 columnas sin desbordar (punto 22 de VENTAS, 23-09-2026) ──
 * Las columnas tienen ancho fijo en `<colgroup>` (suman 100 %) y la tabla deja el
 * `max-content` de `.table` solo acá: con trece columnas, el ancho no puede
 * depender del contenido o se sale de la tarjeta. Los montos, la fecha y el
 * estado no se parten; el cliente, el vendedor y los encabezados largos sí. */
.table.pedidos-lista__tabla { table-layout: fixed; width: 100%; }
.table.pedidos-lista__tabla thead th,
.table.pedidos-lista__tabla tbody td {
  padding: 8px 6px; white-space: normal; overflow: hidden;
}
.table.pedidos-lista__tabla thead th { overflow-wrap: break-word; }
.table.pedidos-lista__tabla tbody td.num,
.table.pedidos-lista__tabla tbody td.nowrap {
  white-space: nowrap; text-overflow: ellipsis;
}
.table.pedidos-lista__tabla tbody td .badge { white-space: nowrap; }

/* ── Tablero del vendedor: Resumen y Productos (puntos 4 y 14 de VENTAS,
 * 23-09-2026, agente VEND-B) ────────────────────────────────────────────────
 *
 * Sigue la captura del equipo y no el Figma V4 —el Figma «no entendía» estas
 * dos pantallas—, con los colores y las piezas que ya existen en SigoOne:
 * `.kpi--tono`, `.card`, `.badge`, `.mix`, `.aging__pill`. Lo que sigue es lo
 * que no tenía dónde reusarse. Prefijo `.vend-b__` para no chocar con lo que
 * tocan los demás agentes de esta tanda. */

/* Dos columnas que se abren con la pantalla —«Facturado y cobrado» con el
 * panel del mes, y «Documentos por cobrar» con «Acciones rápidas»—, igual que
 * `.grid--2` arranca angosta y no como los `.grid--N` ya declarados, que son
 * simétricos y acá una columna pesa el doble que la otra. */
/* Las tres tarjetas de arriba de Productos, separadas del ranking como
 * cualquier fila de indicadores (`.kpis`): pegadas, el borde de una tocaba
 * el de la otra (Carlos, 24-09-2026). */
.vend-b__tarjetas { margin-bottom: 20px; }
.vend-b__split { display: grid; gap: 16px; grid-template-columns: minmax(0, 1fr); }
.vend-b__split > .card + .card { margin-top: 0; }
/* Las filas de tarjetas de la ficha del vendedor no se tocan entre sí: el gap
 * separa las columnas, pero no una rejilla de la siguiente (Carlos, 24-09). */
.grid + .vend-b__split,
.vend-b__split + .vend-b__split,
.vend-b__split + .card { margin-top: 16px; }
/* La franja de meses dentro de la tarjeta de Pedidos lleva el mismo aire que
 * el encabezado de la lista; en Devoluciones va fuera de tarjeta y no lo pide. */
.card > .devol__meses { padding: 16px 20px 0; }
@media (min-width: 900px) {
  .vend-b__split { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
}

/* La barra de «Cartera vencida»: un único listón partido en los tramos de
 * `AGING_BUCKETS` (D8), no tres barras sueltas como en la cartera del
 * cliente —acá hay que leerla de un vistazo, sin abrir la pestaña—. */
.vend-b__bar {
  display: flex; height: 8px; border-radius: 999px; overflow: hidden;
  background: var(--muted); margin: 10px 0 8px;
}
.vend-b__bar-seg { background: var(--tramo, var(--border-input)); min-width: 2px; }
.vend-b__bar-legend { display: flex; flex-wrap: wrap; gap: 6px 16px; margin: 0; padding: 0; list-style: none; }
.vend-b__bar-legend li { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted-foreground); }
.vend-b__bar-legend i { width: 8px; height: 8px; border-radius: 999px; background: var(--tramo, var(--border-input)); flex: none; }

/* La tarjeta oscura del «Producto líder», como la de la captura 14: es la
 * única tarjeta oscura de esta ficha, y por eso no hay un tono `.kpi--tono`
 * que la cubra —los tonos de `.kpi` son todos claros—. */
.vend-b__lider {
  background: #0f172a; color: #e2e8f0; border-color: #0f172a;
}
.vend-b__lider .kpi__label { color: #93a3bd; }
.vend-b__lider .vend-b__lider-valor {
  margin: 2px 0 0; font-size: 15px; font-weight: 600; color: #fff;
  overflow-wrap: anywhere;
}
.vend-b__lider .kpi__value { color: #7dd3fc; font-size: 20px; margin-top: 4px; }

/* La barrita azul de «Productos más vendidos», proporcional al que más
 * vendió y no un porcentaje del total: con ocho productos muy dispares, un
 * porcentaje deja siete barras invisibles. */
.vend-b__minibar-row { padding: 8px 0; }
.vend-b__minibar-row + .vend-b__minibar-row { border-top: 1px solid var(--border-soft); }
.vend-b__minibar-head { display: flex; justify-content: space-between; gap: 10px; font-size: 13px; }
.vend-b__minibar-head .num { font-weight: 600; }
.vend-b__minibar {
  height: 6px; border-radius: 999px; background: var(--muted); margin-top: 6px; overflow: hidden;
}
.vend-b__minibar-fill { display: block; height: 100%; background: var(--primary); border-radius: inherit; }

/* La píldora de tendencia del ranking de productos: gris «Sin comparación»
 * cuando el producto no se vendió en el período anterior —que es casi
 * siempre, con la historia que tiene SigoOne hoy—, verde o roja con el
 * cambio cuando sí hay con qué comparar. */
.vend-b__trend {
  display: inline-flex; align-items: center; gap: 4px; font-size: 11px;
  font-weight: 600; white-space: nowrap;
}
.vend-b__trend--none { color: var(--faint); font-weight: 500; }
.vend-b__trend--up { color: var(--success-text); }
.vend-b__trend--down { color: var(--danger-text); }

/* Las acciones rápidas del vendedor (D9): botones a lo ancho, apilados, como
 * la columna angosta de la captura. */
.vend-b__acciones { display: flex; flex-direction: column; gap: 8px; }
.vend-b__acciones .btn { width: 100%; justify-content: center; }
.vend-b__contexto-titulo {
  margin: 18px 0 4px; font-size: 11px; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--muted-foreground);
}

/* ── Solicitudes de cambio de precio (puntos 19 y 20 del levantamiento de
 * VENTAS.docx, 23-09-2026). Carrito propio de la pantalla del vendedor:
 * cada producto elegido es una tarjeta con su precio, su motivo y, si el
 * motivo lo pide, la evidencia. */
.precio__linea {
  border: 1px solid var(--border-soft); border-radius: 10px; padding: 10px 12px;
  margin-bottom: 10px; background: var(--muted);
}
.precio__linea-head {
  display: flex; justify-content: space-between; align-items: flex-start; gap: 8px;
}
.precio__linea-head .code { display: block; color: var(--muted-foreground); }
.precio__quitar {
  border: none; background: transparent; color: var(--muted-foreground);
  cursor: pointer; padding: 2px;
}
.precio__quitar:hover { color: var(--danger-text); }
/* **Fila y no rejilla de dos** (captura 20/21, l. 610-625 de la V4): precio
 * actual tachado a la izquierda, el input de lo pedido ocupando lo que
 * sobra, y la píldora «−X %» al final solo cuando aplica. */
.precio__linea-precios {
  display: flex; align-items: flex-end; gap: 10px; margin-top: 8px;
}
.precio__linea-actual { flex: none; display: block; }
.precio__linea-actual label,
.precio__linea-solicitado label { display: block; font-size: 11px; color: var(--muted-foreground); margin-bottom: 3px; }
.precio__linea-solicitado { flex: 1 1 auto; min-width: 0; margin: 0; }
.precio__actual {
  display: block; text-decoration: line-through; color: var(--muted-foreground);
  font-weight: 600; padding: 8px 0;
}
.precio__baja { flex: none; align-self: flex-start; margin-top: 18px; }
/* `.badge` pone su propio `display: inline-flex`, y esa clase —con la misma
 * especificidad que `[hidden]` del navegador pero declarada después en la
 * hoja— le gana: la píldora quedaba visible y vacía antes de haber una baja
 * de precio (Carlos lo vio en el Chrome, 24-09-2026). Mismo arreglo que ya
 * llevan `.notice[hidden]` y `.field[hidden]` más arriba en esta hoja. */
.precio__baja[hidden] { display: none !important; }
.precio__evidencia { margin-top: 10px; }
.precio__evidencia .field { margin-top: 6px; }
/* La zona de adjuntar, punteada en ámbar como la captura: el control sigue
 * siendo el `<input type="file">` de siempre —el sitio no tiene un
 * componente de arrastrar y soltar— pero se enmarca para que se note que va
 * junto con la advertencia de arriba. */
.precio__adjuntar {
  border: 1px dashed var(--warning); border-radius: var(--radius);
  background: var(--card); padding: 8px 10px;
}

/* ── Vitrina en filas (punto 19, captura 86.png): casilla, rubro en píldora,
 * nombre, precio y código, en vez de la tarjeta con foto de la toma de
 * pedidos —esa vitrina es de presentaciones con imagen; esta es de
 * productos sueltos y una lista se lee más rápido para escoger varios—. */
.precio__lista { display: flex; flex-direction: column; gap: 6px; max-height: 560px; overflow-y: auto; }
.precio__prod {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 9px 12px; border: 1px solid var(--border); border-radius: 10px;
  background: var(--card); text-align: left; cursor: pointer;
  transition: background .12s, border-color .12s;
}
.precio__prod:hover { border-color: var(--primary); }
.precio__prod:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }
.precio__prod--elegida {
  border-color: var(--primary) !important;
  background: var(--primary-soft) !important;
  box-shadow: 0 0 0 1px var(--primary);
}
/* La casilla se dibuja igual que la de un formulario (mismo gancho en
 * fondo), pero un poco más grande —20 px, como `size-5` de la captura— para
 * que se vea de lejos en una lista que se recorre rápido. */
.precio__prod-casilla {
  flex: none; width: 20px; height: 20px; border: 2px solid var(--border-input);
  border-radius: 5px; background: var(--card);
}
.precio__prod--elegida .precio__prod-casilla {
  border-color: var(--primary); background: var(--primary)
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E")
    center / 12px 12px no-repeat;
}
.precio__prod-rubro { flex: none; font-size: 11px; font-weight: 600; }
.precio__prod-nombre { flex: 1 1 auto; min-width: 0; font-size: 13.5px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.precio__prod-precio { flex: none; font-size: 12.5px; color: var(--muted-foreground); }
.precio__prod-codigo { flex: none; }
/* Siete tonos —los de `.mix__tramo`— cíclicos por identificador de rubro:
 * cada empresa tiene los rubros que tiene, así que no se copian los cinco
 * nombres de ejemplo de la V4 (Aceites, Lácteos…), que no son los de esta
 * base. El 0 es «sin rubro». */
.precio__prod-rubro--0 { background: var(--muted); color: var(--muted-foreground); }
.precio__prod-rubro--1 { background: rgba(37, 99, 235, .12); color: #1d4ed8; }
.precio__prod-rubro--2 { background: rgba(22, 163, 74, .12); color: #15803d; }
.precio__prod-rubro--3 { background: rgba(245, 158, 11, .16); color: #b45309; }
.precio__prod-rubro--4 { background: rgba(168, 85, 247, .12); color: #7e22ce; }
.precio__prod-rubro--5 { background: rgba(236, 72, 153, .12); color: #be185d; }
.precio__prod-rubro--6 { background: rgba(8, 145, 178, .12); color: #0e7490; }
.precio__prod-rubro--7 { background: rgba(100, 116, 139, .12); color: #475569; }

/* Las píldoras de rubro arriba de la lista, y el buscador arriba de todo
 * (captura 86.png). Reusa `.chip`/`.chip--on`, la misma piel que ya filtra
 * por sucursal o por lista de precio en otras pantallas: no hace falta un
 * componente de píldoras nuevo.
 *
 * **Una sola fila, con desplazamiento horizontal** (Carlos, 24-09-2026): con
 * los 45 rubros de la empresa, `flex-wrap: wrap` armaba seis renglones de
 * píldoras antes de llegar al primer producto. Misma solución que ya usa
 * `.topnav` en el celular —`overflow-x: auto` con la barra escondida—, y
 * «Todos» no se encoge porque siempre va primero. */
.precio__prod-rubros {
  display: flex; flex-wrap: nowrap; gap: 6px; margin-bottom: 12px;
  overflow-x: auto; scrollbar-width: none;
}
.precio__prod-rubros::-webkit-scrollbar { display: none; }
.precio__prod-rubros .chip { flex: none; }
/* `.search-wrap` es `inline-flex` (piensa en una barra de filtros, donde
 * mide lo que su contenido pide); acá tiene que ocupar la tarjeta entera. */
.precio__buscador { margin-bottom: 12px; }
.precio__buscador .search-wrap { display: flex; width: 100%; }

/* La cabecera de tres campos (Cliente, Vendedor, Motivo) y el cuerpo a dos
 * columnas comparten franja con la toma de pedidos: `.tomapedido` y
 * `.tomapedido__cabecera` ya resuelven la rejilla, y no hace falta una
 * copia solo para esta pantalla. */
.precio__cabecera { margin-bottom: 16px; }
.precio__panel { display: flex; flex-direction: column; min-height: 0; }
.precio__panel-cabeza { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.precio__panel-cabeza .card__title { margin: 0; border: 0; padding: 0; }
.precio__contador { font-size: 12px; }
/* El panel de la derecha se desplaza aparte cuando hay muchos productos
 * elegidos, sin arrastrar el buscador de la izquierda con él (captura
 * 86.png: «con su propio scroll si crece»). */
/* El aire entre el título «Productos seleccionados» y la primera tarjeta
 * (captura 20/21): sin esto quedaba pegada al título. */
.precio__panel-cuerpo { flex: 1 1 auto; overflow-y: auto; max-height: 560px; margin-top: 12px; }
.precio__vacio { padding: 2.5rem 1rem; }

/* El pie, apilado y no en fila: el botón azul ancho arriba y «Cancelar»
 * como texto centrado debajo, tal como la captura y no el par de botones
 * lado a lado que llevan los demás formularios. */
.precio__pie { display: flex; flex-direction: column; gap: 10px; margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--border-soft); }
.precio__pie .btn { width: 100%; justify-content: center; }
.precio__cancelar {
  text-align: center; font-size: 13px; color: var(--muted-foreground);
  text-decoration: none;
}
.precio__cancelar:hover { color: var(--foreground); }

/* ── Aprobación de cambios de precio (punto 20, captura 22): tarjeta
 * expandible por solicitud, con la tabla del producto y sus proveedores. Los
 * cuatro `KpiCard` de arriba reusan `.kpis`/`.kpi`, que ya existen. */
.precio__card { border: 1px solid var(--border-soft); border-radius: 12px; margin-bottom: 12px; }
.precio__card-head {
  padding: 12px 16px; cursor: pointer; display: flex; justify-content: space-between;
  align-items: center; gap: 12px; list-style: none;
}
.precio__card-head::-webkit-details-marker { display: none; }
.precio__card-head .code { color: var(--primary-text); font-weight: 700; }
.precio__chevron { transition: transform .15s ease; }
details[open] > .precio__card-head .precio__chevron { transform: rotate(180deg); }
.precio__card-body { padding: 0 16px 16px; border-top: 1px solid var(--border-soft); }
.precio__tabla { width: 100%; border-collapse: collapse; font-size: 13px; }
.precio__tabla th {
  text-align: left; font-size: 10px; text-transform: uppercase; color: var(--faint);
  padding: 6px 8px;
}
.precio__tabla td { padding: 6px 8px; vertical-align: top; border-top: 1px solid var(--border-soft); }
.precio__tabla tr.precio__fila--aprobada { background: var(--success-soft); }
.precio__tabla tr.precio__fila--rechazada { background: var(--danger-soft); }
.precio__proveedor-fila td { background: var(--muted); font-size: 12px; color: var(--muted-foreground); }
.precio__tachado { text-decoration: line-through; color: var(--muted-foreground); }
.precio__margen--bajo { color: var(--danger-text); font-weight: 600; }
.precio__margen--medio { color: var(--warning-text); font-weight: 600; }
.precio__margen--alto { color: var(--success-text); font-weight: 600; }
.precio__precio-aprobado { width: 100px; }
.precio__acciones-linea { display: flex; gap: 6px; align-items: center; }

/* ── Nuevo pedido: convenios en la tarjeta y carrito con pasos ───────────────
 *
 * Puntos 16 y 17 del levantamiento de Ventas (aprobado por Carlos el
 * 23-09-2026), captura 18 y 17 de la V4. */

/* Punto 16: la fila de una unidad y la fila verde del volumen pactado, dentro
 * de la tarjeta del producto. */
.ped__prodcard-precios { display: flex; flex-direction: column; gap: 4px; margin-top: 6px; }
.ped__prodcard-fila {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 4px 8px; border-radius: var(--radius-sm);
  background: var(--muted); font-size: 12px;
}
.ped__prodcard-fila--volumen { background: var(--success-soft); color: var(--success-text); }
.ped__prodcard-fila .num { display: flex; align-items: center; gap: 6px; }
.ped__prodcard-tachado { color: var(--muted-foreground); font-weight: 400; }

/* «Ya está en el carrito»: el visto arriba a la derecha de la tarjeta, el aro
 * azul alrededor y la leyenda al pie. Empiezan escondidos —los marca
 * `carrito.js`—. El visto va sobre la tarjeta entera y no sobre la foto: esa
 * regla ya tiene `overflow: hidden` y no es de este agente. */
.ped__prodcard { position: relative; }
.ped__prodcard--puesta { border-color: var(--primary); box-shadow: 0 0 0 2px var(--ring); }
.ped__prodcard-visto {
  position: absolute; top: 8px; right: 8px; width: 22px; height: 22px;
  border-radius: 999px; background: var(--primary); color: #fff;
  display: grid; place-items: center; z-index: 1;
}
.ped__prodcard-visto .icon { width: 14px; height: 14px; }
.ped__prodcard-en-carrito { font-size: 11.5px; font-weight: 600; color: var(--primary-text); }
/* `hidden` tiene que ganar: el `display` de arriba lo pisaba y la marca de
 * «en el carrito» salía en todas las tarjetas con el carrito vacío. */
.ped__prodcard-visto[hidden],
.ped__prodcard-en-carrito[hidden] { display: none; }

/* Punto 17: el renglón del carrito con pasos, precio con IVA y total. */
/* Como la captura 18 (Carlos, 24-09-2026): cada producto es una tarjeta
 * —fondo claro, borde, esquinas redondas— y todo va en **una sola
 * fila**: nombre y precio, los pasos, el total y la ×. Era una rejilla de tres
 * columnas con cuatro piezas, y la × caía sola en otra línea. */
.carrito__linea--pasos {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 14px; margin-bottom: 8px;
  background: var(--card); border: 1px solid var(--border); border-radius: 14px;
}
.carrito__linea--pasos .carrito__info { flex: 1; }
.carrito__linea--pasos .field__check { flex: none; }
.carrito__paso {
  width: 30px; height: 30px; flex: none;
  display: grid; place-items: center;
  border: 1px solid var(--border-input); border-radius: 8px;
  background: var(--card); color: var(--foreground);
  font-size: 16px; line-height: 1; cursor: pointer;
}
.carrito__paso:hover { background: var(--muted); }
.carrito__paso:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
/* La cantidad se lee como en la captura —un número entre los dos pasos—,
 * pero se puede escribir: sin caja, centrada y sin las flechas del navegador.
 * Lleva `.carrito__pasos` delante para ganarle a `.form input[type=number]`. */
.carrito__pasos .carrito__num.carrito__num--pasos {
  width: 40px; max-width: 40px; padding: 0; border: 0; background: transparent;
  text-align: center; font-weight: 600; font-size: 14px; color: var(--foreground);
  -moz-appearance: textfield; appearance: textfield;
}
.carrito__num--pasos::-webkit-outer-spin-button,
.carrito__num--pasos::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.carrito__num--pasos:focus { outline: 1px solid var(--primary); border-radius: 6px; }
.carrito__info { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.carrito__info .carrito__nombre { font-size: 13px; color: var(--foreground); font-weight: 500; }
.carrito__sub { font-size: 11.5px; color: var(--muted-foreground); }
.carrito__pasos { display: flex; align-items: center; gap: 6px; flex: none; }
/* Ancho fijo para que los pasos queden en columna aunque el total cambie. */
.carrito__total-linea { width: 112px; text-align: right; flex: none; white-space: nowrap; }

/* ── Punto 31: filtros estándar dentro de la tabla ───────────────────────────
 * La píldora «Fecha» de `filters_bare.html` reusa `.fdrop`/`.chip`, el mismo
 * panel que un `filter_dropdown`: solo cambia lo que hay adentro, dos campos
 * en vez de casillas, porque acá no hay opciones que buscar.
 */
.fdrop__fecha { display: flex; flex-direction: column; gap: 10px; padding: 6px 14px 10px; }
.fdrop__fecha label { display: flex; flex-direction: column; gap: 4px; }
.fdrop__fecha label > span { font-size: 12px; font-weight: 500; color: var(--muted-foreground); }
.fdrop__fecha .input--date { width: 100%; min-width: 0; }

/* ── Punto 33: los bloques nuevos de «Control de ventas» ────────────────────
 *
 * La captura image36 del levantamiento de VENTAS.docx (24-09-2026), bajo
 * «Desempeño por vendedor»: pedido vs. facturado, la fila de tres
 * (devoluciones, electrónica vs. proforma, rechazadas por Hacienda),
 * productos con mayor recompra y desempeño por ruta. Reusa `.desempeno__*`
 * para la caja, la tabla, el riel y el vacío; acá solo lo que el prototipo
 * dibuja distinto, con el mismo prefijo `.ventas-ind__` del resto de esta
 * pantalla.
 */

/* «Pedido vs. facturado» ---------------------------------------------------- */

.ventas-ind__pedfact-pct { text-align: right; }
.ventas-ind__pedfact-valor {
  display: block; font-size: 24px; line-height: 32px; font-weight: 700;
  color: var(--success-text); font-variant-numeric: tabular-nums;
}
.ventas-ind__pedfact-rotulo { margin: 0; font-size: 12px; line-height: 16px; color: var(--muted-foreground); }

.ventas-ind__pedfact-fila { margin-top: 20px; }
.ventas-ind__pedfact-fila + .ventas-ind__pedfact-fila { margin-top: 16px; }
.ventas-ind__pedfact-cabeza {
  display: flex; align-items: baseline; justify-content: space-between;
  margin-bottom: 6px; font-size: 13px; line-height: 18px;
}
.ventas-ind__pedfact-cabeza span { color: var(--muted-foreground); }
.ventas-ind__pedfact-cabeza strong {
  font-weight: 600; color: var(--foreground); font-variant-numeric: tabular-nums;
}
.ventas-ind__pedfact-riel { flex: none; width: 100%; height: 10px; min-width: 0; }
.ventas-ind__pedfact-relleno--pedido,
.ventas-ind__pedfact-relleno--facturado { height: 100%; border-radius: 999px; }
.ventas-ind__pedfact-relleno--pedido { background: #99a1af; }
.ventas-ind__pedfact-relleno--facturado { background: var(--primary); }
.ventas-ind__pedfact-pie {
  display: flex; align-items: center; gap: 6px; margin: 16px 0 0;
  font-size: 12px; line-height: 16px; color: var(--muted-foreground);
}
.ventas-ind__pedfact-pie .icon { font-size: 14px; color: var(--faint); }

/* La fila de tres: devoluciones, electrónica vs. proforma, rechazadas. */
.ventas-ind__fila-tres {
  display: grid; grid-template-columns: minmax(0, 1fr); gap: 20px;
}
@media (min-width: 1024px) {
  .ventas-ind__fila-tres { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* El «Ver →» de las tres tarjetas, igual que en Compras › Indicadores. */
.ventas-ind__ver {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 12px; line-height: 16px; font-weight: 600;
  color: var(--primary-text); text-decoration: none;
}
.ventas-ind__ver:hover { color: var(--primary); }
.ventas-ind__ver .icon { font-size: 14px; }
.ventas-ind__ver--ambar { margin-top: 16px; color: #973c00; }
.ventas-ind__ver--ambar:hover { color: #7a3000; }

/* «Devoluciones» */
.ventas-ind__monto {
  margin: 12px 0 0; font-size: 24px; line-height: 32px; font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.ventas-ind__monto--rojo { color: var(--danger-text); }

/* «Electrónica vs. proforma»: la barra partida azul/gris. */
.ventas-ind__split {
  display: flex; height: 10px; margin-top: 20px; border-radius: 999px; overflow: hidden;
  background: var(--muted);
}
.ventas-ind__split-tramo:first-child { border-radius: 999px 0 0 999px; }
.ventas-ind__split-tramo:last-child:not(:first-child) { border-radius: 0 999px 999px 0; }
.ventas-ind__split-tramo--azul { background: var(--primary); }
.ventas-ind__split-tramo--gris { background: #d1d5dc; }
.ventas-ind__split-leyenda {
  display: flex; justify-content: space-between; margin-top: 16px;
  font-size: 12px; line-height: 16px;
}
.ventas-ind__split-derecha { text-align: right; }
.ventas-ind__split-leyenda p:first-child {
  display: flex; align-items: center; gap: 6px; margin: 0; color: var(--muted-foreground);
}
.ventas-ind__split-derecha p:first-child { justify-content: flex-end; }
.ventas-ind__split-cifra {
  margin: 4px 0 0; font-size: 16px; line-height: 22px; font-weight: 700;
  color: var(--foreground); font-variant-numeric: tabular-nums;
}
.ventas-ind__split-cifra span { font-size: 12px; font-weight: 500; color: var(--muted-foreground); }
.ventas-ind__punto { display: inline-block; width: 8px; height: 8px; border-radius: 50%; }
.ventas-ind__punto--azul { background: var(--primary); }
.ventas-ind__punto--gris { background: #d1d5dc; }

/* «Rechazadas por Hacienda»: la tarjeta ámbar.
 *
 * **Selector compuesto y no `.ventas-ind__rechazadas` sola.** Esta hoja se
 * importa al principio de `sigoone.css` y `.desempeno__caja` —que pinta el
 * fondo y el borde de toda caja— se declara después: con la misma
 * especificidad (una clase), gana quien viene último en el archivo, que es
 * `.desempeno__caja`. Un selector de dos clases pesa más y no depende del
 * orden de las hojas. */
.desempeno__caja.ventas-ind__rechazadas {
  /* El mismo ámbar de `.desempeno__nivel--atencion` («Foco del día»), para
   * que el tono de una excepción se lea igual en toda la pantalla. */
  background: #fffbeb; border-color: #fef3c6;
}
.ventas-ind__rechazadas-titulo { display: flex; align-items: center; gap: 8px; }
.ventas-ind__rechazadas-titulo .icon { font-size: 18px; color: #973c00; }
.ventas-ind__rechazadas-titulo h2 { color: #973c00; }
.ventas-ind__rechazadas-num {
  margin: 12px 0 0; font-size: 28px; line-height: 34px; font-weight: 700;
  color: var(--foreground); font-variant-numeric: tabular-nums;
}

/* «Productos con mayor recompra» */
.ventas-ind__recompra { display: flex; flex-direction: column; gap: 16px; margin-top: 20px; }
.ventas-ind__recompra-fila { display: flex; align-items: center; gap: 12px; }
.ventas-ind__recompra-num {
  flex: none; display: flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; font-size: 12px; font-weight: 600; color: var(--faint);
}
.ventas-ind__recompra-cuerpo { flex: 1; min-width: 0; }
.ventas-ind__recompra-cabeza {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  margin-bottom: 6px; font-size: 13px; line-height: 18px;
}
.ventas-ind__recompra-cabeza strong {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-weight: 600; color: var(--foreground);
}
.ventas-ind__recompra-cabeza span { flex: none; color: var(--muted-foreground); }

 *
 * Punto 20 de VENTAS (24-09-2026, Carlos: «que quede exacto a la captura»,
 * captura 90 = `SolicitudPrecioCard` de la V4, l. 2237-2445). Las reglas de
 * `.precio__*` que ya trae `sigoone.css` (bloque «Aprobación de cambios de
 * precio») se quedan; acá solo se agrega lo que falta y se **ajusta** lo que
 * ya existe.
 *
 * Se importa antes que el resto de la hoja (regla del proyecto), así que
 * cualquier ajuste a una clase que `sigoone.css` ya declara —el color del
 * input, el tamaño de los botones de la fila— va con un selector más
 * específico (dos clases o más) para ganarle por peso y no por orden: si
 * fuera igual de específico, la regla declarada después en `sigoone.css`
 * mandaría, no ésta.
 */

/* ── Cabecera de la tarjeta: dos bloques, no tres sueltos ───────────────────
 * (captura 90). El bloque de la izquierda —id, badge, fecha, cliente y
 * motivo— y a la derecha las dos columnas (Vendedor / Productos) junto con
 * el chevron, agrupadas para que el `space-between` de `.precio__card-head`
 * no las separe entre sí. */
.precio__card-info { flex: 1 1 auto; min-width: 0; }
.precio__card-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.precio__card-cliente { display: block; margin-top: 3px; font-size: 13.5px; }
.precio__card-motivo { font-size: 12px; margin-top: 1px; }
.precio__card-right { display: flex; align-items: center; gap: 20px; flex: none; margin-left: 12px; }
.precio__card-meta { display: flex; gap: 20px; }
.precio__card-col { text-align: right; white-space: nowrap; }
.precio__card-col .muted { font-size: 11px; margin-bottom: 2px; }
.precio__card-col-valor { font-weight: 700; }

/* La tarjeta lleva sombra suave, como toda tarjeta `.card` de la aplicación
 * —`.precio__card` no la traía—, y el borde un tono más marcado que
 * `--border-soft` para que se note contra el fondo, tal como la captura. */
.sheet .precio__card { border-color: var(--border); box-shadow: var(--shadow-sm); }

/* ── Producto / proveedor (captura 90, tarea 4) ──────────────────────────────
 * Nombre en negrita, código debajo en gris y mono —texto plano, no la ficha
 * con fondo de `.code`, que es de otra familia visual— y, si hay más de un
 * proveedor, «N proveedores ↓» en azul debajo de eso. */
.precio__fila-nombre { font-weight: 600; font-size: 12.5px; line-height: 1.35; color: var(--foreground); }
.precio__fila-codigo { font-family: var(--font-mono); font-size: 10.5px; color: var(--faint); margin-top: 1px; }
.precio__fila-proveedores { font-size: 10.5px; color: var(--primary-text); font-weight: 600; margin-top: 3px; }

/* ── Motivo / evidencia (captura 90, tarea 3) ────────────────────────────────
 * Competidor y Volumen ya salen en rojo y azul con `.badge--danger` y
 * `.badge--primary` —los mismos tonos que pide la V4—; acá sólo falta el
 * gris neutro para los motivos que la V4 no distingue, y el bloque de
 * evidencia (texto, imagen o archivo). */
.precio__motivo { font-style: normal; }
.precio__tabla .precio__badge--neutral { background: var(--muted); color: var(--muted-foreground); }
.precio__motivo-vacio { font-style: italic; }
.precio__motivo-texto { font-size: 11px; margin-top: 3px; }
.precio__motivo-imagen {
  display: block; margin-top: 5px; width: 64px; height: 40px;
  border-radius: 8px; overflow: hidden; border: 1px solid var(--border-soft);
}
.precio__motivo-imagen img { width: 100%; height: 100%; object-fit: cover; display: block; }
.precio__motivo-archivo {
  display: flex; align-items: center; gap: 4px; margin-top: 5px;
  font-size: 11px; color: var(--success-text);
}
.precio__motivo-archivo .icon { font-size: 12px; }

/* P. Vendedor en azul negrita (captura 90, tarea 5): la única celda de
 * precio de la fila que no era ni tachada ni de estado se quedaba en el
 * color del texto corriente. */
.precio__tabla .precio__vendedor-precio { color: var(--primary-text); font-weight: 700; }

/* ── P. Aprobado: el input, ancho, con borde gris claro y el número
 * centrado (captura 90, tarea 1). `.precio__precio-aprobado` ya existe en
 * `sigoone.css` con solo el ancho —heredaba el borde de fábrica del
 * navegador porque el input vive fuera de un `.form`—; acá se completa. */
.precio__tabla td .precio__precio-aprobado {
  width: 100%; max-width: 112px; height: 28px; padding: 0 8px;
  border: 1px solid var(--border-input); border-radius: 8px;
  background: var(--card); color: var(--foreground);
  font-size: 12.5px; font-weight: 600; text-align: center;
}
.precio__tabla td .precio__precio-aprobado:focus {
  outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--ring);
}
.precio__tabla td .precio__precio-aprobado:disabled {
  background: var(--muted); color: var(--muted-foreground);
}

/* ── Estado: Aprobar primero, × después (captura 90, tarea 2) ───────────────
 * El botón verde se queda del tamaño chico de siempre; el que
 * rechaza se achica a un cuadrado pequeño de esquinas redondas —el `size-6
 * rounded-lg` de la V4— para que no compita en peso con «Aprobar». */
.precio__acciones-linea .precio__btn-rechazar {
  width: 26px; height: 26px; padding: 0; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 8px;
}
.precio__acciones-linea .precio__btn-aprobar {
  height: 26px; padding: 0 10px; font-size: 12px; border-radius: 8px; gap: 4px;
}
/* El formulario de rechazo se queda en flujo normal —abre dentro de la
 * celda, como ya hacía antes de este punto— y no como ventana flotante: la
 * fila vive dentro de `.table-wrap`, que se desplaza en el eje horizontal
 * (`overflow-x: auto`) y por eso también recorta el vertical; una ventana
 * `position: absolute` ahí se cortaría o forzaría un scroll dentro de la
 * fila en vez de sobre ella. */
.precio__rechazar[open] > .field { margin-top: 8px; max-width: 220px; }

/* Ajuste fino contra la captura 90 (24-09-2026): código de la solicitud en
 * letra normal, azul y negrita; encabezado de la tabla en franja gris; filas
 * con más aire; montos a la derecha y porcentajes, precio aprobado y estado
 * al centro, como en la captura; «mejor» chico y gris bajo el costo. */
.precio__card-codigo { color: var(--primary-text); font-weight: 700; font-size: 15px; }
.precio__tabla thead th { background: var(--muted); padding-top: 10px; padding-bottom: 10px; }
.precio__tabla > tbody > tr > td { padding-top: 12px; padding-bottom: 12px; vertical-align: middle; }
.precio__tabla > tbody > tr:not(.precio__proveedor-fila) > td:nth-child(n+3):nth-child(-n+6),
.precio__tabla thead th:nth-child(n+3):nth-child(-n+6) { text-align: right; }
.precio__tabla > tbody > tr:not(.precio__proveedor-fila) > td:nth-child(n+7),
.precio__tabla thead th:nth-child(n+7) { text-align: center; }
.precio__tabla > tbody > tr.precio__proveedor-fila > td { padding-top: 8px; padding-bottom: 8px; }
.precio__tabla > tbody > tr.precio__proveedor-fila > td:nth-child(2),
.precio__tabla > tbody > tr.precio__proveedor-fila > td:nth-child(3) { text-align: right; }
.precio__tabla td .muted { font-size: 10.5px; color: var(--muted-foreground); }
.precio__tabla td .precio__acciones-linea { justify-content: center; }

/*
 * «Ruta de ventas» (punto 35 del levantamiento de VENTAS.docx). Clases con
 * el prefijo `r35__` para no chocar con las de otras pantallas que se
 * construyen en paralelo. Lo que ya existe se reusa tal cual: `.kpi`,
 * `.kpis`, `.badge`, `.stops`/`.stop`, `.card`.
 */

.r35__fecha {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 16px;
}

/* Solo la primera letra: `capitalize` volvía «De» el «de» de la fecha. */
.r35__fecha-texto { display: inline-block; }
.r35__fecha-texto::first-letter { text-transform: uppercase; }

.r35__kpis {
  margin-bottom: 16px;
}

.r35__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 16px;
}

.r35__tarjeta {
  display: flex;
  flex-direction: column;
  gap: 10px;
  text-decoration: none;
  color: inherit;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.r35__tarjeta:hover {
  border-color: var(--primary);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}

.r35__tarjeta-cabeza {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.r35__tarjeta-codigo {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  font-size: 12px;
}

.r35__tarjeta-codigo .icon {
  width: 14px;
  height: 14px;
}

.r35__tarjeta-datos {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  font-size: 13px;
}

.r35__barra {
  height: 8px;
  border-radius: 999px;
  background: var(--muted);
  overflow: hidden;
}

.r35__barra-rellena {
  height: 100%;
  border-radius: 999px;
}

.r35__barra-rellena--verde { background: var(--success); }
.r35__barra-rellena--azul  { background: var(--primary); }
.r35__barra-rellena--ambar { background: var(--warning-text); }

.r35__resumen {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 16px;
  margin-bottom: 16px;
}

.r35__resumen-caja {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.r35__resumen-cifra {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* Las tarjetas lado a lado arrancan a la misma altura (le ganan al margen de
 * «.card + .card»), y el porcentaje no se parte en dos renglones. */
.r35__grid > .card + .card,
.r35__resumen > .card + .card { margin-top: 0; }
.r35__tarjeta-cabeza .badge { white-space: nowrap; flex: none; }
/* Rótulo arriba y cifra abajo: en línea, «0 de 0» se partía en dos renglones. */
.r35__tarjeta-datos > div { display: flex; flex-direction: column; gap: 2px; }
.r35__tarjeta-datos > div:last-child { text-align: right; }

/* Punto 5 del levantamiento de COMPRAS (Carlos, 24-09-2026): la foto junto
   al nombre en el catálogo de productos. Mismo recuadro de 40 px, redondeado,
   que la miniatura del alta (Figma V4:142-144); con el ícono de paquete
   cuando el producto no tiene foto todavía. */
.cr2__fila-nombre {
  display: flex; align-items: center; gap: 10px; text-decoration: none;
}
.cr2__foto-mini {
  display: flex; align-items: center; justify-content: center; flex: 0 0 auto;
  width: 40px; height: 40px; border-radius: 10px;
  background: var(--muted); overflow: hidden;
}
.cr2__foto-mini img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cr2__foto-mini .icon { width: 18px; height: 18px; color: var(--faint); }

/*
 * Reposición (puntos 16 y 17 de COMPRAS, 24-09-2026).
 *
 * Reusa `.chip`/`.chip--on` de siempre para el filtro «Urgente» de la
 * vitrina de la orden de compra; solo agrega el tono rojo cuando está
 * encendido, con las mismas variables que ya usa `.badge--danger`.
 */
.chip--urgent.chip--on {
  background: var(--danger);
  border-color: var(--danger);
  color: #fff;
}

/* Las tarjetas de Reposición, calcadas de la V4 (V4:2648-2790) y de la
 * captura del punto 16 (revisado el 25-09-2026): la cabecera teñida con un
 * punto del color de la peor urgencia del proveedor, y la tabla en blanco
 * debajo —no la tarjeta entera teñida ni una franja al costado—. */
.reposicion__cards {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.replenishment-card { overflow: hidden; padding: 0; }
.replenishment-card--urgente { border-color: #fecaca; }
.replenishment-card--alta { border-color: #fde68a; }
.replenishment-card__head {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 20px; background: #f9fafb;
}
.replenishment-card--urgente .replenishment-card__head { background: #fef2f2; }
.replenishment-card--alta .replenishment-card__head { background: #fffbeb; }
.replenishment-card__dot {
  flex: none; width: 10px; height: 10px; border-radius: 999px; background: #9ca3af;
}
.replenishment-card--urgente .replenishment-card__dot { background: #ef4444; }
.replenishment-card--alta .replenishment-card__dot { background: #f59e0b; }
.replenishment-card__who { flex: 1; min-width: 0; }
.replenishment-card__title { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.replenishment-card__name { font-size: 14px; font-weight: 700; color: var(--foreground); }
.replenishment-card__code { font-family: var(--font-mono); font-size: 10px; color: var(--faint); }
.replenishment-card__count { font-size: 12px; color: var(--faint); }
.replenishment-card__fact { display: inline-flex; align-items: center; gap: 4px; }
.replenishment-card__fact .icon { width: 12px; height: 12px; color: var(--faint); }
.replenishment-card__fact strong,
.replenishment-card__days { font-weight: 600; color: #1f2937; }
.replenishment-card__days { font-weight: 500; color: #374151; }
.replenishment-card__when--today { color: #047857 !important; }
.replenishment-card__when--soon { color: #b45309 !important; }
.replenishment-card__today {
  padding: 2px 6px; border-radius: 999px; font-size: 10px; font-weight: 700;
  background: #d1fae5; color: #047857;
}
.replenishment-card__faint { color: var(--faint); }
.replenishment-card__sep { color: #d1d5db; }
.replenishment-card .table-wrap { background: var(--card); border-radius: 0; }
.replenishment-card .reposicion__meta { gap: 12px; font-size: 12px; margin-top: 2px; }
.replenishment-table thead th {
  background: #f9fafb; text-align: left; text-transform: uppercase; letter-spacing: .04em;
  font-size: 11px; font-weight: 700; color: var(--muted-foreground);
}
.replenishment-table tbody td { font-size: 12px; font-variant-numeric: tabular-nums; }
.replenishment-table__sku { font-family: var(--font-mono); font-weight: 700; color: var(--primary); }
.replenishment-table__product { font-size: 14px !important; font-weight: 500; color: var(--foreground) !important; }
.replenishment-table__stock { display: inline-flex; align-items: center; gap: 8px; }
.replenishment-table__stock-danger { color: #dc2626; }
.replenishment-table__stock-warning { color: #d97706; }
.replenishment-table__stock-success { color: #1f2937; }
.replenishment-table__bar {
  display: inline-block; width: 40px; height: 6px; border-radius: 999px; background: #f3f4f6;
  overflow: hidden;
}
.replenishment-table__bar-fill { display: block; height: 100%; border-radius: 999px; }
.replenishment-table__bar-fill--danger { background: #f87171; }
.replenishment-table__bar-fill--warning { background: #fbbf24; }
.replenishment-table__bar-fill--success { background: #34d399; }
.replenishment-table__qty { font-weight: 600; color: #374151 !important; }
.replenishment-table__deficit { font-weight: 700; color: #dc2626 !important; }
.replenishment-table__order { font-weight: 700; color: #1d4ed8 !important; }
.replenishment-table__subtotal { font-weight: 600; color: var(--foreground) !important; }
/* Traslado sugerido (regla 13 de INVENTARIOS, 28-09-2026): morado, el color
   del tipo «Transferencia» en Movimientos. */
.replenishment-card--transfer { border-color: #e9d5ff; }
.replenishment-card--transfer .replenishment-card__head { background: #faf5ff; }
.replenishment-card--transfer .replenishment-card__dot { background: #a855f7; }
.replenishment-table__transfer {
  display: block; margin-top: 2px; font-size: 11px; font-weight: 600; color: #7e22ce;
}
.replenishment-table__transfer-qty { font-weight: 700; color: #7e22ce !important; }
.replenishment-figure--blue { background: #eff6ff; border-color: #dbeafe; }
.replenishment-figure--blue .kpi__label { color: #3b82f6; }
.replenishment-figure--blue .kpi__value { color: #1d4ed8; }
.replenishment-figure--blue .kpi__hint { color: #60a5fa; }
.replenishment-figure--red { background: #fef2f2; border-color: #fee2e2; }
.replenishment-figure--red .kpi__label { color: #ef4444; }
.replenishment-figure--red .kpi__value { color: #b91c1c; }
.replenishment-figure--red .kpi__hint { color: #f87171; }

.reposicion__meta {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
  color: var(--muted-foreground);
  font-size: 13px;
  margin-top: 6px;
}
.reposicion__pedir-hoy {
  color: var(--success, #16a34a);
  font-weight: 600;
}
.reposicion__kpis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 12px;
  margin-bottom: 16px;
}

/* Filtro «Con quién» de Tipos de acuerdo comercial (punto 9 de COMPRAS,
 * 24-09-2026): el maestro pasó a compartirse entre cliente y proveedor, y
 * este filtro deja verlos por separado sin abrir un tipo a la vez. */
.agr-scope-filter { display: inline-flex; gap: 6px; }
.agr-scope-filter__pill {
  display: inline-flex; align-items: center; height: 36px; padding: 0 14px;
  border: 1px solid var(--border-input); border-radius: var(--radius-pill);
  background: var(--card); color: var(--muted-foreground);
  font-size: 13px; font-weight: 600; text-decoration: none;
  transition: background .12s, color .12s, border-color .12s;
}
.agr-scope-filter__pill:hover { background: var(--muted); text-decoration: none; }
.agr-scope-filter__pill--on, .agr-scope-filter__pill--on:hover {
  background: var(--primary); border-color: var(--primary);
  color: var(--primary-foreground);
}

/* Las píldoras de estado de la lista de órdenes (punto 18, D18.2). */
.chips { display: flex; flex-wrap: wrap; gap: 8px; }

/* La fila entera de la lista de órdenes abre la ficha (punto 14 y 18,
 * V4:834): el enlace del código se estira con `::after` sobre todo el
 * renglón, así que las demás celdas no necesitan su propio enlace. */
.po-orders-row { position: relative; }
.po-orders-row:hover { background: var(--muted); }
.po-orders-row__link::after { content: ""; position: absolute; inset: 0; }

/* Cabecera de la orden de compra, a la rejilla de la V4 (punto 15, captura 20):
 * el proveedor a lo ancho doble, y una segunda fila más angosta y discreta
 * para la bodega que recibe y el tipo de compra (D15.3 del levantamiento). */
.tomapedido__cabecera--oc .field:first-child { grid-column: span 2; }
@media (max-width: 640px) {
  .tomapedido__cabecera--oc .field:first-child { grid-column: span 1; }
}
.tomapedido__cabecera--secundaria {
  margin-top: 10px;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
}
.tomapedido__cabecera--secundaria .field { opacity: .88; }

/* La línea de tiempo de la orden (punto 19, «Progreso de la orden»). */
.oc-timeline { display: flex; flex-wrap: wrap; gap: 0; margin: 4px 0 20px; }
.oc-step {
  flex: 1 1 0; min-width: 108px; display: flex; flex-direction: column;
  align-items: center; text-align: center; gap: 6px; position: relative;
}
.oc-step__dot {
  width: 28px; height: 28px; border-radius: 999px; display: flex;
  align-items: center; justify-content: center; font-size: 12px; font-weight: 700;
  background: var(--card); border: 2px solid var(--border-input); color: var(--muted-foreground);
  z-index: 1;
}
.oc-step__dot .icon { width: 15px; height: 15px; }
.oc-step__line {
  position: absolute; top: 14px; left: -50%; width: 100%; height: 2px;
  background: var(--border-input); z-index: 0;
}
.oc-step:first-child .oc-step__line { display: none; }
.oc-step__label { font-size: 12px; font-weight: 600; color: var(--foreground); }
.oc-step__meta { font-size: 11px; color: var(--muted-foreground); }
.oc-step--done .oc-step__dot { background: var(--success-text); border-color: var(--success-text); color: #fff; }
.oc-step--done .oc-step__line { background: var(--success-text); }
.oc-step--active .oc-step__dot { background: var(--primary); border-color: var(--primary); color: #fff; }
.oc-step--partial .oc-step__dot { background: var(--warning-soft); border-color: var(--warning-text); color: var(--warning-text); }
.oc-step--cancelled .oc-step__dot { background: var(--danger-soft); border-color: var(--danger); color: var(--danger); }
.oc-step--cancelled .oc-step__label { color: var(--danger-text); }

/* La franja del otro proveedor más barato, en la tarjeta de la vitrina
 * (punto 15, D15.6). */
.prodcard__hint {
  display: block; font-size: 11px; color: var(--warning-text);
  background: var(--warning-soft); border-radius: 6px; padding: 2px 6px;
  margin: 2px 0; width: fit-content;
}

/* La ficha de envío (punto 22): el enlace de WhatsApp, mostrado como aviso
 * antes de confirmar que se mandó. */
.oc-send-link {
  display: flex; align-items: center; gap: 8px; margin: 4px 0 0;
  padding: 10px 12px; border-radius: 10px; background: var(--muted);
  font-size: 13px; word-break: break-all;
}

/* ── Stock (punto 23 de COMPRAS): ficha del artículo ───────────────────── */
/* Los dos gráficos de treinta días, 2/3 y 1/3 como el Figma (V4:2365). No
   hay una clase de grid así en sigoone.css (.grid--3 reparte parejo). */
.stock-detalle__graficos {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 1rem;
}
@media (max-width: 900px) {
  .stock-detalle__graficos {
    grid-template-columns: 1fr;
  }
}
/* **Los dos gráficos arrancan a la misma altura, y los bloques de la ficha se
 * separan como en la captura** (image4, auditoría 27-09-2026, detalle 1 del
 * punto 4). Las dos tarjetas de adentro vienen de `components/grafico.html`,
 * así que la prueba que busca rejillas con tarjetas en las plantillas no las
 * ve: sin esta línea, `.card + .card` bajaba la segunda 16 px. Y la rejilla
 * no llevaba aire ni con la cabecera de arriba ni con los totales de abajo. */
.stock-detalle__graficos > .card + .card { margin-top: 0; }
.card + .stock-detalle__graficos,
.stock-detalle__graficos + .grid { margin-top: 16px; }
/* Los encabezados del historial en MAYÚSCULAS y negrita, como la captura
 * (V4:309, `uppercase tracking-wide font-bold`; detalle 5). Modificador: el
 * resto de las tablas sigue sin versalitas (ver `.table thead th`). */
/* La cabecera de la lista de Stock en dos filas, como la captura (image3,
 * detalle 7): título y buscador arriba, los tres desplegables abajo, y las
 * píldoras de Sucursal con «Descargar» al final de esa segunda fila. */
.stock-head__row {
  flex-basis: 100%; display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
}
.stock-head__filters {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin: 0;
}
.stock-head__tools {
  margin-left: auto; display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
}
.stock-head__tools .filters__download { margin-left: 0; }
/* La ubicación de la lista: texto mono gris, sin la cajita de `.code` (V4:450,
 * `font-mono text-xs text-gray-500`; detalle 8). */
.code.code--plain { background: none; padding: 0; font-size: 12px; color: var(--muted-foreground); }
.table--caps thead th { text-transform: uppercase; letter-spacing: .025em; font-weight: 700; }

/* El aviso «próximos a vencer» de Stock, rejilla de 3 como el Figma
   (V4:2485); la de Existencias en Bodega la deja de 1 (tira) porque cabe
   menos ahí. */
.aviso-vence__tira--3 {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: .75rem;
}
@media (max-width: 900px) {
  .aviso-vence__tira--3 {
    grid-template-columns: 1fr;
  }
}

/* La leyenda al pie de la tabla de Stock (V4:2568-2573): mismo patrón que
   `.desempeno__leyenda`, con sus propios puntos de color porque acá son
   cuatro conceptos —disponible, comprometido, tránsito, proyectado— y no
   dos series de un gráfico. */
.stock-leyenda {
  display: flex; flex-wrap: wrap; align-items: center; gap: 12px 20px;
  padding: 10px 20px; border-top: 1px solid var(--border);
  font-size: 12px; line-height: 16px; color: var(--muted-foreground);
}
.stock-leyenda span { display: inline-flex; align-items: center; gap: 6px; }
.stock-leyenda .dot--gray   { background: var(--foreground); }
/* Naranja de la V4 (`bg-orange-400`), no el ámbar de `--warning` (auditoría
   25-09-2026). */
.stock-leyenda .dot--orange { background: #fb923c; }
.stock-leyenda .dot--blue   { background: var(--primary); }
/* «Proyectado» va más claro que «Disponible» en la V4 (`bg-gray-400` contra
   `bg-gray-900`): con el mismo punto oscuro de Disponible no se distinguían
   (auditoría 25-09-2026). */
.stock-leyenda .dot--gray-claro { background: #9ca3af; }

/* Las filas agotadas y las próximas a vencer se tiñen (V4:2537), como ya
   hace la vitrina de productos con lo que no compra. */
.row-tint--danger  { background: color-mix(in srgb, var(--danger) 6%, transparent); }
.row-tint--warning { background: color-mix(in srgb, #f97316 8%, transparent); }

/* Casi toda la fila de Stock lleva a la ficha del artículo, como la V4
   (auditoría 25-09-2026): SKU y nombre son enlaces «de verdad»; las demás
   celdas de solo lectura se envuelven en `.row-link`, del mismo color que el
   texto que traían y sin subrayado, para que el clic funcione sin que la
   celda se vea distinta. */
.row-link {
  display: block; color: inherit; text-decoration: none;
}
.row-link:hover { text-decoration: none; }

/* El tipo del kardex, texto de color por tipo (`stockMovData.ts` de la V4,
   auditoría 25-09-2026): ya no una insignia verde/roja por el signo. Ajuste
   se reparte en dos —suma o resta— porque es el único tipo que no dice para
   dónde va con el nombre solo. `inventory.services.movements.movement_tone`
   decide cuál le toca a cada fila. */
/* `font-semibold` (600), como la V4 (auditoría 25-09-2026). */
.tipo-mov { font-weight: 600; }
.tipo-mov--compra        { color: #059669; }
.tipo-mov--venta         { color: #ef4444; }
.tipo-mov--ajuste-mas    { color: #2563eb; }
.tipo-mov--ajuste-menos  { color: #f97316; }
.tipo-mov--devolucion    { color: #9333ea; }
.tipo-mov--transferencia { color: #6b7280; }

/* La «Referencia» del kardex, mono y azul como la V4: texto simple, sin el
   fondo gris de `.code`. Es un enlace cuando el documento existe
   (`inventory.services.document_links`) y se ve igual que el texto. */
/* `font-weight: 500` (font-medium), no 600: calco exacto de la V4
 * (auditoría 25-09-2026). */
.code--azul {
  font-family: var(--font-mono); font-size: 11.5px; font-weight: 500;
  background: none; padding: 0; color: var(--primary);
}
