/* ==========================================================================
   CarShow CRM — Temas DaisyUI: "carshow" (dark) y "carshowlight" (light)
   Derivado del design system (DESIGN.md). Todo tokenizado: NO hex hardcodeados
   en componentes — los colores salen de las variables del tema activo.
   ========================================================================== */

/* ---------- TEMA OSCURO ---------- */
[data-theme="carshow"] {
    /* 🪤 `color-scheme` — la línea que hace que los CONTROLES NATIVOS sigan al tema de la app
       y no al modo del sistema operativo. Sin ella el navegador elige la paleta de los
       elementos que pinta él —campos de texto, selects, textareas, el `Canvas` de un <dialog>,
       las barras de scroll, el selector de fechas— mirando el macOS/Windows de quien abre, no
       este archivo.
       Por qué se anulaban: el reset de Tailwind pone `color:inherit` en los campos pero NO les
       da fondo. Entonces el TEXTO seguía al tema y el FONDO al sistema, y cuando no coincidían
       el campo quedaba ilegible: con la Mac en modo oscuro y la app en tema claro, texto oscuro
       sobre campo oscuro — negro sobre negro. Al revés pasa lo mismo.
       Verificado el 2026-08-26: 39 pantallas usan `input-bordered`/`select-bordered`/
       `textarea-bordered`, y NINGUNA de esas tres clases existe en el css del proyecto: son
       campos nativos sin estilo. Esta declaración los arregla a todos de una, y también a los
       que se escriban mañana. La regla `dialog { … }` de más abajo es del mismo problema. */
    color-scheme: dark;

    /* 🪤 Esto estuvo en ROJO (hue 0) entre el 2026-08-20 y el 2026-09-22, con el comentario
       «Meta blue» mintiendo encima y la línea azul correcta comentada y muerta debajo. El rojo
       que quedaba activo era casi idéntico a `--er` (0 89% 57%), el rojo de ERROR, así que el
       ítem activo del menú, el ícono del grupo abierto, el pin de favorito y el avatar salían
       del mismo color que una alerta de peligro. Y el tema claro nunca se tocó: seguía en azul.
       O sea que la misma app hablaba dos idiomas de color según el tema.
       DESIGN.md ya advertía contra exactamente esto: «el primario es azul tipo Meta; el rojo
       queda reservado para error… antes del 31/07/2026 el primario era el rojo CarShow y llevó
       a pintar de rojo una CTA que debía ser azul».
       El rojo de marca sobrevive donde corresponde: el logo, la landing y el login. */
    --p: 212 100% 52%; --pf: 212 100% 44%; --pc: 0 0% 100%;   /* #0A7CFF — el tono del azul CarShow, aclarado para el fondo oscuro */
    --s: 40 88% 56%; --sf: 40 88% 46%; --sc: 40 90% 10%;
    --a: 169 97% 37%; --af: 169 97% 30%; --ac: 169 100% 8%;
    --n: 216 28% 16%; --nf: 216 28% 12%; --nc: 220 15% 85%;
    --b1: 222 52% 5%;   /* #060A13 lienzo de la página (el negro profundo del rediseño) */
    --b2: 216 26% 13%;  /* #191F2A cards / superficies — MÁS CLARO que --b1: la tarjeta eleva */
    --b3: 220 22% 10%;  /* nav / sidebar / footer */
    --bc: 220 20% 90%;  /* texto base (claro) */
    --in: 198 93% 60%; --inc: 198 100% 10%;
    --su: 158 64% 52%; --suc: 158 100% 8%;
    --wa: 41 96% 56%;  --wac: 41 100% 8%;
    --er: 0 89% 57%;   --erc: 0 0% 100%;
    --cs-naranja: 25 92% 55%; --cs-naranjac: 0 0% 100%; /* entre --wa y --er (buckets de recencia) */
    --rounded-box: 1rem; --rounded-btn: 1rem; --rounded-badge: 1.9rem; --tab-radius: .5rem;
    --cs-onaccent: 0 0% 100%; /* texto sobre superficies siempre-oscuras */
}

/* ---------- TEMA CLARO ---------- */
[data-theme="carshowlight"] {
    /* 🪤 `color-scheme` — la línea que hace que los CONTROLES NATIVOS sigan al tema de la app
       y no al modo del sistema operativo. Sin ella el navegador elige la paleta de los
       elementos que pinta él —campos de texto, selects, textareas, el `Canvas` de un <dialog>,
       las barras de scroll, el selector de fechas— mirando el macOS/Windows de quien abre, no
       este archivo.
       Por qué se anulaban: el reset de Tailwind pone `color:inherit` en los campos pero NO les
       da fondo. Entonces el TEXTO seguía al tema y el FONDO al sistema, y cuando no coincidían
       el campo quedaba ilegible: con la Mac en modo oscuro y la app en tema claro, texto oscuro
       sobre campo oscuro — negro sobre negro. Al revés pasa lo mismo.
       Verificado el 2026-08-26: 39 pantallas usan `input-bordered`/`select-bordered`/
       `textarea-bordered`, y NINGUNA de esas tres clases existe en el css del proyecto: son
       campos nativos sin estilo. Esta declaración los arregla a todos de una, y también a los
       que se escriban mañana. La regla `dialog { … }` de más abajo es del mismo problema. */
    color-scheme: light;

    /* 🪤 EL PRIMARIO ES EL AZUL CARSHOW #0058BE, Y NO SE "SIMPLIFICA" A UNO MÁS CLARO.
       Cambiado el 2026-09-27 por decisión del dueño, sobre el lienzo de la ficha del cliente.
       El motivo no es sólo estético y está medido: el azul anterior (#1876F2) daba **4,27** de
       contraste con el texto blanco que llevan encima TODOS los botones primarios — por debajo
       del 4,5 que pide AA. Éste da **6,71**. Cualquier aclarado futuro vuelve a reprobar. */
    --p: 212 100% 37%; --pf: 214 100% 30%; --pc: 0 0% 100%;   /* #0058BE · hover #004299 */
    --s: 35 92% 42%; --sf: 35 92% 36%; --sc: 0 0% 100%;   /* ámbar más oscuro: legible sobre blanco */
    --a: 169 90% 32%; --af: 169 90% 26%; --ac: 0 0% 100%;
    --n: 220 16% 22%; --nf: 220 16% 16%; --nc: 0 0% 100%;
    /* Invertido (2026-07-31): el CANVAS es gris y las CARDS blancas (look dashboard claro). */
    --b1: 220 16% 95%;  /* gris suave: fondo / canvas de la página */
    --b2: 0 0% 100%;    /* blanco: cards / superficies elevadas */
    --b3: 0 0% 100%;    /* blanco: nav / sidebar / topbar */
    --bc: 222 22% 16%;  /* texto base (oscuro) */
    --in: 198 88% 42%; --inc: 0 0% 100%;
    --su: 158 64% 38%; --suc: 0 0% 100%;
    --wa: 41 90% 45%;  --wac: 0 0% 100%;
    --er: 0 84% 52%;   --erc: 0 0% 100%;
    --cs-naranja: 25 86% 45%; --cs-naranjac: 0 0% 100%; /* entre --wa y --er (buckets de recencia) */
    --rounded-box: 1rem; --rounded-btn: 1rem; --rounded-badge: 1.9rem; --tab-radius: .5rem;
    --cs-onaccent: 0 0% 100%;
}

/* ==========================================================================
   PUENTE DaisyUI 4 (oklch) ↔ nuestros tokens (HSL)   — bug de fondo, 2026-08-08

   El CDN sirve DaisyUI 4.12.10, que define sus colores en **oklch**
   (`--su: 64.8% 0.150 160`). Nuestros temas de arriba los definen en **HSL**
   (`--p: 214 90% 58%`), que es el formato de DaisyUI **v3**. Resultado:
   DaisyUI arma `oklch(214 90% 58%)` — inválido, porque el hue no puede ser un
   porcentaje — el navegador descarta la declaración y el elemento queda SIN COLOR.

   Por eso `btn-primary`, `badge-success`, `progress-primary`, etc. se veían
   transparentes, y por eso `.checkbox`/`.radio` necesitaron el parche de
   `appearance:auto` en ops.css: era el mismo bug, tratado como síntoma.

   Arreglo en dos partes, sin tocar la versión de DaisyUI ni el formato HSL
   (que usa TODO nuestro CSS propio vía `hsl(var(--x))`):
   ========================================================================== */

/* 1) DaisyUI consulta `var(--fallback-X, oklch(...))` en casi todas sus reglas —
      ese fallback solo lo define dentro de `@supports not (color:oklch(...))`, o sea
      nunca en navegadores modernos. Definiéndolo nosotros, gana ANTES de que se
      evalúe el oklch roto. Cubre badges, alerts, progress, y el hover/texto de los
      botones (que usan color-mix sobre el mismo fallback). */
[data-theme="carshow"], [data-theme="carshowlight"] {
    --fallback-p:  hsl(var(--p));   --fallback-pc: hsl(var(--pc));
    --fallback-s:  hsl(var(--s));   --fallback-sc: hsl(var(--sc));
    --fallback-a:  hsl(var(--a));   --fallback-ac: hsl(var(--ac));
    --fallback-n:  hsl(var(--n));   --fallback-nc: hsl(var(--nc));
    --fallback-b1: hsl(var(--b1));  --fallback-b2: hsl(var(--b2));
    --fallback-b3: hsl(var(--b3));  --fallback-bc: hsl(var(--bc));
    --fallback-in: hsl(var(--in));  --fallback-inc: hsl(var(--inc));
    --fallback-su: hsl(var(--su));  --fallback-suc: hsl(var(--suc));
    --fallback-wa: hsl(var(--wa));  --fallback-wac: hsl(var(--wac));
    --fallback-er: hsl(var(--er));  --fallback-erc: hsl(var(--erc));
}

/* 2) El fondo del botón es la excepción: `.btn` lo arma como
      `oklch(var(--btn-color, var(--b2)))` — sin fallback que consultar. Hay que
      declararlo explícito. Los `:not()` dejan intactos outline/ghost/link, que
      deben seguir sin relleno (y suben la especificidad, por eso el hover propio). */
