:root {
    --navy:       #0b1120;
    --navy-2:     #111827;
    --navy-3:     #1a2540;
    --navy-4:     #1e2d4a;
    --blue:       #2563eb;
    --blue-light: #3b82f6;
    --blue-glow:  rgba(37,99,235,0.18);
    --cyan:       #06b6d4;
    --green:      #10b981;
    --green-bg:   rgba(16,185,129,0.12);
    --amber:      #f59e0b;
    --amber-bg:   rgba(245,158,11,0.12);
    --red:        #ef4444;
    --red-solido: #CB3A3A;  /* --red mezclado 85 % con negro: fondo de .btn-danger (texto blanco) */
    --red-bg:     rgba(239,68,68,0.12);
    --purple:     #8b5cf6;

    /* ── Gráficos del Monitoreo (--viz-*) ─────────────────────────────────
     * Una sola familia para los 20 widgets: dos colores de serie (azul y naranja,
     * que se distinguen también con daltonismo), un gris para lo que no es el
     * foco, una rampa de 5 pasos de un solo azul para magnitud (mapa de calor,
     * calendario) y tres rellenos de estado. Van en los DOS temas (ver
     * tema-claro.css); el texto NUNCA usa estos colores: lleva --text*, y el
     * color queda en la marca que lo acompaña. Los `-on` son el color de la letra
     * que va ENCIMA de cada paso de la rampa. */
    --viz-1:      #3987e5;  --viz-1-on: #0b1120;
    --viz-2:      #d95926;  --viz-2-on: #0b1120;
    --viz-neutro: #33415a;
    --viz-linea:  #6b7a8e;
    --viz-r1: #184f95;  --viz-r1-on: #ffffff;
    --viz-r2: #256abf;  --viz-r2-on: #ffffff;
    --viz-r3: #3987e5;  --viz-r3-on: #0b1120;
    --viz-r4: #6da7ec;  --viz-r4-on: #0b1120;
    --viz-r5: #b7d3f6;  --viz-r5-on: #0b1120;
    --viz-o1: #6da7ec;
    --viz-o2: #3987e5;
    --viz-o3: #2a78d6;
    --viz-o4: #256abf;
    --viz-o5: #184f95;
    --viz-critico: #d03b3b;  --viz-critico-on: #ffffff;
    --viz-serio:   #ec835a;  --viz-serio-on: #0b1120;
    --viz-aviso:   #f0b429;

    /* ── Los mismos tres colores, para TEXTO ──────────────────────────────
     * `--red`, `--blue` y `--purple` se usan de dos maneras incompatibles: como
     * color de letra (34 · 6 · 1 sitios) y como fondo de letra blanca (17 · 14 ·
     * 7). Un valor no puede servir a las dos: para leerse sobre el navy tiene que
     * ser CLARO, y para que el blanco se lea encima tiene que ser OSCURO. Con un
     * solo token, arreglar un lado rompe el otro.
     *
     * Así que los de superficie se quedan como estaban y estos tres son para
     * texto. Son los tonos 400 de la misma escala de la que salieron los 500 que
     * ya estaban, así que no traen un color nuevo al sistema.
     *
     * ⚠ Al usarlos, `color: var(--red-txt)`, nunca `background`. Y el fallback
     * `var(--red-txt, var(--red))` está puesto a propósito en los reemplazos: si
     * alguien borra la definición, el color vuelve al de antes en vez de
     * desaparecer — un `color` inválido se descarta y el texto hereda, que en una
     * celda puede dejarlo del mismo color que el fondo. */
    --red-txt:    #f87171;   /* 4,64:1 sobre el peor de los 9 fondos del oscuro */
    --blue-txt:   #60a5fa;   /* 5,04:1 */
    --purple-txt: #a78bfa;   /* 4,71:1 */
    /* El índigo de "🐟 Alimento Peces" es el cuarto de esta familia, y llegó
       tarde porque no era un token: eran seis literales #818cf8 repartidos en
       pago-facturas.js y payment-confirm.js. Medido antes de moverlo: 4,68:1 en
       oscuro (pasa) y 2,70:1 en claro (no). Un color que solo existe como
       literal no puede tener dos valores, así que el defecto era estructural. */
    --indigo:     #6366f1;   /* superficie: el mismo que ya usaba --accent-color */
    --indigo-txt: #818cf8;   /* 4,68:1 sobre el fondo de la card de categoría */
    --indigo-bg:  rgba(99,102,241,0.08);
    /* `--blue-light` es el acento (valores, enlaces, pestaña activa) y también
       aparece 18 veces como fondo, así que necesita su propia variante. En el
       tema CLARO no hace falta cambiarlo —ahí vale #00579E y ya da 6,48:1— y por
       eso `tema-claro.css` repite ese mismo valor: el claro no se mueve. */
    --blue-light-txt: #60a5fa;   /* 5,04:1 · era #3b82f6 con 3,49:1 */
    /* Los tres niveles de texto del tema OSCURO. El claro los da vuelta en
     * tema-claro.css, así que estos valores no lo afectan.
     *
     * ⚠ Los dos grises se subieron el 17-08-2026 por CONTRASTE, no por gusto.
     * `--text-3` era #64748b y quedaba en 2,89:1 sobre el fondo de las cards —
     * bien por debajo del mínimo AA de 4,5 para texto normal— y era el origen de
     * 462 de los 634 textos ilegibles del tema oscuro: un token, no 462 arreglos.
     *
     * Y `--text-2` subió aunque YA CUMPLÍA (5,36:1). Con `--text-3` en su piso de
     * 4,74:1 los dos grises quedaban a 1,13:1 entre sí, o sea indistinguibles: la
     * jerarquía de tres niveles se aplanaba a dos. Los valores de ahora dejan
     * escalones de 1,88 y 1,41 y los dos por encima del mínimo (6,68 y 4,74).
     *
     * Medido contra los seis fondos donde aparecen, tomando el peor. Al tocarlos,
     * volver a medir: el fondo MÁS CLARO es el que manda. */
    --text:       #f1f5f9;
    --text-2:     #aab6c6;
    --text-3:     #8b99ac;
    --border:     rgba(255,255,255,0.07);
    --border-2:   rgba(255,255,255,0.12);
    --radius:     10px;
    --font:       'DM Sans', sans-serif;
    --mono:       'DM Mono', monospace;

    /* ── Ritmo del sidebar (18-08-2026) ──────────────────────────────────
     * Un solo lugar para las cuatro cifras, así el riel (`.sidebar`), los
     * ítems (`.nav-item`) y los rótulos de sección (`.nav-label`) abren y
     * cierran EXACTAMENTE en el mismo tiempo y con la misma curva — antes
     * cada uno tenía la suya (`.15s` en `.nav-item` contra `.18s`/`.14s` en
     * `.sidebar`, y `.nav-label` sin transición alguna) y el desfase de
     * ~30ms entre ellos, sumado al salto de `.nav-label`, era gran parte de
     * lo que se veía "desordenado".
     *
     * Del 17-08-2026 (`528222c`) se venía de 180ms al abrir / 140ms al
     * cerrar, buscando una barra más "responsiva" — y quedó DEMASIADO
     * rápido: 140ms está por debajo del piso de ~150ms en el que un cambio
     * de tamaño se percibe como un salto en vez de un movimiento. Subir
     * ambos números (240/160) los deja los dos por encima de ese piso y
     * dentro del rango habitual de un panel que se despliega (200-300ms),
     * sin cruzar los ~400ms en que un despliegue empieza a sentirse lento.
     * La asimetría se mantiene: abrir es más lento y decelera al llegar
     * ("a propósito"); cerrar es más corto y acelera al irse (a nadie le
     * importa mirar cómo se cierra).
     *
     * 18-08-2026: el usuario aprobó el rediseño y pidió "un poco más
     * lento" (repetido dos veces, sin más detalle) — un ajuste fino, no
     * un cambio de curva. 300ms al abrir queda en el TOPE del rango
     * 200-300ms ya usado como referencia arriba, todavía lejos de los
     * ~400ms que se sienten lentos. 200ms al cerrar mantiene la misma
     * proporción abrir:cerrar (~1,5:1) que tenía 240/160, así que la
     * asimetría no cambia de carácter, solo de escala. */
    --sb-open-dur:   .3s;
    --sb-open-ease:  cubic-bezier(0,0,.2,1);
    --sb-close-dur:  .2s;
    --sb-close-ease: cubic-bezier(.4,0,1,1);

    /* La sombra proyectada de los elementos flotantes (globos, pop-ups).
     *
     * 18-08-2026. Se agregó porque siete reglas ya escribían
     * `var(--sombra, rgba(0,0,0,.5))` y el token NO EXISTÍA: caían todas al
     * fallback, así que el `var()` sugería una intención que nada cumplía. Lo
     * detectó un agente al revisar los globos de NC/ND, no un test.
     *
     * ⚠ Y el fallback no era inocuo: negro al 50% es correcto sobre el navy del
     * tema oscuro, pero sobre papel blanco se ve como una mancha gris. `tema-claro`
     * lo baja a un tinte de la tinta corporativa al 16%. Es el mismo error de fondo
     * que tenían esos globos —un valor pensado para un tema, aplicado a los dos—
     * solo que en la sombra en vez de en el fondo. */
    --sombra: rgba(0, 0, 0, .5);
  }

  * { margin:0; padding:0; box-sizing:border-box; }

  body {
    font-family: var(--font);
    background: var(--navy);
    color: var(--text);
    min-height: 100vh;
    font-size: 14px;
  }

  /* ── TOP BAR ─────────────────────────────── */
  .topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 32px;
    height: 60px;
    background: var(--navy-2);
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    z-index: 100;
  }
  .logo { display:flex; align-items:center; gap:10px; }

  .logo-sub  { font-size:11px; color:var(--text-2); font-weight:400; }

  .topbar-right { display:flex; align-items:center; gap:20px; }
  .topbar-date  { font-size:12px; color:var(--text-2); }


  /* ── LAYOUT ──────────────────────────────── */
  .shell { display:flex; height:calc(100vh - 60px); }

  /* ── SIDEBAR ─────────────────────────────── */
  /* 18-08-2026: pedido del usuario — vuelve el botón "Contraer menú" y pasa a
     ser el ÚNICO disparador del ancho. El reposo sigue siendo colapsado (solo
     iconos): la barra mide 62px acá y 220px con la clase `.expandida`, que
     pone y quita el botón desde el JS de `index.html` (y que además persiste en
     `localStorage`, así que el estado sobrevive a la recarga).

     ⚠ Lo que se fue con este cambio: `:hover` y `:has(:focus-visible)` ya NO
     tocan el ancho. La barra se abría al pasar el mouse por encima, y eso es lo
     que el usuario pidió quitar. Cuidado con reintroducir cualquiera de los dos
     como "ayuda": una sola regla que le diera ancho al riel con el puntero
     encima dejaría al botón de adorno y traería de vuelta el gesto que se quitó,
     sin que nada avise. Tiene test (), que
     busca el EFECTO y no el nombre de la pseudoclase.

     ⚠ La animación NO cambia con este pedido: sigue siendo la misma que se
     aprobó el 18-08-2026 (300ms al abrir, 200 al cerrar, curvas asimétricas,
     centralizadas en las cuatro variables `--sb-*` de arriba). Lo único que
     cambia es QUÉ selector activa el estado ancho; las duraciones, las curvas y
     las propiedades animadas son las mismas de antes, letra por letra.

     ⚠ Historia, para no repetirla: entre el 17 y el 18-08-2026 este ancho lo
     gobernó el hover, primero con `:focus-within` y después con
     `:has(:focus-visible)` — el primero dejaba la barra pegada en 220px después
     de un clic, porque un `.nav-item` con `tabindex` es enfocable con el mouse.
     Con el botón como único disparador el problema desaparece por construcción:
     el foco ya no cambia el ancho, así que no hay foco incidental que distinguir
     del foco de teclado. El `tabindex` de los ítems se queda igual — está ahí
     para que se los pueda activar con Tab, no para abrir la barra. */
  .sidebar {
    width:62px; min-width:62px;
    background: var(--navy-2);
    border-right:1px solid var(--border);
    padding:20px 0;
    display:flex; flex-direction:column; gap:4px;
    overflow-y:auto;
    /* Colapsar (el botón quita la clase): corta y con una curva de "acelera y
       se va" — a nadie le importa mirar cómo se cierra. */
    transition: width var(--sb-close-dur) var(--sb-close-ease),
                min-width var(--sb-close-dur) var(--sb-close-ease);
  }
  .sidebar.expandida {
    width:220px; min-width:220px;
    /* Expandir: un poco más de tiempo y una curva de "decelera al llegar",
       para que se sienta a propósito y no un tirón. */
    transition: width var(--sb-open-dur) var(--sb-open-ease),
                min-width var(--sb-open-dur) var(--sb-open-ease);
  }
  /* ── Sidebar en reposo (solo iconos) ────────
     El colapso es el estado por defecto y esto se activa con `:not(.expandida)`
     — cuando el botón puso la clase, estas reglas simplemente no matchean y el
     `.nav-label` / `.nav-item` / `.nav-icon` / `.badge` / `.chip` de más abajo
     (que nunca se tocaron) vuelven a mandar solos, sin necesidad de un segundo
     juego de reglas "expandido". Es la misma mecánica que gobernaba el hover
     hasta el 18-08-2026, con la clase en lugar de la pseudoclase: por eso el
     aspecto de los dos estados no cambió con el pedido del botón. */
  /* `.nav-label` es una sola palabra ("Configuración", "Análisis Ejecutivo")
     que no tiene dónde partir línea dentro de 62px: aunque el texto está
     invisible (visibility:hidden), su caja sigue en el flujo y el contenido
     que no entra se sigue pintando afuera de ella, y eso es lo que el
     `overflow-y:auto` del sidebar convierte en scroll horizontal (la regla del
     overflow: si un eje es 'visible' y el otro no, el navegador fuerza el
     'visible' a 'auto'). `overflow:hidden` (+ nowrap para no depender de si la
     frase tiene un espacio donde cortar) recorta ese contenido DENTRO de su
     propia caja de 62px, así que deja de propagarse al contenedor. */
  /* 18-08-2026: antes esta regla no tenía NINGUNA transición — `height` y
     `padding` saltaban de golpe al perder el hover, y sobre todo `visibility`
     pasaba a `visible` INSTANTÁNEO al ganarlo (el `.nav-label` de base, más
     abajo, no declaraba `visibility` y el valor inicial es `visible`).
     Medido con `transitionrun`/`transitionstart` en el navegador: mientras
     `.sidebar` y `.nav-item` tardaban 100-180ms en llegar a su tamaño final,
     el rótulo de sección ("Compras", "Ventas"…) aparecía YA a su alto
     completo en el primer cuadro — un salto vertical instantáneo que corre
     de golpe todo lo que está debajo, mientras el resto de la barra todavía
     se estaba abriendo. Ese desfase es gran parte de lo que se veía
     "desordenado". Ahora `opacity`, `height` y `padding` se apagan CON la
     misma curva y duración que el cierre del riel, y `visibility` se retrasa
     hasta que la transición de opacidad termina (el patrón estándar para que
     un elemento con `visibility:hidden` se pueda desvanecer en vez de
     desaparecer de un tirón). */
  .sidebar:not(.expandida) .nav-label {
    visibility:hidden; height:8px; padding:4px 0 0; opacity:0;
    overflow:hidden; white-space:nowrap;
    transition: opacity var(--sb-close-dur) var(--sb-close-ease),
                height var(--sb-close-dur) var(--sb-close-ease),
                padding var(--sb-close-dur) var(--sb-close-ease),
                visibility 0s linear var(--sb-close-dur);
  }
  /* font-size:0 oculta el texto del item; el icono conserva su tamaño propio.
     18-08-2026: se sacó el `justify-content:center` que tenía esta regla.
     Medido con `getBoundingClientRect()` cuadro a cuadro: al entrar el mouse,
     `justify-content` pasaba de `center` a `normal` de un solo salto —esa
     propiedad no es animable— así que el icono SALTABA del centro del riel
     (~32px) al borde izquierdo (~20px) en el primer cuadro, y recién después
     iba deslizándose de vuelta hacia la derecha a medida que crecía el
     `padding-left`. Con `padding:11px 22px` en las dos direcciones (en vez de
     centrar con `justify-content` en una y con `padding-left` en la otra) el
     icono queda centrado en el riel colapsado SIN el salto: solo el `padding`
     — que ya estaba en la transición— cambia, así que la posición es una
     función continua del tiempo en vez de un corte seguido de un deslizamiento. */
  .sidebar:not(.expandida) .nav-item {
    font-size:0; padding:11px 22px; gap:0;
    transition: all var(--sb-close-dur) var(--sb-close-ease);
  }
  .sidebar:not(.expandida) .nav-item .badge,
  .sidebar:not(.expandida) .nav-item .chip { display:none; }
  .sidebar:not(.expandida) .nav-icon {
    font-size:17px; width:auto;
    transition: font-size var(--sb-close-dur) var(--sb-close-ease),
                width var(--sb-close-dur) var(--sb-close-ease);
  }

  /* ── Botón de contraer / expandir (al fondo de la barra) ──────────────────
     Vuelve del commit 528222c~1, que lo había retirado cuando el hover pasó a
     gobernar el ancho. Se reusa su diseño —`margin-top:auto`, borde superior,
     flecha + rótulo, la flecha girando 180° al colapsar— con tres cambios:

       · La flecha era el glifo `◀`. Ahora es un SVG de la misma familia que los
         íconos del menú (lienzo 24, trazo 1,6, puntas redondeadas): un glifo lo
         dibuja la fuente del sistema, sale distinto en cada equipo y no sigue al
         tema. Es la conversión que hizo el commit c4465be con los 34 íconos.
       · El texto se oculta con `font-size:0`, como lo hace `.nav-item`, y no con
         `display:none`: así se va CON la animación del riel en vez de
         desaparecer de un tirón en el primer cuadro.
       · El relleno horizontal es el MISMO en los dos estados (23px). Es la
         lección del commit 0c7cbe5: centrar con `justify-content` en un estado y
         con `padding` en el otro hacía saltar el ícono, porque
         `justify-content` no es animable. Con 23px fijos la flecha ni se mueve:
         su centro cae en 30px, donde también caen los íconos del menú. */
  .sidebar-toggle {
    margin-top:auto;
    display:flex; align-items:center; gap:10px;
    padding:12px 23px; cursor:pointer; user-select:none;
    color:var(--text-3); font-size:12px; font-weight:500;
    border-top:1px solid var(--border);
    transition: font-size var(--sb-open-dur) var(--sb-open-ease),
                gap var(--sb-open-dur) var(--sb-open-ease),
                color .18s ease, background .18s ease;
  }
  .sidebar-toggle:hover { color:var(--text); background:var(--blue-glow); }
  /* El botón es alcanzable con Tab (`tabindex="0"` en el HTML), así que necesita
     verse cuando lo está: sin esto, quien navega con teclado no sabe dónde está
     parado y el único disparador de la barra queda invisible para él. */
  .sidebar-toggle:focus-visible { outline:2px solid var(--blue-light); outline-offset:-2px; }
  .st-arrow {
    display:inline-flex; align-items:center; flex:0 0 auto;
    transition: transform var(--sb-open-dur) var(--sb-open-ease);
  }
  .st-arrow svg { width:14px; height:14px; display:block; }
  /* `nowrap` + `overflow:hidden` por el mismo motivo que `.nav-label`: un texto
     que no entra en 62px se sigue pintando afuera de su caja, y el
     `overflow-y:auto` del riel lo convierte en una barra de scroll horizontal. */
  .st-text { white-space:nowrap; overflow:hidden; }
  .sidebar:not(.expandida) .sidebar-toggle {
    font-size:0; gap:0;
    transition: font-size var(--sb-close-dur) var(--sb-close-ease),
                gap var(--sb-close-dur) var(--sb-close-ease),
                color .18s ease, background .18s ease;
  }
  .sidebar:not(.expandida) .st-arrow {
    transform: rotate(180deg);
    transition: transform var(--sb-close-dur) var(--sb-close-ease);
  }

  .nav-label {
    font-size:10px; font-weight:600; letter-spacing:1.2px;
    color:var(--text-3); padding:12px 20px 6px; text-transform:uppercase;
    /* Alto explícito (medido: ~31px con este padding y este `font-size`) para
       que `height` tenga un número del que partir y otro al que llegar — no
       se puede transicionar contra `auto`. `overflow:hidden` de resguardo si
       algún día una fuente de reemplazo rinde una línea un par de px más alta. */
    height:31px; opacity:1; overflow:hidden;
    /* ⚠ `flex-shrink:0` y NO es de adorno: sin él estos rótulos se APLASTAN.
       `.sidebar` es un flex en columna, y con los 14 apartados de un Admin el
       contenido mide más que la barra (medido en un 1366x768: 815px de contenido
       en 708 disponibles). El flex reparte ese faltante encogiendo lo que puede,
       y `overflow:hidden` le da a estos rótulos un tamaño mínimo automático de 0:
       caen de 31px a 18px y se ve solo la mitad de arriba de la palabra. Con menos
       apartados el contenido entra y el defecto no aparece, que es lo que lo hace
       difícil de reproducir — hay que entrar como Admin y con la barra ABIERTA.
       Medido el 01-09-2026: 18px en 1366x768 y en 1280x720, 23px en 1440x900.
       ⚠ Los `.nav-item` NO lo necesitan: se midió y ninguno de los 14 se encogía.
         Ponérselo igual sería agregar una regla que no arregla nada.
       ⚠ Y va solo acá, en el bloque de la barra EXPANDIDA. Colapsada los rótulos
         se encogen a 8px A PROPÓSITO como parte de la animación de cierre (ver el
         bloque `.sidebar:not(.expandida) .nav-label` más arriba): un flex-shrink:0
         global rompería ese cierre.
       El sobrante lo absorbe el scroll que la barra ya tiene (`overflow-y:auto`). */
    flex-shrink:0;
    transition: opacity var(--sb-open-dur) var(--sb-open-ease),
                height var(--sb-open-dur) var(--sb-open-ease),
                padding var(--sb-open-dur) var(--sb-open-ease);
  }
  .nav-item {
    display:flex; align-items:center; gap:10px;
    padding:9px 20px; cursor:pointer;
    color:var(--text-2); font-weight:500; font-size:13px;
    border-left:3px solid transparent;
    transition: all var(--sb-open-dur) var(--sb-open-ease);
  }
  .nav-item:hover { color:var(--text); background:var(--blue-glow); }
  .nav-item.active {
    color:var(--blue-light-txt, var(--blue-light));
    background:var(--blue-glow);
    border-left-color:var(--blue-light);
  }
  .nav-icon {
    font-size:16px; width:20px; text-align:center;
    transition: font-size var(--sb-open-dur) var(--sb-open-ease),
                width var(--sb-open-dur) var(--sb-open-ease);
  }
  /* ── Restaurar el estado guardado NO debe animarse ───────────────────────
     Al entrar, el JS lee la preferencia de `localStorage` y pone (o no) la
     clase `.expandida`. Si ese primer cambio de clase encontrara las
     transiciones activas, quien dejó la barra abierta vería la aplicación
     abrirla sola en cada carga: una animación de 300ms que nadie disparó, que
     se lee como un tirón de la interfaz y no como su preferencia respetada.
     El JS agrega esta clase, aplica el estado y la quita dos cuadros después
     (uno no alcanza: el navegador necesita haber calculado los estilos con la
     clase puesta para no ver un cambio de valor donde interpolar).

     ⚠ Va acá, al final del bloque del sidebar, y no arriba: `.sidebar.sin-transicion
     .nav-item` y `.sidebar:not(.expandida) .nav-item` tienen la MISMA
     especificidad (0,3,0), así que ante el empate gana la que aparece después en
     la hoja. Puesta antes, sus `transition:none` quedarían pisados y la
     supresión no haría nada — el mismo error que el bloque de
     `prefers-reduced-motion` ya cometió una vez. */
  .sidebar.sin-transicion,
  .sidebar.sin-transicion .nav-item,
  .sidebar.sin-transicion .nav-label,
  .sidebar.sin-transicion .nav-icon,
  .sidebar.sin-transicion .sidebar-toggle,
  .sidebar.sin-transicion .st-arrow { transition:none; }

  /* 18-08-2026: este bloque va DESPUÉS de las cinco reglas que animan el
     sidebar (`.sidebar` en sus dos estados, `.nav-item`, `.nav-label`,
     `.nav-icon` y la flecha del botón en sus dos estados) y no antes, aunque las dos ubicaciones
     "se leen" igual de bien: dentro de un `@media` la especificidad no
     cambia, así que ante selectores de igual especificidad gana el que
     aparece DESPUÉS en la hoja. Puesto antes de `.nav-label`/`.nav-icon`
     (donde vivía el bloque original, pensado solo para `.sidebar`), sus
     `transition:none` quedaban pisados por las reglas de más abajo y
     `prefers-reduced-motion: reduce` dejaba de apagar esas dos — se
     verificó vaciando el bloque y viendo `transitionDuration` seguir en
     "0.24s" con el media query activo. */
  @media (prefers-reduced-motion: reduce) {
    .sidebar, .sidebar.expandida,
    .nav-item, .sidebar:not(.expandida) .nav-item,
    .nav-label, .sidebar:not(.expandida) .nav-label,
    .nav-icon, .sidebar:not(.expandida) .nav-icon,
    .st-arrow, .sidebar:not(.expandida) .st-arrow {
      transition:none;
    }
  }
  .badge {
    margin-left:auto; background:var(--blue);
    border-radius:10px; padding:2px 7px;
    font-size:10px; font-weight:600; color:#fff;
  }

  /* ── MAIN ────────────────────────────────── */
  .main {
    flex:1; overflow-y:auto;
    padding:28px 32px;
    display:flex; flex-direction:column; gap:24px;
  }

  /* ── PAGE HEADER ─────────────────────────── */
  .page-header { display:flex; align-items:flex-start; justify-content:space-between; }
  .page-title  { font-size:22px; font-weight:700; letter-spacing:-.3px; }
  .page-sub    { font-size:13px; color:var(--text-2); margin-top:3px; }
  .header-actions { display:flex; gap:10px; }
  .btn {
    display:flex; align-items:center; gap:6px;
    padding:8px 16px; border-radius:8px; font-size:13px;
    font-family:var(--font); font-weight:500; cursor:pointer;
    border:none; transition:background-color .15s, color .15s, border-color .15s, box-shadow .15s, opacity .15s, transform .15s;
  }
  .btn-primary { background:var(--blue); color:#fff; }
  .btn-primary:hover { background:var(--blue-light); }
  .btn-ghost   { background:transparent; color:var(--text-2); border:1px solid var(--border-2); }
  .btn-ghost:hover { color:var(--text); border-color:var(--border-2); background:var(--navy-3); }

  /* ── KPI CARDS ───────────────────────────── */
  .kpi-grid {
    display:grid;
    grid-template-columns: repeat(4,1fr);
    gap:14px;
  }
  .kpi-card {
    background:var(--navy-3);
    border:1px solid var(--border);
    border-radius:var(--radius);
    padding:18px 20px;
    position:relative; overflow:hidden;
  }
  .kpi-card::before {
    content:''; position:absolute;
    top:0; left:0; right:0; height:3px;
    background:var(--accent-color, var(--blue));
  }
  .kpi-label { font-size:11px; color:var(--text-2); font-weight:500; letter-spacing:.5px; text-transform:uppercase; }
  /* Sin font-family propia: hereda var(--font) (DM Sans) a propósito. DM Mono
     trae el cero con una barra diagonal incorporada en el glifo (no es un
     rasgo opcional por font-feature-settings — se revisó el archivo de la
     fuente y no tiene una variante alternativa de cero), y esa barra es la
     que el número de una tarjeta KPI no debía llevar. */
  .kpi-value { font-size:26px; font-weight:700; margin:8px 0 4px; letter-spacing:-.5px; }
  .kpi-sub   { font-size:12px; color:var(--text-3); display:flex; align-items:center; gap:4px; }
  .kpi-up    { color:var(--green); }
  .kpi-down  { color:var(--red-txt, var(--red)); }

  /* ── SECTION GRID ────────────────────────── */
  .section-grid { display:grid; grid-template-columns:1fr 360px; gap:20px; }


  /* ── CARD ────────────────────────────────── */
  .card {
    background:var(--navy-3);
    border:1px solid var(--border);
    border-radius:var(--radius);
    overflow:hidden;
  }
  .card-header {
    display:flex; align-items:center; justify-content:space-between;
    padding:16px 20px; border-bottom:1px solid var(--border);
  }
  .card-title { font-weight:600; font-size:14px; }
  .card-sub   { font-size:12px; color:var(--text-2); margin-top:2px; }
  .card-body  { padding:20px; }

  /* ── WEEK TABS ───────────────────────────── */


  /* ── TABLE ───────────────────────────────── */
  .tbl-wrap { overflow-x:auto; overflow-y:auto; }
  .tbl-wrap table { min-width:600px; }
  /* Thead sticky dentro del scroll de la tabla */
  .tbl-wrap thead tr th { position:sticky; top:0; background:var(--navy-3); z-index:2; }
  table { width:100%; border-collapse:collapse; font-size:13px; }
  thead tr { border-bottom:1px solid var(--border); }
  th {
    text-align:left; padding:10px 14px;
    font-size:11px; font-weight:600; color:var(--text-3);
    letter-spacing:.5px; text-transform:uppercase; white-space:nowrap;
  }
  th:first-child { padding-left:20px; }
  th:last-child  { padding-right:20px; }
  tbody tr {
    border-bottom:1px solid var(--border);
    transition:background .1s; cursor:pointer;
  }
  tbody tr:last-child { border-bottom:none; }
  tbody tr:hover { background:rgba(255,255,255,0.03); }
  td {
    padding:11px 14px; vertical-align:middle; white-space:nowrap;
  }
  td:first-child { padding-left:20px; }
  td:last-child  { padding-right:20px; }

  .chk { width:16px; height:16px; accent-color:var(--blue); cursor:pointer; }

  /* STATUS CHIPS */
  .chip {
    display:inline-flex; align-items:center; gap:5px;
    padding:3px 9px; border-radius:20px; font-size:11px; font-weight:500;
  }
  .chip-dot { width:6px; height:6px; border-radius:50%; }
  .chip-open    { background:var(--amber-bg);  color:var(--amber);  }
  .chip-open    .chip-dot { background:var(--amber); }
  .chip-process { background:var(--blue-glow); color:var(--blue-light-txt, var(--blue-light)); }
  .chip-process .chip-dot { background:var(--blue-light); }
  .chip-paid    { background:var(--green-bg);  color:var(--green);  }
  .chip-paid    .chip-dot { background:var(--green); }
  .chip-sent    { background:rgba(139,92,246,0.15); color:var(--purple-txt, var(--purple)); }
  .chip-sent    .chip-dot { background:var(--purple); }
  .chip-overdue { background:var(--red-bg);    color:var(--red-txt, var(--red));    }
  .chip-overdue .chip-dot { background:var(--red); }
  .chip-revisar { background:rgba(245,158,11,0.15); color:var(--amber); }
  .chip-revisar .chip-dot { background:var(--amber); }

  .supplier-link {
    color:var(--blue-light-txt, var(--blue-light)); text-decoration:none; font-weight:500;
    display:flex; align-items:center; gap:5px;
  }
  .supplier-link:hover { text-decoration:underline; }

  .amount { font-weight:500; }
  .amount-neg { color:var(--red-txt, var(--red)); }
  .amount-pos { color:var(--green); }

  /* ── WEEK SUMMARY BAR ────────────────────── */


  /* ── PAYMENT ACTION BAR ──────────────────── */


  /* ── CHART ───────────────────────────────── */
  .chart-wrap { position:relative; height:220px; }

  /* ── MINI LIST ───────────────────────────── */
  .mini-list { display:flex; flex-direction:column; }
  .mini-row  {
    display:flex; align-items:center; justify-content:space-between;
    padding:11px 20px; border-bottom:1px solid var(--border);
    font-size:13px; cursor:pointer; transition:background .1s;
  }
  .mini-row:last-child { border-bottom:none; }
  .mini-row:hover { background:rgba(255,255,255,0.03); }
  .mini-name { font-weight:500; }
  .mini-sub  { font-size:11px; color:var(--text-3); margin-top:2px; }
  .mini-right { text-align:right; }

  /* ── PROGRESS BAR ────────────────────────── */
  .progress-wrap { margin-top:4px; }
  .progress-bg   { height:4px; background:var(--border); border-radius:2px; }
  .progress-fill { height:4px; border-radius:2px; background:var(--blue); }

  /* ── MODAL (Supplier Drill-down) ─────────── */
  .modal-overlay {
    display:none; position:fixed; inset:0;
    background:rgba(0,0,0,.65); z-index:9500;  /* sobre el modal de selección (9000): el drill-down de proveedor se abre desde dentro */
    align-items:center; justify-content:center;
    backdrop-filter:blur(4px);
  }
  .modal-overlay.open { display:flex; }
  .modal {
    background:var(--navy-2);
    border:1px solid var(--border-2);
    border-radius:14px; width:820px; max-width:95vw;
    max-height:90vh; overflow-y:auto;
    animation: fadeUp .25s ease;
  }
  @keyframes fadeUp {
    from { opacity:0; transform:translateY(16px); }
    to   { opacity:1; transform:translateY(0); }
  }
  .modal-header {
    display:flex; align-items:center; justify-content:space-between;
    padding:20px 24px; border-bottom:1px solid var(--border);
    position:sticky; top:0; background:var(--navy-2); z-index:1;
  }
  .modal-title { font-size:16px; font-weight:700; }
  .modal-close {
    width:32px; height:32px; border-radius:8px; border:1px solid var(--border-2);
    background:transparent; color:var(--text-2); font-size:18px; cursor:pointer;
    display:flex; align-items:center; justify-content:center;
  }
  .modal-close:hover { color:var(--text); background:var(--navy-3); }
  .modal-body { padding:24px; display:flex; flex-direction:column; gap:20px; }

  .modal-kpis { display:grid; grid-template-columns:repeat(3,1fr); gap:14px; }
  .modal-kpi  {
    background:var(--navy-3); border:1px solid var(--border);
    border-radius:8px; padding:14px 16px;
  }
  .modal-kpi-label { font-size:11px; color:var(--text-3); text-transform:uppercase; letter-spacing:.5px; }
  .modal-kpi-val   { font-size:20px; font-weight:700; font-family:var(--mono); margin-top:4px; }
  .modal-chart-wrap { height:200px; position:relative; }

  /* ── PROJECTED INCOME ────────────────────── */


  /* ── SCROLLBAR ───────────────────────────────────────────────────────────
     Antes: 6px con thumb navy-4 → se camuflaba con el fondo y costaba notar
     que una tabla tenía scroll. Ahora: 11px, riel con tinte sutil y thumb en
     un slate visible (con "padding" vía border transparente para que no se
     vea tosco), más hover/active. Aplica a la página completa y a todos los
     contenedores con scroll (tablas, modales, dropdowns). ── */
  ::-webkit-scrollbar { width:11px; height:11px; }
  ::-webkit-scrollbar-track {
    background:rgba(148,163,184,0.06);
    border-radius:6px;
  }
  ::-webkit-scrollbar-thumb {
    background:var(--border-2,#2f4156);
    border-radius:6px;
    border:2px solid transparent;          /* margen interno: thumb "flotante" */
    background-clip:padding-box;
    min-height:36px;                       /* siempre agarrable, incluso en listas largas */
  }
  ::-webkit-scrollbar-thumb:hover { background-color:var(--text-3,#64748b); background-clip:padding-box; }
  ::-webkit-scrollbar-thumb:active { background-color:var(--text-2,#94a3b8); background-clip:padding-box; }
  ::-webkit-scrollbar-corner { background:transparent; }
  /* Firefox (no soporta ::-webkit-scrollbar): color visible en toda la app.
     OJO: encapsulado en @supports porque Chrome 121+ IGNORA los estilos
     ::-webkit-scrollbar cuando scrollbar-color está definido — sin este guard,
     la regla de Firefox anularía el ancho/estilo de arriba en Chrome/Edge. */
  @supports not selector(::-webkit-scrollbar) {
    * { scrollbar-color:var(--border-2,#2f4156) rgba(148,163,184,0.06); scrollbar-width:auto; }
  }

  /* ── TOOLTIP ─────────────────────────────── */


  /* ── RESPONSIVE ──────────────────────────── */
  @media(max-width:1100px) {
    .kpi-grid { grid-template-columns:repeat(2,1fr); }
    .section-grid { grid-template-columns:1fr; }
  }

/* ── Botones de acción de Pago de Facturas (transición de color al hover) ── */
.py-act {
  padding:5px 12px; font-size:11px; font-weight:600; cursor:pointer;
  border-radius:8px; min-width:132px; font-family:inherit;
  /* ⚠ `line-height` EXPLÍCITO, y no es cosmético: estos botones cambian su etiqueta al
   *   pasar el mouse (`SI PAGAR` → `✕ Quitar`, `NO PAGAR` → `✓ Incluir`) y los símbolos
   *   los dibuja OTRA fuente, con métricas más altas. Con `line-height: normal` la caja
   *   de línea la decidía el glifo: medido en Chrome, 24px sin símbolo y 27px con símbolo,
   *   así que el botón CRECÍA 3px al pasar el mouse, la fila crecía con él y las de abajo
   *   se corrían. El usuario lo reportó como "tiembla mucho la tabla".
   *   Los 15px dan 27px de caja (15 + 10 de relleno + 2 de borde), o sea el alto que ya
   *   tenían los botones CON símbolo: esos no cambian y los otros los igualan. Elegir el
   *   alto chico habría encogido a los que ya tenían símbolo y dejado el glifo asomando.
   *   ⚠ Solo temblaban los dos pares que pasan de SIN símbolo a CON símbolo; los que ya
   *   traían uno en reposo (`⚠ REVISAR`, `✓ En nómina`) no se movían — eso fue lo que
   *   confirmó la causa. */
  line-height: 15px;
  transition: background .18s ease, color .18s ease, border-color .18s ease, transform .12s ease;
}
.py-act:active { transform: scale(.96); }
/* "＋ Incluir en nómina": neutro → al hover anticipa el VERDE del resultado */
.py-act-add { background:transparent; border:1px solid var(--border-2, #2f4156); color:var(--text-2, #94a3b8); }
.py-act-add:hover { background:rgba(34,197,94,.14); border-color:var(--green, #22c55e); color:var(--green, #22c55e); }
/* "✓ En nómina": verde → al hover anticipa el ROJO de quitarla */
.py-act-in { background:rgba(34,197,94,.12); border:1px solid var(--green, #22c55e); color:var(--green, #22c55e); }
.py-act-in:hover { background:rgba(239,68,68,.12); border-color:var(--red, #ef4444); color:var(--red-txt, var(--red, #ef4444)); }
/* "✕ Cancelar envío": rojo suave → al hover se intensifica */
.py-act-cancel { background:transparent; border:1px solid rgba(239,68,68,.4); color:var(--red-txt, var(--red, #ef4444)); }
.py-act-cancel:hover { background:rgba(239,68,68,.15); border-color:var(--red, #ef4444); }
/* REVISAR: pendiente de decisión (ámbar). Al hover anticipa el "SI PAGAR" (verde). */
.py-act-revisar { background:rgba(245,158,11,.14); border:1px solid var(--amber, #f59e0b); color:var(--amber, #f59e0b); font-weight:600; }
.py-act-revisar:hover { background:rgba(34,197,94,.14); border-color:var(--green, #22c55e); color:var(--green, #22c55e); }
@media (prefers-reduced-motion: reduce) { .py-act { transition:none; } }

/* ── Dropdown de clasificación (Proveedores Críticos) ──────────────────────
   Mismo lenguaje visual que el menú de prenómina: panel flotante con borde,
   sombra y scroll. La sección "fija" (Agregar nueva / Sin clasificación) queda
   arriba, fuera del área scrolleable. */
.clasif-dd-btn {
  display:flex; align-items:center; gap:6px; justify-content:space-between;
  min-width:170px; max-width:230px; padding:6px 11px;
  background:var(--navy-4,#0f1b2d); border:1px solid var(--border-2,#2f4156);
  border-radius:8px; color:var(--text-3,#64748b); font-size:12px;
  font-family:var(--font); cursor:pointer; outline:none; transition:border-color .15s, background .15s;
}
.clasif-dd-btn:hover { border-color:var(--blue,#2563eb); background:var(--navy-3,#132133); }
/* ⚠ Decía `var(--text)`, un token que NO EXISTE — los de esta hoja son
   `--text`, `--text-2` y `--text-3`. Como todo `var()` con reserva, no dio
   ningún error: caía siempre a #e2e8f0, que sobre el fondo oscuro es casi
   blanco y acertaba por casualidad. Con el tema claro quedó blanco sobre
   blanco: 1,09:1, el nombre de la clasificación desaparecía de la celda.
   Al usar el token real el valor en oscuro pasa de #e2e8f0 a #f1f5f9 — dos
   blancos rotos, indistinguibles a la vista. */
.clasif-dd-btn.has-val { color:var(--text,#e2e8f0); border-color:var(--border-2,#2f4156); }
.clasif-dd-lbl { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.clasif-dd-car { color:var(--text-3,#64748b); font-size:10px; flex-shrink:0; }

.clasif-dd-panel {
  /* position/left/top los setea el JS (position:fixed) para escapar del
     overflow de la tabla; aquí solo el resto del estilo. */
  z-index:200; min-width:230px; max-width:300px;
  background:var(--navy-2,#111a27); border:1px solid var(--border-2,#2f4156);
  border-radius:10px; box-shadow:0 12px 32px rgba(0,0,0,.5);
  overflow:hidden; animation:clasifDDin .12s ease;
}
@keyframes clasifDDin { from { opacity:0; transform:translateY(-4px); } to { opacity:1; transform:none; } }

/* Sección fija superior (no scrollea) */
.clasif-dd-fixed {
  padding:5px; border-bottom:1px solid var(--border,#1e2d40);
  background:var(--navy-2,#111a27); position:sticky; top:0;
}
/* Área scrolleable de opciones */
.clasif-dd-scroll {
  max-height:240px; overflow-y:auto; padding:5px;
  scrollbar-width:thin; scrollbar-color:var(--border-2,#2f4156) transparent;
}
/* Scrollbar visible y estilizada (antes casi no se veía) */
.clasif-dd-scroll::-webkit-scrollbar { width:10px; }
.clasif-dd-scroll::-webkit-scrollbar-track { background:transparent; }
.clasif-dd-scroll::-webkit-scrollbar-thumb {
  background:var(--border-2,#2f4156); border-radius:6px; border:2px solid var(--navy-2,#111a27);
}
.clasif-dd-scroll::-webkit-scrollbar-thumb:hover { background:var(--text-3,#64748b); }

.clasif-dd-item {
  padding:8px 12px; font-size:12px; color:var(--text-2,#94a3b8);
  border-radius:7px; cursor:pointer; transition:background .12s, color .12s;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.clasif-dd-item:hover { background:var(--navy-4,#0f1b2d); color:var(--text,#e2e8f0); }
.clasif-dd-item.sel   { color:var(--blue-light-txt, var(--blue-light,#60a5fa)); font-weight:600; }
.clasif-dd-item.nueva { color:var(--green,#22c55e); font-weight:600; }
.clasif-dd-item.clear { color:var(--text-3,#64748b); }
.clasif-dd-empty { padding:12px; font-size:12px; color:var(--text-3,#64748b); text-align:center; }

/* Movimiento reducido para las animaciones de entrada de esta hoja (ventana del modal y panel
   de clasificación). Se acorta la duración, no se quita: terminan en su estado final. */
@media (prefers-reduced-motion: reduce) {
  .modal, .clasif-dd-panel { animation-duration: .01ms; }
}