.btn-primary:not(.btn-outline):not(.btn-ghost):not(.btn-link) { background-color: hsl(var(--p)); border-color: hsl(var(--p)); color: hsl(var(--pc)); }
.btn-primary:not(.btn-outline):not(.btn-ghost):not(.btn-link):hover { background-color: hsl(var(--pf)); border-color: hsl(var(--pf)); }
.btn-secondary:not(.btn-outline):not(.btn-ghost):not(.btn-link) { background-color: hsl(var(--s)); border-color: hsl(var(--s)); color: hsl(var(--sc)); }
.btn-accent:not(.btn-outline):not(.btn-ghost):not(.btn-link) { background-color: hsl(var(--a)); border-color: hsl(var(--a)); color: hsl(var(--ac)); }
.btn-success:not(.btn-outline):not(.btn-ghost):not(.btn-link) { background-color: hsl(var(--su)); border-color: hsl(var(--su)); color: hsl(var(--suc)); }
.btn-warning:not(.btn-outline):not(.btn-ghost):not(.btn-link) { background-color: hsl(var(--wa)); border-color: hsl(var(--wa)); color: hsl(var(--wac)); }
.btn-error:not(.btn-outline):not(.btn-ghost):not(.btn-link)   { background-color: hsl(var(--er)); border-color: hsl(var(--er)); color: hsl(var(--erc)); }
.btn-info:not(.btn-outline):not(.btn-ghost):not(.btn-link)    { background-color: hsl(var(--in)); border-color: hsl(var(--in)); color: hsl(var(--inc)); }
.btn-secondary:not(.btn-outline):not(.btn-ghost):not(.btn-link):hover { background-color: hsl(var(--sf)); border-color: hsl(var(--sf)); }
.btn-accent:not(.btn-outline):not(.btn-ghost):not(.btn-link):hover    { background-color: hsl(var(--af)); border-color: hsl(var(--af)); }
.btn-success:not(.btn-outline):not(.btn-ghost):not(.btn-link):hover,
.btn-warning:not(.btn-outline):not(.btn-ghost):not(.btn-link):hover,
.btn-error:not(.btn-outline):not(.btn-ghost):not(.btn-link):hover,
.btn-info:not(.btn-outline):not(.btn-ghost):not(.btn-link):hover { filter: brightness(.92); }

/* ---------- Base ---------- */
/* iOS: evita que agrande el texto solo al girar el teléfono. Lo del doble-tap está abajo, en su
   propia regla — acá NO, por el motivo que explica ese comentario. */
html { -webkit-text-size-adjust: 100%; }
/* 🪤 SIN DOBLE-TAP ZOOM, Y VA EN `*` A PROPÓSITO. `touch-action` NO SE HEREDA: puesto sólo en
   `html` —como estuvo hasta el 2026-09-23, y como sigue en login.php, index.php y offline.html—
   sólo suprime el gesto cuando el dedo cae en el elemento `html` mismo, que no pasa nunca. Por eso
   el doble tap seguía haciendo zoom en todo el portal pese a que la regla "estaba puesta".
   `manipulation` = todo menos los gestos no estándar: el PINCH ZOOM SIGUE ANDANDO (un vendedor
   puede querer agrandar un número) y el scroll también. Lo único que se pierde es el doble tap.
   Lo pidió el dueño el 2026-09-23: «no la usamos y es ultra molesta».
   El lightbox (partials/lightbox.php) declara `touch-action:none` en un `style=` inline para
   poder hacer pinch sobre la imagen: un estilo inline le gana a esta regla, así que sigue igual.
   El WMS no carga este archivo — sus `touch-action:none` para arrastrar no se ven afectados. */
* { touch-action: manipulation; }
body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; background: hsl(var(--b1)); color: hsl(var(--bc)); }

/* Iconos Material Symbols: escalan con el texto y se alinean en linea */
.material-symbols-outlined {
    font-size: 1.25em; line-height: 1; vertical-align: -0.22em;
    font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
    display: inline-block;
}
.btn .material-symbols-outlined { font-size: 1.2em; vertical-align: middle; }
/* Icono de marca WhatsApp (SVG inline, hereda el color) */
.ws-ico { width: 1.15em; height: 1.15em; display: inline-block; vertical-align: -0.18em; fill: currentColor; }

/* Marca */
.brand-mark { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border-radius: .6rem; background: hsl(var(--p)); color: #fff; font-weight: 900; font-size: .8rem; margin-right: .5rem; }

/* Botón CTA principal (del design system) */
.btn-cta { box-shadow: 0 8px 24px hsl(var(--p) / .35); border-radius: 1rem; background: hsl(var(--p)); border-color: hsl(var(--p)); color: hsl(var(--pc)); }
.btn-cta:hover { background: hsl(var(--pf)); border-color: hsl(var(--pf)); }

/* Botones de secuencia de acción del pedido (Enviar → Confirmar): gris de base, verde al
   completar la etapa (.is-done). !important para ganarle al fondo propio de .btn de DaisyUI. */
.btn-step { background: hsl(var(--bc) / .16) !important; border-color: transparent !important; color: hsl(var(--bc)) !important; }
.btn-step:hover { background: hsl(var(--bc) / .26) !important; }
.btn-step.is-done { background: hsl(var(--su)) !important; border-color: hsl(var(--su)) !important; color: hsl(var(--suc)) !important; }
.btn-step.is-done:hover { background: hsl(var(--su)) !important; }
/* Los dos botones del final del paso, pedidos por el dueño el 2026-08-19: el que CIERRA la venta
   en verde y el que pide ayuda (Revisión) en rojo. Van como modificadores de .btn-step y no con
   btn-success/btn-error de DaisyUI porque las reglas de arriba llevan !important y le ganarían a
   las de la librería — el botón quedaría gris igual. Mismo patrón que .is-done, que ya resolvía
   esto para el estado "confirmado". */
.btn-step.is-go { background: hsl(var(--su)) !important; border-color: hsl(var(--su)) !important; color: hsl(var(--suc)) !important; }
.btn-step.is-go:hover { background: hsl(var(--su) / .85) !important; }
.btn-step.is-alert { background: hsl(var(--er)) !important; border-color: hsl(var(--er)) !important; color: hsl(var(--erc)) !important; }
.btn-step.is-alert:hover { background: hsl(var(--er) / .85) !important; }

/* Cara "falta algo" del botón de cierre del pedido (Task 8, spec 2026-08-18 §3): DaisyUI aplica
   el aspecto apagado de .btn-disabled SIN !important, así que pierde contra los !important de
   arriba y el botón se veía idéntico a uno vivo — clickeable a la vista, muerto al tacto,
   exactamente lo que esta tarea vino a eliminar. Colores propios en vez de dejar ganar la
   opacidad de DaisyUI: si ganara, el texto (el motivo — todo el punto de esta cara) quedaría
   al 20% e ilegible. Ámbar de "atención", no gris/verde de etapa. */
.btn-step.btn-disabled { background: hsl(var(--wa) / .18) !important; border-color: hsl(var(--wa) / .45) !important; color: hsl(var(--bc)) !important; opacity: 1 !important; cursor: not-allowed; }

/* Banner demo */
.demo-banner { background: repeating-linear-gradient(45deg,#3a2a00,#3a2a00 12px,#4a3600 12px,#4a3600 24px); color: #fbbd23; text-align: center; font-size: .68rem; font-weight: 800; letter-spacing: .1em; padding: 3px; text-transform: uppercase; }

/* ---------- Cards ---------- */
.cs-card { background: hsl(var(--b2)); border: 1px solid hsl(var(--bc)/.08); border-radius: 1rem; }
/* En el tema claro las cards blancas "flotan" sobre el canvas gris con una sombra suave. */
[data-theme="carshowlight"] .cs-card { border-color: hsl(var(--bc)/.06); box-shadow: 0 1px 3px rgba(16,24,40,.06), 0 1px 2px rgba(16,24,40,.04); }
.cs-card-header { padding: .85rem 1.1rem; border-bottom: 1px solid hsl(var(--bc)/.08); font-weight: 700; display: flex; align-items: center; justify-content: space-between; }
.cs-card-body { padding: 1.1rem; }
.cs-label { font-size: .68rem; text-transform: uppercase; letter-spacing: .12em; color: hsl(var(--bc)/.55); font-weight: 800; }
.cs-value { font-size: 1.6rem; font-weight: 800; color: hsl(var(--bc)); line-height: 1.1; }
.cs-kpi-icon { width: 42px; height: 42px; border-radius: .75rem; display: flex; align-items: center; justify-content: center; font-size: 1.1rem; }

/* ---------- Barras de progreso genéricas (cs_barra() en helpers.php) ---------- */
/* Horizontal por defecto: track fijo + fill que crece en ancho. */
.cs-bar { width: 100%; height: .55rem; border-radius: 999px; background: hsl(var(--bc)/.1); overflow: hidden; }
.cs-bar-fill { height: 100%; width: 0; border-radius: 999px; transition: width .5s ease; }
/* Variante vertical (grilla "día por día"): el fill crece en alto, anclado abajo. */
.cs-bar-v { width: .5rem; height: 3rem; display: flex; align-items: flex-end; justify-content: center; }
.cs-bar-v .cs-bar-fill { width: 100%; height: 0; transition: height .5s ease; }
/* Variante apilada (cartera por recencia "de un vistazo"): varios segmentos de color en una fila. */
.cs-bar-stack { display: flex; }
.cs-bar-stack > div { height: 100%; }

/* ---------- Tarjetas expandibles con <details>/<summary> (desgloses del dashboard) ---------- */
/* Nativo, accesible por teclado y funciona en TODOS los tamaños (a diferencia de .cs-fold-head,
   que es solo-móvil). El chevron rota al abrir sin depender de la media query de .det-chev. */
.cs-exp { border-top: 1px solid hsl(var(--bc)/.08); }
.cs-exp > summary { list-style: none; cursor: pointer; display: flex; align-items: center; gap: .4rem;
    padding: .8rem 0 .1rem; font-size: .82rem; font-weight: 700; color: hsl(var(--bc)/.7); user-select: none; }
.cs-exp > summary::-webkit-details-marker { display: none; }
.cs-exp-chev { margin-left: auto; font-size: 1.15rem; transition: transform .18s ease; }
.cs-exp[open] > summary .cs-exp-chev { transform: rotate(180deg); }
.cs-exp-body { padding: .3rem 0 .1rem; }
.cs-exp-sub { font-size: .68rem; text-transform: uppercase; letter-spacing: .1em; font-weight: 800;
    color: hsl(var(--bc)/.45); margin: .9rem 0 .5rem; }
.cs-exp-sub:first-child { margin-top: .1rem; }
.cs-exp-row { padding: .45rem .6rem; border-radius: .6rem; margin-bottom: .3rem; }
.cs-exp-row.is-current { background: hsl(var(--p)/.08); box-shadow: inset 0 0 0 1px hsl(var(--p)/.25); }
.cs-tag-actual { display: inline-block; font-size: .58rem; font-weight: 800; padding: .05rem .4rem;
    border-radius: 999px; background: hsl(var(--p)/.15); color: hsl(var(--p)); margin-left: .35rem;
    text-transform: uppercase; letter-spacing: .04em; vertical-align: middle; }
/* Grilla de 7 días (semana en curso): columnas iguales, se achica en pantallas muy chicas. */
.cs-dias-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: .3rem; text-align: center; }
@media (max-width: 380px) { .cs-dias-grid { gap: .15rem; } .cs-dias-grid .cs-bar-v { width: .4rem; } .cs-dias-grid .cs-dia-track { width: .4rem; } }
.cs-dia-cell.is-hoy .cs-dia-lbl { color: hsl(var(--p)); font-weight: 800; }

/* ---------- App shell (portales) ---------- */
/* Sidebar off-canvas en TODAS las pantallas: oculta por defecto, se abre con la
   hamburguesa y se cierra al tocar el backdrop / un link / Escape. */
.app-shell { min-height: 100vh; }
.sidebar { width: 250px; background: hsl(var(--b3)); border-right: 1px solid hsl(var(--bc)/.07); display: flex; flex-direction: column; position: fixed; top: 0; left: -280px; height: 100vh; z-index: 1040; transition: left .22s ease; box-shadow: 0 0 40px rgba(0,0,0,.25); }
/* En móvil el 100vh incluye la barra del navegador: los últimos ítems del menú quedan
   tapados. 100dvh usa la altura VISIBLE. Se deja el 100vh arriba como respaldo. */
@supports (height: 100dvh) { .sidebar { height: 100dvh; } }
/* La lista de módulos es lo que scrollea. Sin esto el menú no tiene NINGÚN contenedor
   scrollable: si es más alto que la pantalla, el excedente queda inalcanzable (pasaba
   en celulares). `min-height:0` es obligatorio — un hijo flex no baja de su contenido
   y sin eso el overflow no llega a activarse nunca. */
.sidebar > nav { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }
body.sidebar-open .sidebar { left: 0; }
.sidebar-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,.45); opacity: 0; visibility: hidden; transition: opacity .2s ease, visibility .2s ease; z-index: 1039; }
body.sidebar-open .sidebar-backdrop { opacity: 1; visibility: visible; }
@media (prefers-reduced-motion: reduce) { .sidebar { transition: none; } }

/* ── ESCRITORIO: el menú se queda quieto ──────────────────────────────────────
   Hasta el 2026-09-22 el sidebar era un cajón off-canvas en TODAS las anchuras
   —`left:-280px`, sin una sola media query que lo fijara—, así que en un monitor
   de 27" había que abrir la hamburguesa cada vez que se quería navegar, el menú
   se montaba encima del contenido con un velo, y nunca se veía dónde estaba uno
   parado mientras trabajaba. Era la fricción más cara del día para las personas
   que viven en el CRM.
   Desde 1024px el menú es una columna más: el contenido se corre a su lado, el
   velo desaparece y la hamburguesa se esconde. Debajo de 1024px no cambia nada
   — sigue siendo el cajón, que es lo correcto en un teléfono. */
/* ── BUSCADOR DE PANTALLAS (paleta de comandos) ───────────────────────────────
   Con ~65 destinos, escribir dos letras es más rápido que recorrer el árbol. El botón de la
   barra es tan importante como el atajo: un buscador que sólo se abre con Ctrl+K y no se ve
   en ninguna parte no lo descubre nadie. */
.cs-buscar-btn { color: hsl(var(--bc)/.6); font-weight: 500; }
.cs-buscar-btn:hover { color: hsl(var(--bc)); }
.cs-buscar-kbd { font-size: .6rem; padding: .1rem .3rem; border-radius: .25rem;
    border: 1px solid hsl(var(--bc)/.2); color: hsl(var(--bc)/.45); font-family: inherit; }
/* En el teléfono queda sólo la lupa: el texto y el atajo no aportan y roban ancho al título. */
@media (max-width: 640px) { .cs-buscar-lbl, .cs-buscar-kbd { display: none; } }

/* 🪤 `display` va en la regla del ESTADO ABIERTO, nunca en la regla base.
   Con `display:flex` acá, la paleta quedaba SIEMPRE visible: un estilo de autor le gana al
   `[hidden] { display:none }` del navegador sin importar la especificidad, así que el atributo
   `hidden` del HTML no hacía nada. La paleta tapaba la pantalla con su fondo desde la primera
   carga, cerrarla no tenía efecto, y la app quedaba inalcanzable detrás.
   `:not([hidden])` lo deja imposible de repetir por accidente: mientras el atributo esté, no
   hay `display` que lo contradiga. */
.cs-paleta { display: none; position: fixed; inset: 0; z-index: 1060;
    align-items: flex-start; justify-content: center; padding: 10vh 1rem 1rem; }
.cs-paleta:not([hidden]) { display: flex; }
.cs-paleta-fondo { position: absolute; inset: 0; background: rgba(0,0,0,.5); }
.cs-paleta-caja { position: relative; width: 100%; max-width: 34rem; background: hsl(var(--b2));
    border: 1px solid hsl(var(--bc)/.12); border-radius: var(--rounded-box, 1rem);
    box-shadow: 0 20px 60px rgba(0,0,0,.35); overflow: hidden; }
.cs-paleta-caja input { width: 100%; border: 0; outline: none; background: transparent;
    color: hsl(var(--bc)); font-size: 1.05rem; font-family: inherit;
    padding: 1rem 1.2rem; border-bottom: 1px solid hsl(var(--bc)/.1); }
.cs-paleta-lista { max-height: 50vh; overflow-y: auto; overscroll-behavior: contain; }
/* .85rem de padding vertical ≈ 47px de alto: con .7rem quedaba en ~42, por debajo de la
   referencia táctil de 44. En el teléfono esta lista se toca con el dedo, no se apunta. */
.cs-paleta-item { display: flex; align-items: center; gap: .7rem; padding: .85rem 1.2rem;
    color: hsl(var(--bc)/.8); text-decoration: none; font-size: .93rem;
    -webkit-tap-highlight-color: hsl(var(--p)/.2); }
.cs-paleta-item .material-symbols-outlined { font-size: 1.2rem; width: 20px; text-align: center;
    color: hsl(var(--bc)/.45); }
/* `.sel` y no `:hover`: la selección la maneja el teclado, y el mouse la sigue. Si fuera :hover,
   mover el mouse sin querer pisaría lo que el usuario eligió con las flechas. */
.cs-paleta-item.sel { background: hsl(var(--p)/.14); color: hsl(var(--bc)); }
.cs-paleta-item:active { background: hsl(var(--p)/.22); }
.cs-paleta-item.sel .material-symbols-outlined { color: hsl(var(--p)); }
.cs-paleta-txt { flex: 1 1 auto; }
.cs-paleta-grp { font-size: .7rem; color: hsl(var(--bc)/.4); white-space: nowrap; }
.cs-paleta-vacio { padding: 1.2rem; text-align: center; color: hsl(var(--bc)/.45); font-size: .9rem; }
.cs-paleta-pie { padding: .5rem 1.2rem; border-top: 1px solid hsl(var(--bc)/.1);
    font-size: .68rem; color: hsl(var(--bc)/.4); }
.cs-paleta-pie kbd { border: 1px solid hsl(var(--bc)/.2); border-radius: .25rem;
    padding: .05rem .25rem; margin-right: .15rem; font-family: inherit; }

@media (min-width: 1024px) {
    .sidebar { left: 0; box-shadow: none; }
    .app-main { margin-left: 250px; }
    /* El velo y la hamburguesa no tienen sentido con el menú siempre a la vista.
       `body.sidebar-open` puede quedar puesto de un click previo en una pantalla
       angosta y no molesta: el menú ya está en left:0. */
    .sidebar-backdrop { display: none; }
    .cs-menu-btn { display: none; }
}
.sidebar-brand { display: flex; align-items: center; font-weight: 800; color: hsl(var(--bc)); text-decoration: none; padding: 1.1rem 1.2rem; font-size: 1.1rem; }
.sidebar-role { font-size: .68rem; text-transform: uppercase; letter-spacing: .12em; color: hsl(var(--bc)/.4); padding: 0 1.2rem .6rem; }
.sidebar-link { display: flex; align-items: center; gap: .7rem; color: hsl(var(--bc)/.65); padding: .7rem 1.2rem; border-left: 3px solid transparent; font-size: .92rem; text-decoration: none; }
.sidebar-link .material-symbols-outlined { width: 20px; text-align: center; font-size: 1.25rem; }
.sidebar-link:hover { background: hsl(var(--bc)/.05); color: hsl(var(--bc)); }
.sidebar-link.active { background: hsl(var(--p)/.14); color: hsl(var(--bc)); border-left-color: hsl(var(--p)); font-weight: 600; }
/* `flex:none` para que el pie no se encoja cuando la lista de arriba desborda y scrollea. */
.sidebar-foot { flex: none; margin-top: auto; border-top: 1px solid hsl(var(--bc)/.07); padding: .4rem 0; }

.app-main { display: flex; flex-direction: column; min-width: 0; }
/* z-index 40: el header y sus menús (cuenta, notificaciones) DEBEN quedar por encima del
   contenido. La ops-bar de las páginas es sticky con z-index 6; con el header en z-5 el menú
   de la cuenta —que vive dentro del header— quedaba atrapado DEBAJO de la ops-bar y el
   contenido de la página se veía encima del menú. Ver docs/diseno/DESIGN-crm.md. */
.topbar { background: hsl(var(--b3)); border-bottom: 1px solid hsl(var(--bc)/.07); padding: .7rem 1.4rem; display: flex; align-items: center; gap: 1rem; position: sticky; top: 0; z-index: 40; }
/* En el teléfono la barra reparte hasta seis controles además del título: hamburguesa,
   buscador, carrito, tema, campana y avatar. Con el padding y el gap de escritorio no entran,
   y como el <h1> no se encoge solo, la fila empujaba la página y aparecía scroll horizontal.
   Acá se recupera ancho; el truncado del título (en layout.php) es la otra mitad del arreglo.

   🪤 A propósito NO se pone `overflow-x:hidden` en html/body, que es lo que hace el WMS
   (`wms/_head.php:12`). Allá funciona porque no usa `position:sticky`; acá esta misma barra SÍ
   es sticky, y ese guardrail global la rompe en varios navegadores. Se ataca la causa —que el
   título no truncaba— en vez de esconder el síntoma. */
@media (max-width: 640px) {
    .topbar { padding: .6rem .7rem; gap: .5rem; }
}
.content { padding: 1.6rem; }

/* Superficies utilitarias por token (fondos alternos de secciones) */
.surface-2 { background: hsl(var(--b2)); }
.surface-3 { background: hsl(var(--b3)); }

/* Público: navbar / hero */
.public-nav { background: hsl(var(--b3)); border-bottom: 1px solid hsl(var(--bc)/.08); }
.hero { background: hsl(var(--b2)); }

.cat-card { display: block; background: hsl(var(--b2)); border: 1px solid hsl(var(--bc)/.08); border-radius: 1rem; padding: 1.3rem; text-align: center; text-decoration: none; color: hsl(var(--bc)); transition: .15s; height: 100%; }
.cat-card:hover { border-color: hsl(var(--p)); transform: translateY(-3px); }
.cat-card i, .cat-card .material-symbols-outlined { font-size: 1.7rem; color: hsl(var(--p)); }

/* Barra de categorías: una sola línea, sin bordes, separadas por línea oscura.
   En pantallas chicas se mantiene en línea y se desliza horizontalmente. */
.cat-bar { display: flex; overflow-x: auto; scrollbar-width: none; border-radius: 1rem; }
.cat-bar::-webkit-scrollbar { display: none; }
.cat-item { flex: 1 0 auto; min-width: 128px; display: flex; flex-direction: column; align-items: center; gap: .35rem;
    padding: 1.1rem .75rem; text-decoration: none; color: hsl(var(--bc));
    border-left: 1px solid hsl(var(--bc)/.14); transition: background .15s; }
.cat-item:first-child { border-left: 0; }
.cat-item:hover { background: hsl(var(--bc)/.04); }
.cat-item i, .cat-item .material-symbols-outlined { font-size: 1.5rem; color: hsl(var(--p)); }
.cat-item .cat-name { font-weight: 700; font-size: .82rem; text-align: center; }
.cat-item .cat-count { font-size: .7rem; opacity: .5; }

/* Nombre de producto: SIEMPRE 2 líneas (recorta con "…") → tarjetas de altura uniforme. */
.prod-name { font-weight: 600; font-size: .875rem; line-height: 1.3; margin: .5rem 0;
    color: hsl(var(--bc)); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden; height: 2.6em; }

/* Miniatura de producto — cuadrada 1:1 (imagen estándar 800×800). object-fit cover para <img> reales. */
.prod-thumb { aspect-ratio: 1/1; background: hsl(var(--bc)/.04); display: flex; align-items: center; justify-content: center; color: hsl(var(--bc)/.16); font-size: 2.2rem; overflow: hidden; }
.prod-thumb img { width: 100%; height: 100%; object-fit: cover; }

/* Tier chip (siempre texto blanco sobre color del tier) */
.tier-chip { display: inline-flex; align-items: center; gap: 6px; padding: 3px 12px; border-radius: 999px; font-weight: 700; font-size: .8rem; color: #fff; }
.tier-dot { width: 10px; height: 10px; border-radius: 50%; background: #fff; opacity: .85; }

/* Timeline */
.timeline { list-style: none; padding-left: 0; margin: 0; }
.timeline li { position: relative; padding-left: 1.6rem; padding-bottom: 1rem; border-left: 2px solid hsl(var(--bc)/.12); margin-left: .4rem; }
.timeline li::before { content: ''; position: absolute; left: -7px; top: 3px; width: 12px; height: 12px; border-radius: 50%; background: hsl(var(--p)); }
.timeline li:last-child { border-left-color: transparent; }

/* Login: fondo dramático oscuro en ambos temas (hero-like) */
.login-wrap { min-height: calc(100vh - 22px); display: flex; align-items: center; justify-content: center; background: linear-gradient(135deg,#1a0505 0%,#111318 65%); padding: 2rem; }

/* El sidebar ahora es off-canvas en todas las pantallas (ver "App shell" arriba);
   ya no hace falta una regla responsive aparte. */

/* ---------- Carousel del hero (siempre superficie oscura) ---------- */
/* Hero: relación 8:3 PURA (igual que la imagen 1920×720) → se ve COMPLETA, con sus bordes,
   a cualquier ancho (cover no recorta porque la proporción del contenedor coincide con la
   de la imagen). Sin min/max-height, que romperían la proporción y volverían a recortar. */
.carousel-hero { position: relative; aspect-ratio: 8 / 3; width: 100%; overflow: hidden; }
/* En celular el hero cambia de FORMA, no sólo de imagen. Sin esto, el arte vertical
   (1080×1350) entraba en una caja 8:3 y `object-fit: cover` mostraba apenas el 30 % de su
   altura: marketing producía una imagen que nadie veía entera. El punto de corte es el MISMO
   que usa el <picture> de landing-demo.php para cambiar al arte móvil — si difirieran, entre
   los dos breakpoints quedaría el arte equivocado en la caja equivocada. */
@media (max-width: 640px) { .carousel-hero { aspect-ratio: 4 / 5; } }
.c-slide { position: absolute; inset: 0; opacity: 0; transition: opacity .7s ease; display: flex; align-items: center;
    text-decoration: none; color: inherit; cursor: pointer; }
.c-slide.active { opacity: 1; z-index: 1; }
.c-dots { position: absolute; bottom: 16px; left: 50%; transform: translateX(-50%); display: flex; gap: 8px; z-index: 3; }
.c-dot { width: 9px; height: 9px; border-radius: 50%; background: rgba(255,255,255,.4); cursor: pointer; border: none; transition: .25s; padding: 0; }
.c-dot.active { background: hsl(var(--p)); width: 24px; border-radius: 6px; }
.c-arrow { position: absolute; top: 50%; transform: translateY(-50%); z-index: 3; width: 40px; height: 40px; border-radius: 50%; background: rgba(0,0,0,.4); backdrop-filter: blur(6px); border: 1px solid rgba(255,255,255,.15); color: #fff; cursor: pointer; }
.c-arrow:hover { background: rgba(0,0,0,.65); }
.c-arrow.prev { left: 14px; } .c-arrow.next { right: 14px; }

/* ---------- Marquee de marcas ---------- */
.marquee { overflow: hidden; position: relative; -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); }
.marquee-track { display: flex; gap: 1rem; width: max-content; animation: marquee 30s linear infinite; }
.marquee:hover .marquee-track { animation-play-state: paused; }
@keyframes marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
.marca-chip { padding: .6rem 1.4rem; border-radius: .75rem; font-weight: 800; opacity: .75; white-space: nowrap; background: hsl(var(--bc)/.05); border: 1px solid hsl(var(--bc)/.1); color: hsl(var(--bc)); }

/* Logo real de marca en el marquee. Convive con .marca-chip, que sigue siendo el fallback
   cuando una marca todavía no tiene logo cargado.

   Se normaliza por ALTO y no por ancho: los logos llegan como los entrega cada proveedor y
   sus proporciones van de 1:1 a 5.4:1 (ver CS_MEDIO_LIBRES en db-marketing.php). Fijando el
   alto, un logo apaisado ocupa más ancho que uno cuadrado — que es exactamente como se ve
   bien una tira de logos. `object-fit: contain` garantiza que nunca se deforme. */
.marca-logo { display: inline-flex; align-items: center; justify-content: center; height: 3rem; padding: 0 1.4rem; flex: 0 0 auto; }
.marca-logo img { height: 100%; width: auto; max-width: 11rem; object-fit: contain; opacity: .8; transition: opacity .2s; }
.marquee:hover .marca-logo img { opacity: 1; }

/* TEMA OSCURO: cada logo va sobre una tarjeta blanca.
   La mayoría de los logos llegan con fondo blanco OPACO, no transparente, así que sobre la
   franja oscura se veían como recuadros blancos recortados. Con la tarjeta, ese fondo opaco se
   funde con ella y no se nota la diferencia entre un logo transparente y uno que no lo es.

   Por eso hay UN solo logo por marca y no un par claro/oscuro: sobre blanco, una versión
   invertida sería blanco sobre blanco. La columna que guardaba esa segunda imagen se eliminó
   (db/logos-marca-sin-oscuro.sql). Si algún día la franja vuelve a fondo oscuro sin tarjetas,
   se reactiva con un ALTER y un intercambio por `[data-theme]` acá.

   La opacidad vuelve a 1 en oscuro: el .8 existe para atenuar los logos contra el fondo de la
   página, y sobre blanco sólo los ensuciaría de gris. */
[data-theme="carshow"] .marca-logo {
    background: #fff;
    border-radius: .6rem;
    padding: .45rem 1rem;
    height: 3.7rem;
}
[data-theme="carshow"] .marca-logo img { opacity: 1; }

/* MOVIMIENTO SIEMPRE ACTIVO — decisión de producto del 2026-08-24, tomada a sabiendas.
   Este bloque tenía un `@media (prefers-reduced-motion: reduce)` que dejaba la franja quieta
   para quien configuró esa preferencia en su sistema. Se quitó a pedido del dueño: la franja
   se mueve para todos.

   Qué se pierde, para que quede escrito y no parezca un olvido: quien activa esa preferencia
   suele hacerlo por trastornos vestibulares —mareo o migraña disparados por movimiento en
   pantalla—, y ahora no tiene forma de frenarla desde un celular. La pausa por hover de más
   arriba sigue existiendo, pero no hay hover en táctil.

   WCAG 2.2.2 (nivel A) pide un control de pausa para todo movimiento automático de más de
   5 segundos. Hoy sólo se cumple con mouse. Si alguna vez se agrega un botón de pausa, ese
   es el arreglo — no volver a apagar la animación. */

/* TARJETA CLICKEABLE (enlace estirado).
   El <a> ocupa toda la tarjeta con ::after, así que cualquier punto navega — pero sigue siendo un
   enlace de verdad: se abre en pestaña nueva con el botón del medio y se recorre con el teclado.
   El botón de agregar se apoya por encima (z-index en .prod-actions), que es lo que evita el
   defecto clásico de «aprieto Agregar y además me lleva a otra página». */
.prod-card-link { position: relative; transition: transform .18s, box-shadow .18s; }
.prod-card-link:hover { transform: translateY(-2px); box-shadow: 0 10px 28px hsl(var(--bc)/.10); }
.prod-card-a { position: absolute; inset: 0; z-index: 1; }
.prod-card-a::after { content: ''; position: absolute; inset: 0; }
.prod-card-link:focus-within { outline: 2px solid hsl(var(--p)); outline-offset: 2px; }
.prod-card-link .prod-actions { position: relative; z-index: 2; }

/* Badge "Nuevo" sobre la miniatura */
.prod-thumb-wrap { position: relative; }
.prod-badge { position: absolute; top: .5rem; left: .5rem; z-index: 2; }

/* Carousel horizontal de productos (marca destacada) */
.prod-carousel { display: flex; gap: .75rem; overflow-x: auto; scroll-snap-type: x mandatory;
    scrollbar-width: none; padding-bottom: .25rem; }
.prod-carousel::-webkit-scrollbar { display: none; }
.prod-slide { flex: 0 0 auto; width: 180px; scroll-snap-align: start; }

/* Toggle de tema */
.theme-toggle { color: hsl(var(--bc)/.7); }
.theme-toggle:hover { color: hsl(var(--bc)); }

/* ==========================================================================
   Navegación por secciones + grupo "Configuración" plegable (sidebar admin)
   ========================================================================== */
.sidebar-group-label { font-size: .62rem; text-transform: uppercase; letter-spacing: .14em;
    color: hsl(var(--bc)/.38); font-weight: 800; padding: .9rem 1.2rem .3rem; }
/* El encabezado del grupo dejó de ser una etiqueta diminuta en versalitas: ahora ES el
   control que abre y cierra, así que se ve y se toca como una fila del menú (mismo alto,
   mismo ícono de 20px, mismo padding que .sidebar-link). */
.sidebar-group-toggle { display: flex; align-items: center; gap: .7rem; width: 100%; background: none;
    border: 0; border-left: 3px solid transparent; cursor: pointer; color: hsl(var(--bc)/.65);
    font: inherit; font-size: .92rem; font-weight: 600; text-align: left; padding: .7rem 1.2rem; }
.sidebar-group-toggle:hover { background: hsl(var(--bc)/.05); color: hsl(var(--bc)); }
.sidebar-group-toggle .gi { width: 20px; text-align: center; font-size: 1.25rem; }
.sidebar-group-toggle .gl { flex: 1 1 auto; }
.sidebar-group-toggle .chev { margin-left: auto; font-size: 1.1rem; opacity: .55; transition: transform .2s ease; }
.sidebar-group.collapsed .sidebar-group-toggle .chev { transform: rotate(-90deg); }
.sidebar-group.collapsed .sidebar-group-body { display: none; }
/* Grupo abierto: el ícono se tiñe y una guía vertical ata los hijos a su encabezado, para no
   perder de vista de qué grupo cuelga lo que estás mirando. */
.sidebar-group:not(.collapsed) > .sidebar-group-toggle { color: hsl(var(--bc)); }
.sidebar-group:not(.collapsed) > .sidebar-group-toggle .gi { color: hsl(var(--p)); }
.sidebar-group-body { margin-left: 1.85rem; border-left: 1px solid hsl(var(--bc)/.09); }
.sidebar-group-body .sidebar-link { padding-left: .9rem; font-size: .88rem; }

/* Fijar en favoritos. El botón se apoya SOBRE el link (no puede ir adentro: un <button>
   dentro de un <a> es HTML inválido) y solo aparece al pasar por encima, para que el pin no
   ensucie 43 filas de menú. */
.sidebar-row { position: relative; }
.sidebar-row .sidebar-link { padding-right: 2.2rem; }
.sidebar-pin { position: absolute; right: .45rem; top: 50%; transform: translateY(-50%);
    display: flex; align-items: center; justify-content: center; width: 26px; height: 26px;
    padding: 0; border: 0; border-radius: 50%; background: none; cursor: pointer;
    color: hsl(var(--bc)/.55); opacity: 0; transition: opacity .15s ease; }
.sidebar-pin .material-symbols-outlined { font-size: 1rem; }
.sidebar-row:hover .sidebar-pin, .sidebar-pin:focus-visible { opacity: 1; }
.sidebar-pin:hover { background: hsl(var(--bc)/.1); color: hsl(var(--bc)); }
.sidebar-row.pinned .sidebar-pin, .sidebar-pin.on { opacity: 1; color: hsl(var(--p)); }
/* Sin mouse no hay hover: en táctil el pin se ve siempre, tenue, o sería inalcanzable. */
@media (hover: none) { .sidebar-pin { opacity: .45; } }
/* Favoritos: tiene que LEERSE como una zona aparte del menú, no como un grupo más.
   El borde anterior era de 7% de opacidad — prácticamente invisible—, así que los favoritos
   se confundían con el resto y no se entendía por qué esas pantallas estaban repetidas.
   Ahora: fondo propio muy tenue, borde visible abajo, y aire arriba y abajo. */
.sidebar-fav { padding: .35rem 0 .5rem; margin: 0 0 .5rem;
    background: hsl(var(--bc)/.03);
    border-bottom: 1px solid hsl(var(--bc)/.14); }
/* La etiqueta «Favoritos» lleva el color del acento para que se distinga de los encabezados
   de grupo, que son gris tenue. Es la única sección que el usuario armó a mano. */
.sidebar-fav .sidebar-group-label { color: hsl(var(--p)/.9); font-weight: 700; }

/* ==========================================================================
   Tarjeta de usuario (topbar, esquina superior derecha) — mobile-first
   ========================================================================== */
.cs-user-dd { position: relative; }
.cs-user-btn { display: flex; align-items: center; gap: .5rem; background: none; border: 0; cursor: pointer;
    color: hsl(var(--bc)); padding: .25rem .35rem; border-radius: .8rem; }
.cs-user-btn:hover { background: hsl(var(--bc)/.06); }
.cs-avatar { width: 34px; height: 34px; border-radius: 50%; flex: 0 0 auto; display: flex; align-items: center;
    justify-content: center; font-weight: 800; font-size: .78rem; color: hsl(var(--cs-onaccent));
    background: linear-gradient(135deg, hsl(var(--p)), hsl(var(--p)/.7)); }
.cs-avatar-lg { width: 44px; height: 44px; font-size: .95rem; }
.cs-user-btn .cs-user-name { font-size: .82rem; font-weight: 700; line-height: 1.1; display: block; max-width: 12rem;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cs-user-btn .cs-user-rol { font-size: .68rem; opacity: .55; line-height: 1.1; display: block; }
.cs-user-btn .chev { font-size: 1.15rem; opacity: .5; }
.cs-user-menu { position: absolute; right: 0; top: calc(100% + .5rem); width: 268px; max-width: calc(100vw - 1.5rem);
    background: hsl(var(--b2)); border: 1px solid hsl(var(--bc)/.1); border-radius: 1rem;
    box-shadow: 0 18px 50px rgba(0,0,0,.35); z-index: 60; overflow: hidden;
    opacity: 0; visibility: hidden; transform: translateY(-6px); transition: opacity .16s ease, transform .16s ease, visibility .16s; }
.cs-user-dd.open .cs-user-menu { opacity: 1; visibility: visible; transform: translateY(0); }
/* Opciones de cobro en el panel de filtros: la seleccionada se resalta claramente. */
.cs-cobro-opt { padding: .3rem .5rem; border-radius: .45rem; transition: background .12s ease; }
.cs-cobro-opt:hover { background: hsl(var(--bc)/.06); }
.cs-cobro-opt:has(input:checked) { background: hsl(var(--p)/.16); font-weight: 700; }
/* Notificaciones: en mobile el dropdown pasa a panel fijo casi full-width (mobile-first). */
@media (max-width: 640px) {
    #csNotifMenu { position: fixed; left: 8px; right: 8px; top: 58px; width: auto !important;
        max-width: none !important; max-height: 72vh; }
}
.cs-user-head { display: flex; align-items: center; gap: .7rem; padding: 1rem; background: hsl(var(--b3)); }
.cs-user-head .nm { font-weight: 800; line-height: 1.15; }
.cs-user-meta { padding: .5rem 1rem; }
.cs-user-meta-row { display: flex; justify-content: space-between; gap: 1rem; padding: .35rem 0; font-size: .82rem;
    border-bottom: 1px solid hsl(var(--bc)/.06); }
.cs-user-meta-row:last-child { border-bottom: 0; }
.cs-user-meta-row span { opacity: .55; }
.cs-user-logout { display: flex; align-items: center; gap: .6rem; padding: .8rem 1rem; margin: .3rem;
    border-radius: .7rem; color: hsl(var(--er)); text-decoration: none; font-weight: 600; font-size: .88rem; }
.cs-user-logout:hover { background: hsl(var(--er)/.1); }

/* ==========================================================================
   Etiquetas ultra small (categoría / precio en tarjeta de producto)
   ========================================================================== */
.txt-2xs { font-size: .68rem; line-height: 1.1; }
.txt-3xs { font-size: .6rem; line-height: 1.1; letter-spacing: .01em; }
.prod-cat { font-size: .6rem; text-transform: uppercase; letter-spacing: .04em; opacity: .5; font-weight: 700; }
.prod-price-tag { font-size: .6rem; font-weight: 700; color: hsl(var(--p)); display: inline-flex; align-items: center; gap: 2px; }
.prod-price-tag .material-symbols-outlined { font-size: .85rem; }
/* Tarjeta de producto como columna: el botón "Agregar" se ancla al fondo, así queda
   a la MISMA altura en toda la fila aunque el nombre/categoría ocupen distinto. */
.prod-card { display: flex; flex-direction: column; height: 100%; }
.prod-card > .p-4 { display: flex; flex-direction: column; flex: 1 1 auto; }
.prod-actions { margin-top: auto; padding-top: .6rem; }

/* ==========================================================================
   Tarjeta de producto e-commerce (catálogo del cliente): imagen enmarcada,
   fila precio·meta con divisores, marca "verificada" y CTA ancho al fondo.
   ========================================================================== */
.pcard { background: hsl(var(--b2)); border: 1px solid hsl(var(--bc)/.08); border-radius: 1.25rem;
    padding: .55rem; display: flex; flex-direction: column; height: 100%; transition: border-color .15s, transform .15s, box-shadow .15s; }
.pcard:hover { border-color: hsl(var(--p)/.45); transform: translateY(-2px); box-shadow: 0 12px 30px hsl(var(--b1)/.5); }
/* Fondo FIJO, no el del tema: 2.199 de las ~4.000 fotos de producto son PNG con canal alfa
   (recortes sin fondo). Sobre hsl(var(--b1)) el producto quedaba flotando en oscuro y las
   partes negras del artículo desaparecían. El gris casi blanco despega el recuadro de la
   tarjeta y además se funde con los JPEG, que ya vienen con fondo blanco propio.
   Medido el 2026-08-18: 2.313 PNG (2.199 con alfa) contra 1.761 JPEG. */
.pcard-img { position: relative; aspect-ratio: 1/1; background: #fafafa; border: 1px solid hsl(var(--bc)/.06);
    border-radius: 1rem; display: flex; align-items: center; justify-content: center; overflow: hidden; }
.pcard-img img { width: 100%; height: 100%; object-fit: contain; padding: .7rem; }
/* El ícono de «sin foto» ya no puede depender del tema: sobre el fondo claro fijo de
   arriba, un hsl(var(--bc)/.18) en tema oscuro era casi blanco sobre casi blanco. */
.pcard-img .material-symbols-outlined { font-size: 2.4rem; color: rgba(20,25,35,.20); }
.pcard-body { display: flex; flex-direction: column; flex: 1 1 auto; padding: .75rem .5rem .35rem; }
.pcard-title { font-weight: 800; font-size: .92rem; line-height: 1.25; color: hsl(var(--bc));
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; min-height: 2.3em; }
.pcard-meta { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; margin-top: .45rem; font-size: .68rem; }
.pcard-meta .price { color: hsl(var(--s)); font-weight: 800; font-size: .82rem; white-space: nowrap; }
.pcard-meta .sep { color: hsl(var(--bc)/.5); padding-left: .4rem; border-left: 1px solid hsl(var(--bc)/.15);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 9rem; }
.pcard-sub { display: flex; align-items: center; gap: .7rem; margin: .5rem 0; flex-wrap: wrap; min-height: 1rem; }
.pcard-store { display: inline-flex; align-items: center; gap: 3px; font-size: .72rem; font-weight: 700; color: hsl(var(--a));
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
.pcard-store .material-symbols-outlined { font-size: .95rem; }
/* CTA: siempre en una línea (el btn de daisyUI hace flex-wrap y apilaba ícono+texto). */
.pcard-btn { margin-top: auto; width: 100%; border-radius: .95rem; flex-wrap: nowrap; white-space: nowrap; gap: .4rem; }
.pcard-btn .material-symbols-outlined { font-size: 1.1rem; }
.pcard-btn .lbl-short { display: none; }
@media (max-width: 500px) {
    /* En tarjetas angostas: botón más compacto y etiqueta corta "Agregar". */
    .pcard-btn { min-height: 2.5rem; height: auto; padding-left: .5rem; padding-right: .5rem; font-size: .82rem; }
    .pcard-btn .lbl-full { display: none; }
    .pcard-btn .lbl-short { display: inline; }
}

/* ==========================================================================
   Vista LISTA del catálogo: una columna, tarjeta horizontal (imagen a la izq.,
   datos al centro, CTA a la derecha). Se activa con .cat-list en el grid.
   ========================================================================== */
.cat-list { display: flex !important; flex-direction: column; gap: .6rem !important; }
.cat-list .pcard { flex-direction: row; align-items: center; gap: .85rem; padding: .6rem .75rem; }
.cat-list .pcard-img { width: 88px; flex: 0 0 88px; align-self: center; }
.cat-list .pcard-body { flex-direction: row; align-items: center; gap: 1rem; padding: 0; flex-wrap: wrap; }
.cat-list .pcard-info { flex: 1 1 60%; min-width: 0; }
.cat-list .pcard-title { -webkit-line-clamp: 2; min-height: 0; margin: 0; }
.cat-list .pcard-meta { margin-top: .3rem; }
.cat-list .pcard-sub { margin: .3rem 0 0; }
.cat-list .pcard-btn { width: auto; flex: 0 0 auto; margin: 0; align-self: center; padding-left: 1rem; padding-right: 1rem; }
.cat-list .pcard-btn .lbl-full { display: inline; }
.cat-list .pcard-btn .lbl-short { display: none; }
@media (max-width: 640px) {
    .cat-list .pcard { align-items: stretch; }
    .cat-list .pcard-img { width: 66px; flex-basis: 66px; }
    .cat-list .pcard-body { gap: .5rem; }
    .cat-list .pcard-info { flex-basis: 100%; }
    .cat-list .pcard-btn { flex: 1 1 100%; width: 100%; margin-top: .35rem; }
}
.cs-vista-toggle .btn:not(.btn-primary) { background: hsl(var(--b2)); border-color: hsl(var(--bc)/.12); }
.cs-vista-toggle .material-symbols-outlined { font-size: 1.2rem; }
/* Botón quitar (papelera) en la línea del pedido */
.cs-item-del { border: 0; background: transparent; color: hsl(var(--bc)/.4); cursor: pointer; padding: .25rem;
    display: flex; align-items: center; border-radius: .5rem; flex: 0 0 auto; }
.cs-item-del:hover { color: hsl(var(--er)); background: hsl(var(--er)/.1); }
.cs-item-del .material-symbols-outlined { font-size: 1.15rem; }

/* ==========================================================================
   Secciones KPI colapsables SOLO en móvil (ahorran alto en pantallas chicas)
   ========================================================================== */
.cs-fold-head { display: none; }
@media (max-width: 767px) {
    .cs-fold-head { display: flex; align-items: center; gap: .5rem; width: 100%; cursor: pointer;
        background: hsl(var(--b2)); border: 1px solid hsl(var(--bc)/.08); border-radius: 1rem;
        padding: .7rem 1rem; margin-bottom: .75rem; font-weight: 700; font-size: .9rem; color: hsl(var(--bc)); }
    .cs-fold-head .chev { margin-left: auto; transition: transform .2s ease; }
    .cs-fold.open .cs-fold-head .chev { transform: rotate(180deg); }
    .cs-fold:not(.open) .cs-fold-body { display: none; }

    /* Chevron para <details> con <summary> en flex (el marcador nativo se pierde). */
    .det-chev { transition: transform .18s ease; }
    details[open] > summary .det-chev { transform: rotate(180deg); }
}

/* ==========================================================================
   Carrito / solicitud de pedido (contador en topbar + modales)
   ========================================================================== */
.cs-cart-btn { position: relative; }
.cs-cart-count { position: absolute; top: -3px; right: -3px; min-width: 17px; height: 17px; padding: 0 4px;
    border-radius: 999px; background: hsl(var(--p)); color: hsl(var(--pc)); font-size: .62rem; font-weight: 800;
    display: flex; align-items: center; justify-content: center; line-height: 1; }
.cs-cart-count:empty, .cs-cart-count[data-n="0"] { display: none; }
.cs-pedido-item { display: flex; gap: .6rem; align-items: center; padding: .55rem 0; border-bottom: 1px solid hsl(var(--bc)/.07); }
.cs-pedido-item:last-child { border-bottom: 0; }
.cs-pedido-thumb { width: 44px; height: 44px; border-radius: .6rem; object-fit: cover; background: hsl(var(--b2)); flex: 0 0 auto;
    display: flex; align-items: center; justify-content: center; color: hsl(var(--bc)/.3); }
.cs-qty { display: inline-flex; align-items: center; border: 1px solid hsl(var(--bc)/.15); border-radius: .7rem; overflow: hidden; }
.cs-qty button { width: 34px; height: 34px; background: hsl(var(--b2)); border: 0; color: hsl(var(--bc)); font-size: 1.1rem; cursor: pointer; }
.cs-qty button:hover { background: hsl(var(--bc)/.08); }
.cs-qty input { width: 48px; height: 34px; text-align: center; border: 0; background: transparent; color: hsl(var(--bc)); font-weight: 700; }
.cs-qty input::-webkit-outer-spin-button, .cs-qty input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* ==========================================================================
   Tablas de datos: Acordeón en móvil (Sin scroll horizontal)
   ========================================================================== */
/* Guarda contra desborde horizontal en una columna flex. `.app-main` ya lo trae en su
   propia regla, así que acá queda sólo `.content`. */
.content { min-width: 0; }
.cs-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.cs-table-wrap .table { width: 100%; }
.cs-table-wrap .badge, .cs-table-wrap .btn { white-space: nowrap; }

/* --- Estilos del Acordeón --- */
.detail-row td { padding: 0 !important; border: 0 !important; }
.detail-content {
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.3s ease, padding 0.3s ease;
    padding: 0 0.75rem;
}
.detail-content.open {
    max-height: 300px; /* Ajusta según el contenido */
    padding: 0.75rem;
}
.detail-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.5rem;
    font-size: 0.85rem;
    background: hsl(var(--b2, 230 20% 95%));
    padding: 0.75rem;
    border-radius: 0.5rem;
}
.detail-grid .detail-item { display: flex; flex-direction: column; }
.detail-grid .detail-label { font-size: 0.7rem; opacity: 0.6; text-transform: uppercase; letter-spacing: 0.05em; }
.detail-grid .detail-value { font-weight: 600; }

/* --- Responsive (Mobile First) --- */
@media (max-width: 768px) {
    
    /* 🪤 ESTO ERA `overflow-x: hidden !important` PARA TODAS LAS TABLAS, Y ESCONDÍA DATOS.
       La idea original ("cortar lo que sobra en vez de pintarlo encima") sólo funciona si la
       pantalla tiene el acordeón que devuelve las columnas escondidas — y ese acordeón
       (.hide-mobile + .expand-btn) existe en DOS pantallas de las más de VEINTE que usan
       .cs-table-wrap. En las otras, las columnas quedaban sencillamente inalcanzables desde el
       teléfono: en /vendedor/clientes.php no se veían «Debe», «Vencido» ni «Última compra», y en
       la ficha del pedido no se veían «P. unit.» ni «Subtotal». Un vendedor lo reportó como que
       "no puede desplazar la pantalla del pedido" — y tenía razón, no se podía.
       Ahora el default es poder desplazar (que es lo que hace `.cs-table-wrap` en escritorio) y
       esconder es lo que se pide, con `.cs-table-acordeon`, en las dos pantallas que sí lo
       compensan. Para las pantallas principales esto es un piso: el rediseño a tarjetas lo
       reemplaza. */
    .cs-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
    .cs-table-acordeon { overflow-x: hidden !important; }
    
    /* 2. Dejamos que la tabla se calcule sola, sin forzarla */
    .cs-table-wrap .table { 
        width: 100%; 
        table-layout: auto; /* Cambiamos a 'auto' */
        min-width: 0; 
    }
    
    /* Ocultar columnas que no son clave */
    .hide-mobile { display: none !important; }
    
    /* Mostrar el botón expandir */
    .expand-btn { display: inline-flex !important; }
    
    /* 3. La primera columna (Cliente) tiene un TOPE de ancho del 45% — sólo donde se corta a
       propósito; en una tabla que ahora se desplaza, recortar el nombre con «…» no hace falta. */
    .cs-table-acordeon .table th:first-child,
    .cs-table-acordeon .table td:first-child { 
        max-width: 45%; /* Si el nombre es muy largo, se corta con (...) */
        white-space: nowrap; 
        overflow: hidden;
        text-overflow: ellipsis;
    }
    
    /* 4. Las demás columnas visibles (Neto, Atraso, Botón) se quedan con su tamaño justo */
    .cs-table-wrap .table th:not(:first-child),
    .cs-table-wrap .table td:not(:first-child) { 
        white-space: nowrap !important; /* Nunca se parte el texto */
        padding-left: 0.75rem; 
        padding-right: 0.75rem;
    }
}

/* Selector de cantidad en la tarjeta del catálogo (la suma se acumula en el carrito).
   La fila toma el lugar que tenía el botón solo (era width:100% + margin-top:auto). */
.cs-add-row { display: flex; align-items: center; gap: .4rem; margin-top: auto; }
.cs-add-row > .btn { flex: 1; min-width: 0;
  background: hsl(var(--p)); border-color: hsl(var(--p)); color: hsl(var(--pc)); }
.cs-add-row > .btn:hover { background: hsl(var(--pf)); border-color: hsl(var(--pf)); }
/* .pcard-btn se diseñó como hijo directo (width:100%): dentro de la fila manda el flex. */
.cs-add-row > .pcard-btn { width: auto; margin-top: 0; }
.cs-qty-sm { flex: none; }
.cs-qty-sm button { width: 30px; height: 32px; font-size: 1rem; }
.cs-qty-sm input { width: 36px; height: 32px; font-size: .85rem; }

/* Mobile: el botón queda solo con el ícono (add_shopping_cart ya es un carrito con +)
   y el selector se queda con el ancho sobrante. Así no desborda la tarjeta. */
@media (max-width: 640px) {
  .cs-add-row > .btn .lbl-full, .cs-add-row > .btn .lbl-short { display: none; }
  .cs-add-row > .btn { flex: none; width: 3.1rem; padding-left: 0; padding-right: 0; }
  .cs-add-row > .btn .material-symbols-outlined { font-size: 1.25rem; }
  .cs-add-row .cs-qty-sm { flex: 1; }
  .cs-add-row .cs-qty-sm input { width: 100%; }
}

/* ==========================================================================
   Bottom-nav del portal VENDEDOR · SOLO móvil (mobile-first / modelo app)
   Ordena la navegación como app en el teléfono; en desktop se oculta y manda el
   sidebar/hamburguesa. Usa el tema actual (no un tema aparte).
   ========================================================================== */
.bottom-nav { display: none; }
@media (max-width: 767px) {
    .bottom-nav {
        display: flex; justify-content: space-around; align-items: stretch;
        position: fixed; left: 0; right: 0; bottom: 0; z-index: 1030;
        background: hsl(var(--b3)); border-top: 1px solid hsl(var(--bc)/.08);
        padding: 6px 4px calc(6px + env(safe-area-inset-bottom));
        -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
    }
    .bottom-nav a {
        flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 2px;
        padding: 5px 2px; color: hsl(var(--bc)/.55); text-decoration: none;
        font-size: .62rem; font-weight: 600;
    }
    .bottom-nav a .material-symbols-outlined { font-size: 1.4rem; }
    .bottom-nav a.active { color: hsl(var(--p)); }
    .bottom-nav .bn-lbl { max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    /* aire para que el contenido no quede tapado por la barra */
    main.content { padding-bottom: calc(76px + env(safe-area-inset-bottom)); }
    /* el FAB del carrito sube por encima del bottom-nav (su estilo vive inline → !important) */
    .cs-cart-fab { bottom: calc(80px + env(safe-area-inset-bottom)) !important; }
}

/* ==========================================================================
   Navegación entre páginas (mobile-first / PWA): sin flash blanco + feedback
   ========================================================================== */
/* View Transitions entre documentos: crossfade suave en cada navegación same-origin.
   Mata el parpadeo en blanco (muy notorio en la PWA). No-op en navegadores viejos. */
@view-transition { navigation: auto; }
@media (prefers-reduced-motion: reduce) {
    ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
}

/* Barra de carga superior: aparece al tocar un link/menu y se va al cargar la nueva página. */
#csNavProg {
    position: fixed; top: 0; left: 0; height: 3px; width: 0; z-index: 2000; pointer-events: none;
    background: hsl(var(--p)); box-shadow: 0 0 10px hsl(var(--p) / .7);
    opacity: 0; transition: width .3s ease, opacity .25s ease;
}
#csNavProg.go { opacity: 1; }

/* Hover en los resultados de los buscadores inline (cliente / producto / crear-pedido). */
#cliResultados > div, #prodResultados > div, #cpRes > div { transition: background .12s; }
#cliResultados > div:hover, #prodResultados > div:hover, #cpRes > div:hover { background: hsl(var(--p) / .08); }

/* 🪤 TODO <dialog> saca su superficie del TEMA, nunca del `Canvas` del sistema.
   Un <dialog> que no declara fondo hereda `Canvas`, el lienzo del sistema operativo — y
   Safari en macOS con el sistema en modo oscuro lo devuelve NEGRO. En el tema CLARO, donde
   el texto es oscuro (--bc: 222 22% 16%), eso deja texto negro sobre fondo negro: la ventana
   sale completamente en negro. En el tema oscuro no se notaba, porque el texto claro sobre
   ese canvas oscuro se lee igual — por eso pasó desapercibido.
   Verificado el 2026-08-26 con «agregar producto» de la solicitud de compra. Son TRES los
   diálogos que no declaran fondo (analisis.php, marcas.php, solicitud.php), así que la regla
   va en la BASE y no diálogo por diálogo: el cuarto que se escriba mañana ya nace sano. */
dialog { background: hsl(var(--b2)); color: hsl(var(--bc)); }

/* Fondo sólido de los diálogos: DaisyUI .modal-box no lo trae en este tema (salían
   transparentes). Antes cada modal lo parcheaba inline; esta regla lo hace sistémico. */
.modal-box { 
background: hsl(var(--b2));
color: hsl(var(--bc));
border: 1px solid hsl(var(--cs-border));
box-shadow: 0px 0px 9px -3px rgba(94, 94, 94, 0.55);

}
/* ---------- TEMA OSCURO ---------- */
/* Dos variables que suman al tema ya definido más arriba — NO lo redefinen: el bloque
   original queda intacto y el cascade agrega éstas. Las consume el diseño nuevo del portal
   vendedor (bordes y texto secundario de las tarjetas). */
[data-theme="carshow"] {
  --cs-border: 220 20% 13%;   /* ≈ tu #0a0e18 */
  --cs-muted:  215 16% 55%;   /* ≈ tu #64748b */
}

/* ---------- TEMA CLARO ---------- */
[data-theme="carshowlight"] {
  --cs-border: 220 16% 88%;   /* gris claro visible sobre blanco */
  --cs-muted:  215 10% 45%;   /* gris medio para texto secundario */
}

/* ── Grilla de días del ritmo de meta ────────────────────────────────────────
   El sábado pide menos que un martes (pesa 0,4), así que su barra es más corta:
   la altura del riel ES el objetivo del día. El domingo y los feriados van
   rayados — no son días incumplidos, no son días. */
.cs-dia-track { width:.62rem; border-radius:99px; background:hsl(var(--bc)/.09);
                display:flex; align-items:flex-end; overflow:hidden; }
.cs-dia-track > i { display:block; width:100%; border-radius:99px; }
.cs-dia-libre .cs-dia-track { background:repeating-linear-gradient(45deg,
    hsl(var(--bc)/.05), hsl(var(--bc)/.05) 3px, transparent 3px, transparent 6px); }
.cs-dia-cap { width:.62rem; height:2px; background:hsl(var(--bc)/.32);
              border-radius:2px; margin-bottom:.15rem; }
/* «Tu día»: el número grande es el único accionable de la tarjeta. */
.cs-dia-k { font-size:.68rem; text-transform:uppercase; letter-spacing:.11em;
            font-weight:800; color:hsl(var(--cs-muted)); }
.cs-dia-v { font-weight:900; font-size:2.15rem; letter-spacing:-.025em;
            line-height:1.05; margin:.15rem 0 .1rem; }
.cs-dia-pie { display:flex; gap:.5rem; margin-top:.85rem; padding-top:.75rem;
              border-top:1px solid hsl(var(--bc)/.08); font-size:.75rem; }
.cs-dia-pie > div { flex:1; }
.cs-dia-pie b { display:block; font-size:.9rem; font-weight:800; margin-top:.1rem; }

/* ── El spinner: que gire, y que un botón siga siendo un botón ───────────────
   Anotado el 2026-09-16, arreglado el 2026-09-17.

   DaisyUI cambió el significado de `.loading` entre versiones: antes le AGREGABA
   un spinner al elemento, hoy convierte al elemento EN el spinner (una caja de
   1,5 rem pintada con `currentColor` y recortada por `mask-image`). Los dos usos
   que hay en el CRM se rompieron de maneras distintas:

   · `<span class="loading loading-spinner">` — el uso correcto, y aun así NO GIRA.
     La animación vive DENTRO del SVG que se usa como máscara, y un SVG-máscara se
     pinta estático: sus animaciones internas no corren. Peor: el primer fotograma
     de ese SVG es `stroke-dasharray:0 150`, o sea arco de largo cero. Por eso se
     ve un puntito y no un anillo. Verificado: en tailwind.css no hay un solo
     `@keyframes` de spinner fuera del data-URI.

   · `btn.classList.add('loading')` — el botón entero se vuelve la máscara. Pierde
     su texto (queda recortado por el arco) y su fondo (pasa a `currentColor`).

   Se arregla acá y no en el JS porque son ~40 llamadas en 12 archivos, y porque
   theme.css NO pasa por la purga de Tailwind. Carga DESPUÉS de tailwind.css, así
   que a igual especificidad mandan estas reglas. */

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

/* Máscara con el arco YA dibujado (dasharray fijo, no animado), y la rotación
   afuera del SVG, donde el navegador sí la corre. */
:root { --cs-spinner: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-dasharray='42 150'/%3E%3C/svg%3E"); }

.loading, .loading-spinner {
    -webkit-mask-image: var(--cs-spinner);
            mask-image: var(--cs-spinner);
    animation: cs-girar .8s linear infinite;
}

/* Un BOTÓN con `.loading` vuelve a ser un botón: sin máscara, con su texto y su
   fondo, y el spinner aparece al lado como un pseudo-elemento.
   `--btn-color` es la misma variable que usa DaisyUI para el fondo de `.btn`, y
   la fijan las variantes (`.btn-primary`, `.btn-error`, …) sobre este mismo
   elemento: por eso esto no adivina un color, lo recupera.
   `animation:none` es obligatorio — sin eso giraría el botón entero. */
.btn.loading {
    -webkit-mask-image: none;
            mask-image: none;
    display: inline-flex;
    width: auto;
    aspect-ratio: auto;
    background-color: var(--btn-color, var(--fallback-b2));
    animation: none;
    opacity: .75;
    /* Todas las llamadas hacen `btn.disabled = true` junto con la clase, y la regla
       `.btn:disabled` de DaisyUI baja el texto al 20 % (`--tw-text-opacity:.2`).
       Sin esto el texto y el spinner —que hereda `currentColor`— salen casi
       invisibles. Se devuelve la opacidad, no el color: así sigue mandando la
       paleta de DaisyUI y esto no tiene que adivinar ningún tono. */
    --tw-text-opacity: 1;
    --tw-bg-opacity: 1;
}
.btn.loading::before {
    content: ''; flex: none;
    width: 1em; height: 1em;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    animation: cs-girar .7s linear infinite;
}

/* ==========================================================================
   Mudado desde el <style> embebido en public/partials/layout.php (dentro del
   <body>, después de $__with_sidebar). Pegado TAL CUAL al final del archivo,
   sin fusionar las reglas que ya existían arriba con otro nombre — así el
   orden de cascada (esto cargaba DESPUÉS de theme.css) no cambia y ninguna
   pantalla se ve distinta. Ver el comentario arriba de .cs-bar y de .cs-exp.
   ========================================================================== */
/* Drawer de historial de auditoría (csHistorial()/csHistCerrar(), definidas en
   layout.php): se llama desde grupos.php, metas.php, rol-almacenes.php y roles.php.
   Estas reglas SÍ se usan — el contenedor (.hist-backdrop/.hist-drawer/.hist-head/
   .hist-list/.hist-x) lo pone el propio layout.php, en el mismo archivo que las
   necesita. No se tocan al mudar el bloque. */
          .hist-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.35);opacity:0;visibility:hidden;transition:.2s;z-index:55}
          .hist-backdrop.open{opacity:1;visibility:visible}
          .hist-drawer{position:fixed;top:0;right:0;height:100vh;width:min(400px,92vw);background:hsl(var(--b1));
            border-left:1px solid hsl(var(--bc)/.12);box-shadow:-16px 0 50px rgba(0,0,0,.3);transform:translateX(100%);
            transition:transform .22s ease;z-index:56;display:flex;flex-direction:column}
          .hist-drawer.open{transform:translateX(0)}
          .hist-head{display:flex;justify-content:space-between;align-items:center;padding:1rem 1.1rem;
            border-bottom:1px solid hsl(var(--bc)/.1);font-weight:800}
          .hist-x{background:none;border:0;color:hsl(var(--bc));font-size:1.1rem;cursor:pointer;opacity:.6}
          .hist-list{overflow-y:auto;padding:.4rem .5rem;flex:1}
          .hist-empty{padding:2.5rem 1rem;text-align:center;color:hsl(var(--bc)/.5);font-size:.9rem}
          .hist-item{display:flex;gap:.7rem;padding:.7rem .5rem;border-bottom:1px solid hsl(var(--bc)/.06)}
          .hist-av{width:2rem;height:2rem;border-radius:50%;background:hsl(var(--p)/.15);color:hsl(var(--p));
            display:grid;place-items:center;font-weight:700;font-size:.72rem;flex:0 0 auto}
          .hist-body{flex:1;min-width:0}
          .hist-top{display:flex;justify-content:space-between;gap:.5rem}
          .hist-user{font-weight:700;font-size:.85rem}
          .hist-when{font-size:.72rem;color:hsl(var(--bc)/.5);white-space:nowrap}
          .hist-accion{font-size:.85rem;margin-top:.1rem}
          .hist-ent{font-family:var(--mono,monospace);font-size:.7rem;color:hsl(var(--bc)/.5);margin-left:.3rem}
          .hist-det{font-size:.78rem;color:hsl(var(--bc)/.72);margin-top:.2rem;word-break:break-word}
          .hist-meta{font-size:.68rem;color:hsl(var(--bc)/.4);margin-top:.2rem;font-family:var(--mono,monospace)}

          /* ── EL CROMO DE LA APP NO SE IMPRIME ─────────────────────────────────────────
             Va acá, en el layout, y no en cada pantalla que imprime: estos elementos los
             pone el layout, así que ensuciaban TODAS las impresiones del sistema
             —presupuesto, solicitud, análisis, reporte de metas, cambios— y cada una tenía
             que acordarse de taparlos por su cuenta.

             El caso que lo destapó (2026-09-03): una banda oscura en el borde DERECHO de
             cada hoja. Era este mismo drawer de historial. Está escondido con
             `transform: translateX(100%)`, o sea justo afuera del borde derecho — pero su
             `box-shadow: -16px 0 50px` apunta hacia ADENTRO, y al imprimir 50px de sombra
             negra caían sobre la hoja. Un elemento fuera de la pantalla puede seguir
             pintando dentro de ella.

             `display:none` y no `visibility:hidden`: lo segundo saca el dibujo pero deja el
             box ocupando lugar, y una sombra sigue proyectándose igual. */
          @media print{
            .sidebar,.sidebar-backdrop,.topbar,.bottom-nav,
            .hist-drawer,.hist-backdrop,#cs-toasts,#csNavProg{ display:none!important }
            /* Sin sombras de ningún elemento fijo que se haya colado: en papel son manchas. */
            body{ background:#fff!important }
          }
        /* ==========================================================================
   CarShow CRM — Componentes UI (theme-aware)
   Todos los colores salen de las variables del tema activo.
   
   ========================================================================== */

/* === Cards glassmorphism === */
.bento {
  background: hsl(var(--b2) / 0.85);
  border: 2px solid hsl(var(--cs-border));
  border-radius: 1.5rem;
  position: relative;
  overflow: hidden;
  /* Sin esto el :hover de abajo (borde + elevación + sombra) salta de golpe: era el
     único componente del bloque nuevo que cambiaba de estado sin transición. */
  transition: border-color .25s ease, box-shadow .25s ease, transform .25s ease;
}

@media (min-width: 1024px) {
  .bento {
    -webkit-backdrop-filter: blur(24px);
    backdrop-filter: blur(24px);
    background: hsl(var(--b2));
  }
}



/* El brillo diagonal de la esquina superior izquierda.
   Iba a `transparent 60%`, y ese 60% se mide sobre la DIAGONAL de cada tarjeta: el brillo
   se estiraba o se comprimía según la forma, así que una tarjeta alta y angosta y otra ancha
   y baja se veían de tonos distintos con la misma regla. Con una distancia fija todas
   reciben el mismo destello, sin importar el tamaño. */
.bento::before{
  content:'';position:absolute;inset:0;border-radius:inherit;
  background:linear-gradient(135deg, hsl(var(--bc) / .05) 0, transparent 260px);
  pointer-events:none;
}
.bento:hover{
  border-color:hsl(var(--a) / .25);
  transform:translateY(-3px);
  box-shadow:0 24px 64px -12px hsl(var(--a) / .08);
}


/* === Barra de progreso === */
.prog-track{
  width:100%;height:10px;border-radius:999px;
  background:hsl(var(--bc) / .06);overflow:hidden;position:relative;
}
.prog-fill{
  height:100%;border-radius:999px;width:0;
  transition:width 1.6s cubic-bezier(.4,0,.2,1);
}
.prog-fill.emerald{background:linear-gradient(90deg,#10b981,#34d399);box-shadow:0 0 18px hsl(var(--a) / .35)}
.prog-fill.danger{background:linear-gradient(90deg,#ef4444,#f87171);box-shadow:0 0 18px hsl(var(--er) / .3)}
.prog-fill.amber{background:linear-gradient(90deg,#f59e0b,#fbbf24);box-shadow:0 0 18px hsl(var(--wa) / .35)}

/* === Gradientes de texto ===
   ⚠️ RENOMBRADAS AL INTEGRAR (2026-08-25). Venían como .tg / .tg-a / .tg-d / .tg-p, y `.tg` YA
   EXISTE en ops.css:85 como la ETIQUETA DE ESTADO del admin —inline-block, monoespaciada, con
   borde—, usada ~32 veces en 8 pantallas (cola de push, productos, personas, compras,
   solicitudes, vista-pedidos…). Este <style> vive dentro de `if ($with_sidebar)` y carga DESPUÉS
   del css externo, así que habría ganado: las 32 etiquetas se habrían vuelto texto con degradado
   y relleno transparente. El nombre estaba tomado, así que se renombra el que llega. */
.txt-grad{background:linear-gradient(135deg,#34d399,#6ee7b7);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
.txt-grad-a{background:linear-gradient(135deg,#fbbf24,#fcd34d);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
.txt-grad-d{background:linear-gradient(135deg,#ef4444,#fca5a5);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}

/* === Separadores === */
.divider{height:1px;background:linear-gradient(90deg,transparent,hsl(var(--cs-border)),transparent)}


/* === Reveal animation === */
.reveal{opacity:0;transform:translateY(32px);transition:all .7s cubic-bezier(.4,0,.2,1)}
.reveal.on{opacity:1;transform:translateY(0)}
.s1{transition-delay:.05s}.s2{transition-delay:.1s}.s3{transition-delay:.15s}
.s4{transition-delay:.2s}.s5{transition-delay:.25s}.s6{transition-delay:.3s}.s7{transition-delay:.35s}

/* NO hay regla global de prefers-reduced-motion, y es una decisión, no un olvido.
   El rediseño trajo una que ponía animation-duration:.01ms en `*`: con el ajuste «Reducir
   movimiento» del sistema prendido —lo tiene el dueño— apagaba TODAS las animaciones de la
   aplicación, incluida la entrada de las tarjetas en los tableros de revisión, que no es
   decoración sino la señal de que entró trabajo. Decisión del dueño (2026-08-25): las
   animaciones corren siempre.
   El proyecto ya venía tratando esta preferencia de forma PUNTUAL y sigue igual: theme.css
   la respeta para el deslizamiento del menú lateral (:261) y para las transiciones de página
   (:859), que son las dos que producen desplazamiento de pantalla completa. */

/* === Stat icon === */
.stat-icon{
  width:46px;height:46px;border-radius:13px;
  display:flex;align-items:center;justify-content:center;font-size:1.15rem;flex-shrink:0;
}

/* === Pulse dot === */
.pulse-dot{
  width:8px;height:8px;border-radius:50%;flex-shrink:0;
  animation:pdot 2s ease-in-out infinite;
}
@keyframes pdot{
  0%,100%{box-shadow:0 0 0 0 currentColor}
  50%{box-shadow:0 0 0 7px transparent}
}

/* === Scrollbar === */
::-webkit-scrollbar{width:5px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:hsl(var(--bc) / .08);border-radius:999px}

/* === Tooltip de porcentaje en barra === */
.prog-label{
  position:absolute;right:0;top:-22px;font-size:.75rem;font-weight:600;
  opacity:0;transition:opacity .3s;
}
.bento:hover .prog-label{opacity:1}


/* ⚠️ .cs-bar y .cs-bar-stack YA EXISTEN arriba en este archivo, con otra definición
   (barras simples de una sola pieza). Esta es la versión que vivía en el <style>
   embebido de layout.php, DENTRO del <body>, así que cargaba después de theme.css
   y ganaba, propiedad por propiedad, sobre la de arriba. Se pega acá, al final y sin
   fusionar, para que el orden de carga siga siendo el mismo y GANE la misma versión
   que ganaba antes: la de abajo (ésta). No fusionar las dos declaraciones. */
/* === Barras cs-bar animadas === */
.cs-bar{
  width:100%;border-radius:999px;overflow:hidden;position:relative;
  background:hsl(var(--bc) / .06);
}
.cs-bar-stack{height:.7rem;display:flex;gap:2px}
.cs-bar-stack .cs-bar-seg{
  height:100%;border-radius:999px;min-width:0;
  width:0;
  transition:width 1.4s cubic-bezier(.4,0,.2,1);
  box-shadow:0 0 12px hsl(var(--bc) / .08);
}
.cs-bar-stack.animated .cs-bar-seg{width:var(--w)}

 /* Las VERTICALES quedan fuera a propósito (:not(.cs-bar-v)). Crecen en alto, no en ancho:
   la regla de abajo les ponía width:0 y, como su style inline sólo trae la altura, no había
   nada que ganara ese cero — las barras del desglose por día salían invisibles. Sin esta
   excepción vuelven a mandar las reglas de .cs-bar-v de theme.css, que sí las contemplan. */
.cs-bar:not(.cs-bar-stack):not(.cs-bar-v){height:var(--bar-h,.45rem)}
.cs-bar:not(.cs-bar-stack):not(.cs-bar-v) > div{
  height:100%;border-radius:999px;
  transition:width 1.6s cubic-bezier(.4,0,.2,1);
  box-shadow:0 0 14px var(--bar-glow, hsl(var(--bc) / .1));
  position:relative;
}

/* ⚠️ .cs-exp y sus sub-clases (-chev, -body, -row) YA EXISTEN arriba en este archivo,
   con otra definición. Mismo caso que .cs-bar: esta versión vivía en el <style>
   embebido de layout.php y cargaba después, así que ganaba. Se pega tal cual, sin
   fusionar, para no cambiar el ganador. */
/* === Details / Summary expandible === */
.cs-exp{
  margin-top:1rem;border:1px solid hsl(var(--cs-border));border-radius:.85rem;
  overflow:hidden;transition:border-color .3s;
}
.cs-exp[open]{border-color:hsl(var(--a) / .15)}
.cs-exp > summary{
  display:flex;align-items:center;gap:.55rem;
  padding:.7rem 1rem;font-size:.85rem;font-weight:500;
  cursor:pointer;list-style:none;
  color:hsl(var(--bc));transition:background .3s;
  user-select:none;
  /* background-color:#f43434;  ← ESTO ERA UN BUG, eliminado */
}
.cs-exp > summary::-webkit-details-marker{display:none}
.cs-exp > summary:hover{background:hsl(var(--bc) / .03)}
.cs-exp > summary::marker{display:none;content:''}

.cs-exp-chev{
  margin-left:auto;font-size:1.2rem!important;
  color:hsl(var(--cs-muted));transition:transform .35s cubic-bezier(.4,0,.2,1);
}
.cs-exp[open] .cs-exp-chev{transform:rotate(180deg)}

.cs-exp-body{
  padding:0 1rem 1rem;
  animation:expOpen .35s cubic-bezier(.4,0,.2,1);
}
@keyframes expOpen{
  from{opacity:0;transform:translateY(-8px)}
  to{opacity:1;transform:translateY(0)}
}

.cs-exp-row{
  padding:.65rem 0;
  border-bottom:1px solid hsl(var(--bc) / .04);
}
.cs-exp-row:last-child{border-bottom:none;padding-bottom:0}
.cs-exp-row:first-child{padding-top:0}

/* === Grid KPI 2×6 === */
.kpi-grid{
  display:grid;
  grid-template-columns:1fr;
  grid-template-rows:repeat(6,auto);
  gap:.75rem;
}
@media (min-width:640px){
  .kpi-grid{grid-template-columns:1fr 1fr;}
}
.kg-i1{grid-column:1;grid-row:1/3}
.kg-r1{grid-column:2;grid-row:1}
.kg-r2{grid-column:2;grid-row:2}
.kg-i2{grid-column:1;grid-row:3/5}
.kg-r3{grid-column:2;grid-row:3}
.kg-r4{grid-column:2;grid-row:4}
.kg-i3{grid-column:1;grid-row:5/7}
.kg-r5{grid-column:2;grid-row:5}
.kg-r6{grid-column:2;grid-row:6}
           /**/
