/* ═══════════════════════════════════════════════════════════
   DESIGN TOKENS — única fuente de verdad de color, tipografía,
   espaciado y radios. Cambia aquí, no en las reglas de abajo.
   ═══════════════════════════════════════════════════════════ */
:root{
  /* Marca / primarios. --c-brand es el teal de marca FIJO (nunca cambia
     con el tema: navbar, botones y headers de tablero siguen siendo el
     mismo teal en claro y oscuro) -- --c-primary en cambio SÍ se redefine
     en modo oscuro (mas claro) porque la mayoria de sus usos en este
     archivo son texto/acento sobre una superficie (tarjeta, tabla) que en
     oscuro deja de ser blanca. */
  /* Fase 132: --c-brand pasa del teal aproximado de siempre (#0d4a5e) al
     teal OFICIAL de marca (#004150, muestreado de los archivos de logo
     reales) -- mismo uso de siempre (navbar, botones, headers de
     tablero), mejora el contraste con texto blanco (9.73:1 -> 11.22:1 en
     ambos temas, ya que --c-brand nunca se redefine en oscuro). --c-primary
     NO se toca: ya es casi identico al oficial (diferencia imperceptible)
     y lo usan >100 reglas de texto funcional en todo el tablero -- tocarlo
     no aporta nada visible y multiplica el riesgo de esta fase.
     --c-brand-blue y --c-brand-gray-* son los otros colores oficiales del
     logo (azul #3DA2DB, grises #575756/#878787/#B2B2B2) -- documentados
     aqui para quien los necesite (variantes de logo, graficas futuras),
     pero el azul NO se usa como texto sobre fondo claro (2.84:1, no pasa
     AA) y los grises no reemplazan --c-text-muted (ya afinado). */
  --c-brand:#004150;
  /* Fase 133 (hallazgo real, cerrando un pendiente anotado en la Fase 132):
     ~18 reglas usaban color:var(--c-surface) para texto "blanco" sobre un
     fondo que NUNCA cambia con el tema (--c-brand fijo, o un overlay
     rgba(255,255,255,..) sobre el degradado tambien fijo del login/
     bienvenida) -- pero --c-surface SI cambia con el tema (blanco en claro,
     #132c35 en oscuro), asi que en oscuro ese texto quedaba oscuro sobre
     oscuro (toast/.btn-login/.btn-primary: 1.30:1, el resto de la navbar
     iba por el mismo camino). --c-on-brand es texto que SIEMPRE va sobre
     una superficie fija oscura -- nunca se redefine, igual que --c-brand. */
  --c-on-brand:#fff;
  --c-brand-blue:#3DA2DB;
  --c-brand-green:#74B859;
  --c-brand-gray-dark:#575756; --c-brand-gray-mid:#878787; --c-brand-gray-light:#B2B2B2;
  --c-primary:#0d4a5e; --c-primary-mid:#1a7a9e; --c-primary-light:#5bb8d4;
  --c-primary-darker:#0a3d50; --c-primary-soft:#4aaccc;
  /* Fase 133 (Parte 5): sin degradados decorativos -- login y botones de
     marca pasan a color SOLIDO. Se dejan los 2 nombres de token (3 usos:
     #login-page, .btn-login, .btn-primary) para no tocar cada regla una
     por una -- ahora ambos resuelven directo a --c-brand, sin gradiente. */
  --grad-brand:var(--c-brand);
  --grad-btn:var(--c-brand);

  /* Superficies y fondos */
  --c-surface:#fff; --c-bg:#f0f4f8; --c-surface-subtle:#f7fbfd;
  --c-surface-alt:#f5f8fa; --c-surface-alt2:#fafcfd;
  --c-hover-bg:#e8f6fb;

  /* Bordes */
  --c-border:#dde8ef; --c-border-soft:#e8f0f5; --c-border-soft2:#edf2f6;

  /* Texto -- Fase 133 (accesibilidad, WCAG 2.1 AA): --c-text-muted/-2/
     -muted2 subieron de luminancia (eran 2.97/4.13/4.28:1 sobre sus fondos
     reales, por debajo del 4.5:1 de texto normal) -- re-medido con
     public/js/contraste-logic.js, no a ojo. --c-text-link ya pasaba
     (6.05:1), sin cambio. */
  --c-text:#2a4a58; --c-text-2:#4a6877; --c-text-muted:#4f6f7d;
  --c-text-muted2:#436e7d; --c-text-link:#2a6a80;

  /* Semánticos -- Fase 133: --c-success/--c-warning (relleno/borde/
     gráfica, umbral 3:1) bajaron de luminosidad para pasar incluso contra
     fondo blanco (2.87/2.85:1 -> 3.49/3.92:1); --c-danger ya pasaba
     (3.82:1), sin cambio. --c-success-dark/--c-warning-dark (texto sobre
     su -bg, umbral 4.5:1) tambien bajaron (4.31/3.80:1 -> 5.27/4.83:1);
     --c-danger-dark ya pasaba (4.63:1), sin cambio. --c-*-text son
     tokens NUEVOS, para texto corto sobre la celda/tarjeta tintada del
     semaforo (td.kpi-*, .kpi-green/org/red) -- mas saturados que los
     -dark, pensados solo para ese uso puntual. */
  --c-success:#1f9d55; --c-success-dark:#15753a; --c-success-bg:#e8f8f5;
  --c-danger:#e74c3c;  --c-danger-dark:#c0392b;  --c-danger-bg:#fde8e8;
  --c-warning:#c26a00; --c-warning-dark:#b84800; --c-warning-bg:#fef3e2;
  --c-success-text:#17703b; --c-warning-text:#8f4a00; --c-danger-text:#b02a1d;
  --c-accent:#ffd54f;  --c-accent-bg:#fff8e1;    --c-purple:#8e44ad;
  --c-info-bg:#eaf4fb;  --c-on-primary-soft:#a8d8ea;

  /* Anillo de foco (WCAG 2.4.7/2.4.11) -- reusa --c-primary-mid (ya es un
     token por tema) porque pasa >=3:1 contra --c-surface/--c-bg en los 2
     temas (4.86/4.40 claro, 5.34/6.27 oscuro). Sobre --c-brand (navbar,
     siempre oscuro) NO alcanza (2.31) -- ahi se usa --c-on-brand en vez
     de --c-focus, ver la seccion ".navbar *:focus-visible" mas abajo. */
  --c-focus:var(--c-primary-mid);

  /* Borde de CONTROL (input/select/boton de filtro) -- WCAG 1.4.11, 3:1
     minimo contra la superficie donde se dibuja. Los bordes DECORATIVOS de
     tarjetas (--c-border, sin cambio) pueden seguir suaves: esta fase solo
     toca donde el borde es la unica pista del limite de un control
     interactivo real (ver Parte 1 del prompt de la Fase 133). */
  --c-border-control:#6b8794;

  /* Sombras (RGB del primario para rgba(...)) */
  --shadow-rgb:13,74,94;
  --shadow-sm:0 2px 8px rgba(var(--shadow-rgb),0.07);
  --shadow-md:0 2px 12px rgba(var(--shadow-rgb),0.1);
  --shadow-lg:0 8px 40px rgba(var(--shadow-rgb),0.2);
  /* Fase 136 (F05): ya se repetia IDENTICO 3 veces (los overlays grandes:
     Calidad/Cargas/constructor de dashboards) antes de nombrarlo -- cero
     cambio visual, solo deja de estar duplicado. */
  --shadow-xl:0 20px 60px rgba(var(--shadow-rgb),0.35);

  /* Tipografía */
  --font-sans:'Quicksand','Segoe UI',system-ui,-apple-system,Roboto,sans-serif;

  /* Escala tipografica (Fase 133, Parte 4) -- 118 declaraciones font-size
     sueltas (41 valores distintos, de 0.62rem/~10px a 3rem) migradas a
     estos 8 tokens (7 de la tabla del prompt + --fs-display, para los
     pocos icono-emoji grandes que tambien usan font-size). --fs-note
     (12px) es el PISO: ningun texto de la plataforma queda por debajo.
     Pesos: Quicksand solo trae 4/public/fonts/ (400/500/600/700, Fase
     132) -- --fw-* se queda en esos 4, nunca 800/300 (el navegador cae al
     peso cargado mas cercano, pero declarar solo los reales evita
     sorpresas). */
  --fs-note:0.75rem; --fs-small:0.8125rem; --fs-body:0.875rem;
  --fs-body-lg:1rem; --fs-h3:1.125rem; --fs-h2:1.375rem;
  --fs-kpi:1.75rem; --fs-display:2rem;
  --fw-regular:400; --fw-medium:500; --fw-semibold:600; --fw-bold:700;
  --lh-tight:1.25; --lh-body:1.5;

  /* Escala de espaciado (base 4px) — usar en reglas nuevas */
  --space-1:4px; --space-2:8px; --space-3:12px; --space-4:16px;
  --space-5:20px; --space-6:24px; --space-8:32px; --space-10:40px;

  /* Radios */
  --r-sm:6px; --r-md:10px; --r-lg:14px; --r-xl:18px; --r-pill:20px;

  /* Movimiento (Fase 136, PR 4) -- especificacion completa en
     docs/auditoria-ui-fase135.md ("Especificacion de movimiento") y
     docs/sistema-de-diseno.md. 3 duraciones, 2 curvas, sin rebotes --
     pensadas para un tablero de datos denso (crisp), no una app de
     consumo. Usar SIEMPRE estos tokens en vez de un numero suelto. */
  --dur-fast:120ms;   /* feedback de prensado/hover */
  --dur-medium:200ms; /* dropdowns, pestañas, microinteracciones */
  --dur-slow:320ms;   /* modales/paneles grandes */
  --ease-out:cubic-bezier(0.23,1,0.32,1);     /* entradas -- el default para casi todo */
  --ease-in-out:cubic-bezier(0.77,0,0.175,1); /* movimiento ya en pantalla (ej. contenido que cambia de posicion) */

  /* Fase 99: le dice al navegador que dibuje los controles nativos (la
     lista emergente de un <select>, el scrollbar...) en modo claro -- sin
     esto, la lista hereda el `color` que tenga puesto el <select> (varios
     selects del encabezado lo fuerzan a blanco para verse bien SOBRE el
     encabezado oscuro) pero el navegador le pinta el FONDO de la lista en
     blanco tambien (su default), asi que el texto queda blanco sobre
     blanco. Ver la regla `option,optgroup` mas abajo, que ademas fija
     color Y fondo EXPLICITOS para no depender solo de esta pista. */
  color-scheme:light;
}

/* ═══════════════════════════════════════════════════════════
   TEMA OSCURO — mismo set de tokens de arriba, redefinido.
   --c-brand NO se redefine a propósito (ver comentario arriba).
   ═══════════════════════════════════════════════════════════ */
:root[data-theme="dark"]{
  --c-primary:#5fc9ea; --c-primary-mid:#3fa8cc; --c-primary-light:#2f7fa0;
  --c-primary-darker:#082028; --c-primary-soft:#1a5a72;

  --c-surface:#132c35; --c-bg:#0a1e25; --c-surface-subtle:#17343e;
  --c-surface-alt:#1a3941; --c-surface-alt2:#143038;
  --c-hover-bg:#1c3e49;

  --c-border:#2a4a56; --c-border-soft:#23414c; --c-border-soft2:#1f3b45;

  --c-text:#dbe9ee; --c-text-2:#a9c3cc; --c-text-muted:#7fa3ae;
  --c-text-muted2:#8fb4bf; --c-text-link:#7dd6f0;

  --c-success:#3ecf7e; --c-success-dark:#4ade80; --c-success-bg:#123a26;
  --c-danger:#f0665a;  --c-danger-dark:#f87171;  --c-danger-bg:#3a1c1c;
  --c-warning:#f0a248; --c-warning-dark:#fbbf24; --c-warning-bg:#3a2a10;
  /* Fase 133: en oscuro estos 3 ya pasaban AA de sobra (7-8:1 contra su
     -bg) -- los -text solo reusan el mismo valor que -dark, no hace falta
     un tono aparte. */
  --c-success-text:#4ade80; --c-warning-text:#fbbf24; --c-danger-text:#fca5a5;
  --c-accent:#ffd54f;  --c-accent-bg:#3a2f0a;    --c-purple:#c084fc;
  --c-info-bg:#122f42;  --c-on-primary-soft:#7dd6f0;

  /* Fase 133: borde de control -- #2a4a56 (--c-border) solo daba 1.54:1
     contra --c-surface oscuro, por debajo del 3:1 minimo. */
  --c-border-control:#5a8290;

  --shadow-rgb:0,0,0;
  --shadow-sm:0 2px 8px rgba(var(--shadow-rgb),0.35);
  --shadow-md:0 2px 12px rgba(var(--shadow-rgb),0.4);
  --shadow-lg:0 8px 40px rgba(var(--shadow-rgb),0.55);

  color-scheme:dark;
}

/* ── Tipografia de marca (Fase 132) ──
   Quicksand autoalojada (public/fonts/, OFL -- ver LICENSE-quicksand.txt),
   NUNCA Google Fonts en runtime (privacidad + la CSP de server.js solo
   permite 'self'/data: en font-src). Solo 4 pesos latin (400/500/600/700,
   ~61KB total en woff2) -- ni latin-ext ni vietnamese, la plataforma es en
   espanol. font-display:swap evita texto invisible mientras carga. */
@font-face{font-family:'Quicksand';font-style:normal;font-weight:400;font-display:swap;src:url('../fonts/quicksand-latin-400.woff2') format('woff2');}
@font-face{font-family:'Quicksand';font-style:normal;font-weight:500;font-display:swap;src:url('../fonts/quicksand-latin-500.woff2') format('woff2');}
@font-face{font-family:'Quicksand';font-style:normal;font-weight:600;font-display:swap;src:url('../fonts/quicksand-latin-600.woff2') format('woff2');}
@font-face{font-family:'Quicksand';font-style:normal;font-weight:700;font-display:swap;src:url('../fonts/quicksand-latin-700.woff2') format('woff2');}
/* Numeros tabulares (ancho fijo por digito) en KPIs y celdas de tabla --
   Quicksand es redondeada y mas ancha que Segoe UI, sin esto una columna
   de numeros se desalinea levemente al cambiar de digito. */
.stat-num,.aurora-kpi .kv,.aurora-kpi .kpi-val,.qi-preview .qi-pill .qv,
.aurora-table tbody td,.aurora-rank-table td,.perm-table td{font-variant-numeric:tabular-nums;}

/* ── Fase 99 (hallazgo real en producción, Brave/Windows): el selector MES
   de arriba (y el resto de selects del encabezado de cada módulo) fuerza
   `color:#fff` en linea para leerse sobre su encabezado oscuro -- ese color
   se hereda en la lista emergente que abre el navegador al hacer clic, y
   el navegador le pone fondo blanco a esa lista (default de UA, o el
   fondo que le toque al tema si no hay `color-scheme`), asi que las
   opciones quedan en blanco sobre blanco: solo se lee la que tiene el
   mouse encima (el navegador la resalta con su azul de seleccion nativo).
   Encontrado en el selector MES, pero el mismo problema le puede pasar a
   CUALQUIER <select> que herede un color pensado para su propio fondo
   (no el de la lista) -- por eso el arreglo es UNA regla central, no un
   parche por select: SIEMPRE color y fondo explicitos en `option`/
   `optgroup`, tomados de los mismos tokens de texto/superficie del tema
   actual (nunca de lo que tenga puesto el <select> padre) — el cierre del
   select (lo que se ve sin abrir la lista) no lo toca esta regla, sigue
   leyendo su propio `color` como siempre. */
select option,select optgroup{color:var(--c-text);background-color:var(--c-surface);}

/* ── Toggle de tema (navbar + login) ── */
/* Fase 136 (PR 7): microinteraccion de "cambio de tema" -- el icono
   (luna/sol) giraba sin transicion, de golpe. Un giro corto (200ms,
   --dur-medium) en el propio boton cuando cambia -- no en el contenido
   del dashboard (ese sigue siendo instantaneo, Paso 4 de la auditoria:
   el cambio de tema NO debe re-disparar el fade de #gd-panels, ver PR
   6). Clase .theme-spin la agrega/quita toggleTema() (theme.js). */
.theme-toggle{background:none;border:none;color:var(--c-on-brand);font-size:var(--fs-h3);line-height:1;padding:4px 8px;cursor:pointer;border-radius:var(--r-sm);display:inline-block;transition:transform var(--dur-medium) var(--ease-out);}
.theme-toggle.theme-spin{transform:rotate(180deg);}
.theme-toggle:hover{background:rgba(255,255,255,0.12);}
.theme-toggle-fab{position:fixed;top:18px;right:18px;background:var(--c-surface);color:var(--c-primary);border:1.5px solid var(--c-border);width:40px;height:40px;border-radius:50%;font-size:var(--fs-h3);box-shadow:var(--shadow-md);z-index:50;cursor:pointer;}
.theme-toggle-fab:hover{background:var(--c-surface-subtle);}
@media(max-width:768px){.theme-toggle{font-size:var(--fs-body-lg);padding:4px 6px;}}

/* ── Ajustes de modo oscuro que no son parte del sistema de tokens de
   marca: bordes sutiles de inputs y pares fondo-pastel/texto-saturado de
   badges y estados que ya tenian su propio color fijo en claro (no
   duplicaban ninguna variable, por eso PR #51 no los tokenizo). Se
   redefinen aca directamente sobre la clase, en vez de inventar una
   variable nueva por cada uno. ── */
[data-theme="dark"] .ig input,[data-theme="dark"] .ig select,
[data-theme="dark"] .aurora-filters select,
[data-theme="dark"] .canal-btn{border-color:var(--c-border-control);}
[data-theme="dark"] .btn-reset{border-color:#5a3030;}
[data-theme="dark"] .navbar-right .btn-logout:hover{background:#5a2a28;}
[data-theme="dark"] .badge-ADMIN{background:#4a3c05;color:#ffe082;}
[data-theme="dark"] .badge-CLIENTES_DASH{background:#4a3a00;color:#ffd54f;}
[data-theme="dark"] .badge-AUX_ADMIN{background:#152a4a;color:#8ab4f8;}
[data-theme="dark"] .btn-pass,[data-theme="dark"] .hb-PASSWORD,[data-theme="dark"] .hb-PASSWORD_PROPIA{background:#2a2a4a;color:#b8b8f0;}
[data-theme="dark"] .role-card.aux-card{border-color:#2a3a5a;background:#16202f;}
[data-theme="dark"] .role-card.aux-card:hover{border-color:#3a6ac0;background:#1a2840;}
[data-theme="dark"] .role-card.admin-card{background:#2a2410;}
[data-theme="dark"] .role-card.admin-card:hover{border-color:#c99a1a;background:#3a3010;}
[data-theme="dark"] .perm-wrap{scrollbar-color:#3a5866 var(--c-bg);}
[data-theme="dark"] .perm-wrap::-webkit-scrollbar-thumb{background:#3a5866;}
[data-theme="dark"] .mt-track,[data-theme="dark"] .master-toggle .mt-track{background:#3a5866;}
[data-theme="dark"] .aux-item{border-color:#2a4a56;}
[data-theme="dark"] .aux-banner{background:#16202f;border-color:#2a3a5a;color:#8ab4f8;}
[data-theme="dark"] .hist-badge{background:#26333a;color:#a9c3cc;}
[data-theme="dark"] .hb-EDITADO{background:#4a3a00;color:#ffd54f;}
[data-theme="dark"] .hb-PERMISOS{background:#3a1a5a;color:#d8a8ff;}
[data-theme="dark"] .client-perm-item{border-color:#2a4a56;}
[data-theme="dark"] .aurora-plans-title{color:#ffb74d;}
[data-theme="dark"] .aurora-plans li{color:#e0c9a8;}
[data-theme="dark"] .dm-readonly{background:#17343e;border-color:#2a4a56;}
[data-theme="dark"] .qi-answer-si{background:#123a26;}
[data-theme="dark"] .qi-answer-no{background:#3a1c1c;}
[data-theme="dark"] .qi-answer-na{background:#1f343a;border-color:#3a5866;}
[data-theme="dark"] .qi-answer-blank{color:#5a7580;border-color:#3a5866;}
[data-theme="dark"] .qi-row{border-color:#2a4a56;}
[data-theme="dark"] .qi-select{border-color:var(--c-border-control);}
[data-theme="dark"] .qbar-wrap{background:#1f343a;}
[data-theme="dark"] .aurora-var-card.pos{background:#0f2e1c;border-color:#1e5a35;}
[data-theme="dark"] .aurora-var-card.neg{background:#3a1414;border-color:#5a2020;}
[data-theme="dark"] .aurora-var-card.pos .vc-val,[data-theme="dark"] .aurora-var-card.pos .vc-pct{color:#4ade80;}
[data-theme="dark"] .aurora-var-card.neg .vc-val,[data-theme="dark"] .aurora-var-card.neg .vc-pct{color:#f87171;}

/* Semáforo: mismo criterio verde/amarillo/rojo (semaforo-logic.js), solo
   se sube el alpha del fondo y se aclara el texto para que no se pierda
   contra una superficie oscura. */
[data-theme="dark"] td.kpi-green{background:rgba(39,174,96,.28);color:var(--c-success-text);}
[data-theme="dark"] td.kpi-org{background:rgba(230,126,34,.28);color:var(--c-warning-text);}
[data-theme="dark"] td.kpi-red{background:rgba(231,76,60,.28);color:var(--c-danger-text);}
[data-theme="dark"] .aurora-kpi.kpi-green{border-left-color:#4ade80;}
[data-theme="dark"] .aurora-kpi.kpi-red{border-left-color:#f87171;}
[data-theme="dark"] .aurora-kpi.kpi-org{border-left-color:#fbbf24;}
[data-theme="dark"] .aurora-kpi.kpi-pur{border-left-color:#c084fc;}

*{margin:0;padding:0;box-sizing:border-box;}
body{font-family:var(--font-sans);background:var(--c-bg);min-height:100vh;}

/* ── LOGIN ── */
#login-page{min-height:100vh;display:flex;align-items:center;justify-content:center;background:var(--grad-brand);}
.login-box{background:var(--c-surface);border-radius:var(--r-xl);padding:48px 52px;box-shadow:0 8px 40px rgba(var(--shadow-rgb),0.22);width:420px;max-width:95vw;text-align:center;}
.login-logo{width:220px;height:44px;margin-bottom:30px;}
/* .login-box (la tarjeta) cambia de fondo con el tema (blanco en claro,
   teal oscuro en #132c35 oscuro, ver --c-surface) -- el logo necesita la
   version a color sobre claro y la version blanca sobre oscuro. Las dos
   imagenes van siempre en el DOM (mismo ancho/alto -> sin salto de layout
   al cambiar de tema) y se alternan con display. */
.login-logo.logo-dark{display:none;}
[data-theme="dark"] .login-logo.logo-light{display:none;}
[data-theme="dark"] .login-logo.logo-dark{display:inline-block;}
.login-box h2{color:var(--c-primary);font-size:var(--fs-body-lg);margin-bottom:26px;font-weight:500;}
.ig{margin-bottom:16px;text-align:left;}
.ig label{font-size:var(--fs-note);color:var(--c-text-2);font-weight:700;text-transform:uppercase;letter-spacing:0.06em;display:block;margin-bottom:5px;}
/* Fase 136 (PR 5): border-radius unificado a --r-sm (6px) -- antes 8px
   aqui y 7px en .aurora-filters select (abajo), sin relacion entre si;
   .qi-select ya usaba --r-sm desde la Fase 133. Diferencia de 1-2px,
   cambio visual minimo pero real (a diferencia del PR 3, que solo
   nombraba valores YA identicos) -- el pedido de esta fase para
   formularios es explicito: "una sola apariencia". */
.ig input,.ig select{width:100%;padding:11px 14px;border:1.5px solid var(--c-border-control);border-radius:var(--r-sm);font-size:var(--fs-body-lg);color:var(--c-primary);outline:none;background:var(--c-surface-subtle);transition:border-color var(--dur-fast) var(--ease-out);}
.ig input:focus,.ig select:focus{border-color:var(--c-primary-mid);background:var(--c-surface);}
.pass-wrap{position:relative;}
.pass-wrap input{padding-right:44px;}
.btn-eye{position:absolute;right:10px;top:50%;transform:translateY(-50%);background:none;border:none;cursor:pointer;color:var(--c-text-muted);font-size:var(--fs-body-lg);padding:0;line-height:1;}
.btn-login{width:100%;padding:13px;background:var(--grad-btn);color:var(--c-on-brand);border:none;border-radius:8px;font-size:var(--fs-body-lg);font-weight:700;cursor:pointer;margin-top:8px;transition:opacity 0.2s;}
.btn-login:hover{background:var(--c-primary-darker);}
.login-error{color:var(--c-danger-dark);font-size:var(--fs-small);margin-top:10px;display:none;}

/* ── NAVBAR ── */
/* justify-content:space-between (removido, Fase 46) esperaba EXACTAMENTE 2
   hijos flex (brand, navbar-right) -- con el boton hamburguesa ahora
   siempre visible (ver .navbar-menu-toggle abajo) pasan a ser 3, y
   space-between los habria repartido con espacios iguales entre los tres en
   vez de pegar el logo a la izquierda. margin-left:auto en .navbar-right
   (mas abajo) logra el mismo resultado visual de siempre sin importar
   cuantos hijos haya antes. */
.navbar{background:var(--c-brand);padding:0 28px;display:flex;align-items:center;gap:10px;height:62px;position:sticky;top:0;z-index:100;box-shadow:0 2px 10px rgba(var(--shadow-rgb),0.2);}
/* El logo original era una sola imagen (símbolo a color + wordmark "InConexion"
   horneado en el mismo PNG, en teal oscuro fijo) -- el texto se perdía por
   contraste sobre el fondo del navbar, también teal oscuro (mismo var(--c-primary)
   en ambos). Se separó: .navbar-logo-icon es solo el símbolo (imagen), y el
   wordmark ahora es texto real (.navbar-logo-text) que sí puede tomar color de
   CSS. Ambos van dentro de .navbar-brand. */
.navbar-brand{display:flex;align-items:center;gap:10px;}
.navbar-logo-icon{height:34px;}
.navbar-right{display:flex;align-items:center;gap:12px;margin-left:auto;}
.navbar-user{color:var(--c-on-primary-soft);font-size:var(--fs-body);}
.navbar-role{background:rgba(255,255,255,0.15);color:var(--c-on-brand);font-size:var(--fs-note);font-weight:700;padding:3px 10px;border-radius:var(--r-pill);}
.navbar-role.aux{background:rgba(91,184,212,0.3);color:#c8eaf5;}
.navbar-role.admin-role{background:rgba(255,193,7,0.25);color:var(--c-accent);}
.btn-logout{background:rgba(255,255,255,0.12);color:var(--c-on-brand);border:none;border-radius:var(--r-sm);padding:6px 14px;font-size:var(--fs-small);cursor:pointer;transition:background 0.2s;}
.btn-logout:hover{background:rgba(255,255,255,0.22);}
.navbar-help-link{background:rgba(255,255,255,0.12);color:var(--c-on-brand);border:none;border-radius:var(--r-sm);padding:6px 14px;font-size:var(--fs-small);cursor:pointer;transition:background 0.2s;text-decoration:none;display:inline-block;}
.navbar-help-link:hover{background:rgba(255,255,255,0.22);}
/* Fase 136 (PR 7): transicion tokenizada (--dur-medium, antes 0.3s
   suelto -- 100ms de diferencia, imperceptible) para la microinteraccion
   de "Guardado" (saveData(), state.js: aparece, se queda 1.6s, se va). */
.save-flash{font-size:var(--fs-note);color:#2ecc71;font-weight:700;opacity:0;transition:opacity var(--dur-medium) var(--ease-out);white-space:nowrap;}
.save-flash.show{opacity:1;}

/* Botón hamburguesa: abre/cierra el sidebar. Hasta la Fase 46 solo existía
   en movil (sidebar off-canvas); ahora tambien colapsa el sidebar en
   escritorio (ver .sidebar / html[data-sidebar-collapsed] mas abajo), asi
   que queda visible siempre, no solo bajo la media query de celular.
   Boton/menú de perfil (agrupa nombre+rol+cerrar sesión) sigue oculto en
   desktop por defecto -- la media query de abajo lo activa en pantallas
   angostas. En desktop, .navbar-profile-dropdown se ve exactamente igual
   que antes (mismo display:flex + gap que tenía .navbar-right), así que
   envolver navbar-user/navbar-role/btn-logout en este div no cambia nada
   visualmente arriba de 768px. */
.navbar-menu-toggle{display:inline-flex;align-items:center;background:none;border:none;color:var(--c-on-brand);font-size:var(--fs-h2);line-height:1;padding:4px 8px;cursor:pointer;border-radius:var(--r-sm);}
.navbar-profile-toggle{display:none;background:none;border:none;color:var(--c-on-brand);font-size:var(--fs-h2);line-height:1;padding:4px 8px;cursor:pointer;border-radius:var(--r-sm);}
.navbar-menu-toggle:hover,.navbar-profile-toggle:hover{background:rgba(255,255,255,0.12);}
.navbar-profile-dropdown{display:flex;align-items:center;gap:12px;}
.sidebar-overlay{display:none;}

/* ── ADMIN PANEL ── */
#admin-page{display:none;min-height:100vh;}
#asesor-page{display:none;min-height:100vh;}
#supervisor-page{display:none;min-height:100vh;}
/* color:var(--c-on-brand) (blanco fijo, Fase 133 -- antes var(--c-surface),
   que en oscuro deja de ser blanco) -- antes de eso era var(--c-primary)
   (el mismo teal oscuro del fondo del navbar): texto invisible por contraste, el
   mismo bug de fondo que el logo rasterizado de arriba. var(--c-surface) es
   el token que ya usan .navbar-role y .btn-logout para texto claro sobre
   este mismo fondo, no uno nuevo. Afecta también a los navbars de
   Asesor/Supervisor, que ya usaban esta clase directamente (sin ícono) y
   tenían el mismo problema. */
.navbar-logo-text{font-size:var(--fs-body-lg);font-weight:700;color:var(--c-on-brand);letter-spacing:0.3px;}
.admin-layout{display:flex;min-height:calc(100vh - 62px);}
.sidebar{width:220px;background:var(--c-surface);border-right:1.5px solid var(--c-border);padding:24px 0 0;flex-shrink:0;transition:width 0.2s ease;}

/* Sidebar colapsable en escritorio (Fase 46, pedido de Edwin: el menu va a
   tener cada vez mas contenido -- mas lineas/campanas, Trafico de WhatsApp
   -- y hoy siempre ocupa 220px fijos). Mismo boton/misma logica de
   toggleSidebar() (ui-core.js) que YA abre/cierra el sidebar off-canvas en
   movil; en escritorio en vez de superponerse (un overlay oscuro es mas
   una necesidad de movil, donde no sobra espacio) "empuja": se colapsa a 0
   y .main-content usa el ancho liberado (ya es flex:1, no requiere ningun
   cambio ahi). El estado se guarda en localStorage (inco_sidebar_colapsado)
   y se aplica ANTES del primer paint via este atributo en <html> (mismo
   patron que data-theme, ver el <script> inline al inicio de <head> en
   index.html) -- sin esto habria un parpadeo del sidebar expandido
   colapsandose recien despues de cargar. Sin el atributo (caso por
   defecto, ningun usuario lo ha tocado todavia) el sidebar se ve
   exactamente como siempre: expandido.
   Envuelto en min-width:769px a proposito: sin esto, el selector de mayor
   especificidad ([data-sidebar-collapsed]+.sidebar vs. el .sidebar suelto
   de la media query de movil) pisaria el ancho 80%/off-canvas de movil si
   alguien colapsa en escritorio y despues abre la app en el celular (mismo
   localStorage) -- este flag es puramente de escritorio, no debe tocar el
   drawer de movil para nada. */
@media(min-width:769px){
  html[data-sidebar-collapsed="1"] .sidebar{width:0;padding-top:0;border-right-width:0;overflow:hidden;}
}
.sidebar-title{color:var(--c-primary);font-size:var(--fs-note);font-weight:700;letter-spacing:0.12em;text-transform:uppercase;padding:0 20px 12px;border-bottom:1px solid var(--c-border-soft);margin-bottom:8px;}
.sidebar-menu{list-style:none;}
/* Fase 136 (PR 5): el estado activo dependia SOLO de color/peso de fuente
   (--c-hover-bg + negrita) -- el mismo cambio que produce ":hover",
   indistinguible a simple vista de "esta es la seccion en la que estoy"
   vs "el mouse esta encima". Un indicador de posicion (borde izquierdo)
   que SOLO aparece en .active, nunca en :hover, resuelve la ambiguedad
   sin tocar el color. border-left en vez de outline/box-shadow para que
   no quede recortado por overflow en el sidebar movil. */
.sidebar-menu li a{display:flex;align-items:center;gap:9px;padding:10px 20px;color:var(--c-text-link);font-size:var(--fs-body);cursor:pointer;border-left:3px solid transparent;transition:background var(--dur-fast) var(--ease-out),color var(--dur-fast) var(--ease-out),border-color var(--dur-fast) var(--ease-out);}
.sidebar-menu li a:hover{background:var(--c-hover-bg);color:var(--c-primary);}
.sidebar-menu li a.active{background:var(--c-hover-bg);color:var(--c-primary);font-weight:600;border-left-color:var(--c-brand);}
.s-icon{font-size:var(--fs-body-lg);width:18px;text-align:center;}

.main-content{flex:1;padding:30px 36px;overflow-y:auto;min-width:0;}
.page-title{font-size:var(--fs-h2);color:var(--c-primary);font-weight:700;margin-bottom:3px;}
.page-subtitle{color:var(--c-text-2);font-size:var(--fs-body);margin-bottom:24px;}

/* ── NAVBAR + SIDEBAR EN CELULAR (<=768px) ──
   Diagnóstico (confirmado con capturas reales en teléfono, ~412px de ancho):
   1) El navbar no tenía ningún ajuste para ancho angosto — el logo se
      solapaba con "Administrador" y el badge de rol tapaba "Cerrar Sesión".
      Ya existían breakpoints (700px para grids de dashboards, 520px para
      login) pero ninguno tocaba .navbar ni .sidebar.
   2) .sidebar tenía un ancho fijo (220px, ~55% del viewport en ese
      teléfono) sin media query — le dejaba al main-content tan poco
      espacio que títulos como "Gestión de Usuarios" (.page-title) se
      partían palabra por palabra en 3+ líneas.
   Solución: patrón estándar — perfil de usuario colapsado a un ícono con
   menú desplegable (nombre/rol/cerrar sesión), y sidebar off-canvas
   (oculto por defecto, superpuesto con overlay al abrir, no empuja el
   contenido). Con el sidebar fuera del flujo, main-content usa el 100%
   del ancho y .page-title ya no necesita partirse — se le baja el
   tamaño igual, de más, como red de seguridad para títulos largos. */
@media(max-width:768px){
  .navbar{padding-left:calc(14px + env(safe-area-inset-left));padding-right:calc(14px + env(safe-area-inset-right));gap:6px;}
  .navbar-logo-icon{height:26px;}
  .navbar-logo-text{font-size:var(--fs-body);}
  .save-flash{display:none;} /* con el perfil colapsado a ícono no hay lugar para este aviso secundario */

  .navbar-right{position:relative;gap:2px;}
  .navbar-profile-toggle{display:inline-flex;align-items:center;}

  /* nombre+rol+logout: en desktop es un <div> inline (ver regla base),
     acá pasa a panel desplegable oculto por defecto. */
  .navbar-profile-dropdown{
    display:none;position:absolute;top:calc(100% + 10px);right:0;
    flex-direction:column;align-items:stretch;gap:10px;
    background:var(--c-surface);padding:14px 16px;border-radius:12px;
    min-width:180px;box-shadow:0 10px 30px rgba(var(--shadow-rgb),0.3);
    z-index:250;
  }
  .navbar-right.open .navbar-profile-dropdown{display:flex;}
  .navbar-right .navbar-user{color:var(--c-text);font-size:var(--fs-body);font-weight:600;}
  .navbar-right .navbar-role{align-self:flex-start;}
  .navbar-right .btn-logout{background:var(--c-danger-bg);color:var(--c-danger-dark);}
  .navbar-right .btn-logout:hover{background:#fbd4d1;}

  /* Sidebar: de fijo-en-flujo a off-canvas superpuesto */
  .admin-layout{display:block;min-height:0;}
  .sidebar{
    position:fixed;top:62px;left:0;bottom:0;width:80%;max-width:300px;
    transform:translateX(-105%);transition:transform 0.25s ease;
    z-index:300;box-shadow:6px 0 24px rgba(0,0,0,0.3);overflow-y:auto;
  }
  .app-page.sidebar-open .sidebar{transform:translateX(0);}
  .sidebar-overlay{
    display:none;position:fixed;top:62px;left:0;right:0;bottom:0;
    background:rgba(8,38,52,0.5);z-index:290;
  }
  .app-page.sidebar-open .sidebar-overlay{display:block;}

  .main-content{width:100%;padding:20px 16px;}
  .page-title{font-size:var(--fs-h3);}
}

/* ── STATS ── */
.stats-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:14px;margin-bottom:24px;}
.stat-card{background:var(--c-surface);border-radius:var(--r-md);padding:18px 16px;box-shadow:var(--shadow-sm);border-left:3px solid var(--c-primary-mid);}
.stat-num{font-size:var(--fs-kpi);font-weight:700;color:var(--c-primary);line-height:1;}
.stat-label{font-size:var(--fs-note);color:var(--c-text-muted);margin-top:5px;text-transform:uppercase;letter-spacing:0.06em;}

/* ── SECTION CARDS ── */
.section-card{background:var(--c-surface);border-radius:12px;box-shadow:0 2px 12px rgba(var(--shadow-rgb),0.07);overflow:hidden;margin-bottom:24px;}
.section-header{padding:16px 20px;display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid var(--c-border-soft2);flex-wrap:wrap;gap:10px;}
.section-header h3{color:var(--c-primary);font-size:var(--fs-body-lg);font-weight:700;}
.header-actions{display:flex;gap:8px;align-items:center;flex-wrap:wrap;}
.search-bar{display:flex;align-items:center;background:var(--c-bg);border:1.5px solid var(--c-border);border-radius:7px;padding:0 10px;gap:6px;}
.search-bar input{border:none;background:transparent;padding:7px 0;font-size:var(--fs-small);color:var(--c-primary);outline:none;width:180px;}
.btn-primary{background:var(--grad-btn);color:var(--c-on-brand);border:none;border-radius:var(--r-sm);padding:8px 16px;font-size:var(--fs-small);font-weight:600;cursor:pointer;white-space:nowrap;transition:opacity 0.2s;}
.btn-primary:hover{background:var(--c-primary-darker);}
.btn-reset{background:var(--c-surface);color:var(--c-danger-dark);border:1.5px solid #e0b0b0;border-radius:var(--r-sm);padding:6px 12px;font-size:var(--fs-note);font-weight:600;cursor:pointer;}
.btn-reset:hover{background:var(--c-danger-bg);}

/* ── TABLE ── */
.table-wrap{overflow-x:auto;}
table{width:100%;border-collapse:collapse;min-width:600px;}
thead th{background:var(--c-surface-alt);color:var(--c-text-muted2);font-size:var(--fs-note);font-weight:700;text-transform:uppercase;letter-spacing:0.07em;padding:10px 16px;text-align:left;white-space:nowrap;}
tbody tr{border-bottom:1px solid var(--c-bg);transition:background 0.1s;}
tbody tr:hover{background:var(--c-surface-subtle);}
tbody td{padding:10px 16px;font-size:var(--fs-body);color:var(--c-text);}
.no-results{text-align:center;color:var(--c-text-muted);padding:28px;font-size:var(--fs-body);}

/* ── BADGES ── */
.badge{display:inline-block;padding:2px 9px;border-radius:var(--r-pill);font-size:var(--fs-note);font-weight:700;white-space:nowrap;}
.badge-ADMIN{background:#fff3cd;color:#856404;}
.badge-CALIDAD{background:var(--c-success-bg);color:var(--c-success-dark);}
.badge-INVENTARIO{background:var(--c-info-bg);color:var(--c-primary-mid);}
.badge-GERENCIA{background:var(--c-danger-bg);color:var(--c-danger-dark);}
.badge-CLIENTES_DASH{background:#fef9e2;color:#9e7a00;}
.badge-AUX_ADMIN{background:#e8f0fb;color:#1a4a9e;}
.dot{display:inline-block;width:7px;height:7px;border-radius:50%;margin-right:5px;}
.dot-on{background:var(--c-success);}
.dot-off{background:var(--c-danger);}

/* ── ACTION BUTTONS ── */
.btn-sm{padding:4px 9px;border-radius:5px;font-size:var(--fs-note);font-weight:600;cursor:pointer;border:none;white-space:nowrap;transition:opacity 0.2s;}
.btn-sm:hover:not([disabled]){opacity:0.78;}
.btn-edit{background:var(--c-info-bg);color:var(--c-primary-mid);}
.btn-suspend{background:var(--c-warning-bg);color:var(--c-warning-dark);}
.btn-activate{background:var(--c-success-bg);color:var(--c-success-dark);}
.btn-delete{background:var(--c-danger-bg);color:var(--c-danger-dark);}
.btn-pass{background:#f0f0fa;color:#5e5ea8;}

/* Fase 137 (Parte B, F07): menu de acciones por fila (boton "kebab",
   SVG, nunca emoji) en Gestion de Usuarios -- antes 4 botones del mismo
   peso por fila (Editar/Contrasena/Suspender/Eliminar); ahora "Editar"
   queda visible como accion principal y las demas se agrupan en este
   menu (logica WAI-ARIA "menu button" en row-actions.js), sin cambiar
   NINGUN permiso -- cada accion sigue gateada por exactamente la misma
   condicion de antes, el menu solo reorganiza. position:fixed (no
   absolute): .table-wrap tiene overflow-x:auto, que por la regla de
   CSS computa overflow-y:auto tambien -- un hijo absolute que se saliera
   del alto visible quedaria cortado a la mitad. */
.row-actions{display:flex;align-items:center;gap:4px;}
.row-actions-kebab{display:inline-flex;align-items:center;justify-content:center;
  width:28px;height:28px;padding:0;border:none;border-radius:var(--r-sm);
  background:none;color:var(--c-text-muted);cursor:pointer;
  transition:background var(--dur-fast) var(--ease-out),color var(--dur-fast) var(--ease-out);}
.row-actions-kebab:hover,.row-actions-kebab[aria-expanded="true"]{background:var(--c-surface-subtle);color:var(--c-text);}
.row-actions-kebab svg{width:18px;height:18px;pointer-events:none;}
.row-actions-menu{position:fixed;top:0;left:0;min-width:180px;background:var(--c-surface);
  border:1px solid var(--c-border);border-radius:var(--r-md);box-shadow:var(--shadow-md);
  z-index:80;overflow:hidden;opacity:0;transform:scale(0.95);transform-origin:top right;
  pointer-events:none;
  transition:opacity var(--dur-fast) var(--ease-out),transform var(--dur-fast) var(--ease-out);}
.row-actions-menu.row-actions-menu-show{opacity:1;transform:scale(1);pointer-events:auto;}
.row-actions-menu-item{display:block;width:100%;text-align:left;padding:10px 14px;
  border:none;background:none;cursor:pointer;font-family:inherit;font-size:var(--fs-note);font-weight:600;color:var(--c-text);}
.row-actions-menu-item:hover,.row-actions-menu-item:focus-visible{background:var(--c-surface-subtle);}
.row-actions-menu-item.danger{color:var(--c-danger-dark);border-top:1px solid var(--c-border-soft2);}
.row-actions-menu-item.danger:hover,.row-actions-menu-item.danger:focus-visible{background:var(--c-danger-bg);}
@media(max-width:768px){
  .row-actions-kebab{min-width:44px;min-height:44px;width:44px;height:44px;}
  .row-actions-menu-item{padding:13px 16px;min-height:44px;}
}

/* ── ROLES GRID ── */
.roles-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(175px,1fr));gap:14px;padding:20px;}
.role-card{border:2px solid var(--c-border);border-radius:var(--r-md);padding:18px 16px;background:var(--c-surface-subtle);cursor:pointer;transition:all 0.18s;text-align:center;}
.role-card:hover{border-color:var(--c-primary-mid);background:var(--c-hover-bg);transform:translateY(-2px);box-shadow:0 4px 14px rgba(var(--shadow-rgb),0.1);}
.role-card.aux-card{border-color:#c0d0f0;background:#f0f4fb;}
.role-card.aux-card:hover{border-color:#1a4a9e;background:#e0eafb;}
.role-card.admin-card{border-color:var(--c-accent);background:#fffde7;}
.role-card.admin-card:hover{border-color:#f9a825;background:var(--c-accent-bg);}
.role-card.blocked-card{opacity:0.4;cursor:not-allowed;}
.role-card.blocked-card:hover{transform:none;box-shadow:none;border-color:var(--c-border);background:var(--c-surface-subtle);}
.role-icon{font-size:var(--fs-kpi);margin-bottom:7px;}
.role-name{font-size:var(--fs-body);font-weight:700;color:var(--c-primary);margin-bottom:3px;}
.role-count{font-size:var(--fs-note);color:var(--c-text-muted);}
.role-locked{font-size:var(--fs-note);color:#bbb;margin-top:4px;}
.back-btn{display:inline-flex;align-items:center;gap:6px;background:var(--c-bg);color:var(--c-primary);border:none;border-radius:var(--r-sm);padding:7px 13px;font-size:var(--fs-small);font-weight:600;cursor:pointer;margin-bottom:18px;transition:background 0.2s;}
.back-btn:hover{background:var(--c-border);}

/* ── PERM TABLE ── */
.perm-wrap{overflow-x:auto;scrollbar-width:thin;scrollbar-color:#b8d0dc var(--c-bg);}
.perm-wrap::-webkit-scrollbar{height:6px;}
.perm-wrap::-webkit-scrollbar-track{background:var(--c-bg);border-radius:var(--r-sm);}
.perm-wrap::-webkit-scrollbar-thumb{background:#b8d0dc;border-radius:var(--r-sm);}
.perm-table{border-collapse:separate;border-spacing:0;width:100%;min-width:max-content;}
.perm-table .col-user{position:sticky;left:0;z-index:2;background:var(--c-surface-alt);min-width:200px;white-space:nowrap;}
.perm-table tbody .col-user{background:var(--c-surface);border-right:1px solid var(--c-border-soft);}
.perm-table tbody tr:hover .col-user{background:var(--c-surface-subtle);}
.perm-table .col-all{position:sticky;left:200px;z-index:2;background:var(--c-surface-alt);min-width:90px;border-right:2px solid var(--c-border);white-space:nowrap;}
.perm-table tbody .col-all{background:var(--c-surface-alt2);border-right:2px solid var(--c-border);}
.perm-table tbody tr:hover .col-all{background:var(--c-surface-subtle);}
.perm-table thead th{background:var(--c-surface-alt);color:var(--c-text-muted2);font-size:var(--fs-note);font-weight:700;text-transform:uppercase;letter-spacing:0.06em;padding:10px 14px;text-align:center;white-space:nowrap;border-bottom:2px solid var(--c-border);}
.perm-table thead th.col-user{text-align:left;}
.perm-table thead th.col-all{color:var(--c-primary);}
.perm-table tbody td{padding:9px 14px;font-size:var(--fs-small);color:var(--c-text);text-align:center;border-bottom:1px solid var(--c-bg);}
.perm-table tbody td.col-user{text-align:left;}
.u-name{font-weight:600;color:var(--c-primary);font-size:var(--fs-small);display:block;line-height:1.3;}
.u-login{font-size:var(--fs-note);color:var(--c-text-muted);display:block;}
.u-status{font-size:var(--fs-note);display:inline-flex;align-items:center;margin-top:2px;}

/* ── TOGGLES ── */
.mini-toggle{position:relative;display:inline-block;width:32px;height:17px;cursor:pointer;}
.mini-toggle input{opacity:0;width:0;height:0;position:absolute;}
.mt-track{position:absolute;inset:0;background:#d0dde5;border-radius:17px;transition:background 0.18s;}
.mini-toggle input:checked + .mt-track{background:var(--c-primary-mid);}
.mt-thumb{position:absolute;top:2.5px;left:2.5px;width:12px;height:12px;background:var(--c-surface);border-radius:50%;transition:left 0.18s;pointer-events:none;box-shadow:0 1px 3px rgba(0,0,0,0.15);}
.mini-toggle input:checked ~ .mt-thumb{left:17.5px;}
.master-toggle{position:relative;display:inline-block;width:36px;height:20px;cursor:pointer;}
.master-toggle input{opacity:0;width:0;height:0;position:absolute;}
.master-toggle .mt-track{position:absolute;inset:0;background:#d0dde5;border-radius:var(--r-pill);transition:background 0.18s;}
.master-toggle input:checked + .mt-track{background:var(--c-brand);}
.master-toggle .mt-thumb{position:absolute;top:3px;left:3px;width:14px;height:14px;background:var(--c-surface);border-radius:50%;transition:left 0.18s;pointer-events:none;box-shadow:0 1px 3px rgba(0,0,0,0.2);}
.master-toggle input:checked ~ .mt-thumb{left:19px;}

/* ── AUX PERMS ── */
.aux-section-title{font-size:var(--fs-note);font-weight:700;color:var(--c-primary);text-transform:uppercase;letter-spacing:0.08em;margin-bottom:14px;padding-bottom:8px;border-bottom:2px solid var(--c-border-soft);display:flex;align-items:center;gap:8px;}
.aux-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:10px;margin-bottom:6px;}
.aux-item{display:flex;align-items:center;justify-content:space-between;background:var(--c-surface-subtle);border:1.5px solid #e0ecf3;border-radius:8px;padding:10px 14px;font-size:var(--fs-small);color:var(--c-text);}
.aux-item.role-item{border-color:var(--c-border);}
.aux-banner{background:#eef2fb;border:1.5px solid #c0d0f0;border-radius:var(--r-md);padding:14px 18px;margin-bottom:20px;display:flex;align-items:center;gap:12px;color:#1a4a9e;font-size:var(--fs-body);}

/* ── HISTORIAL ── */
.hist-badge{display:inline-block;padding:2px 9px;border-radius:var(--r-pill);font-size:var(--fs-note);font-weight:700;white-space:nowrap;background:#eef1f4;color:#4a5a68;}
.hb-CREADO{background:var(--c-success-bg);color:var(--c-success-dark);}
.hb-SUSPENDIDO{background:var(--c-warning-bg);color:var(--c-warning-dark);}
.hb-ACTIVADO{background:var(--c-info-bg);color:var(--c-primary-mid);}
.hb-ELIMINADO{background:var(--c-danger-bg);color:var(--c-danger-dark);}
.hb-PASSWORD,.hb-PASSWORD_PROPIA{background:#f0f0fa;color:#5e5ea8;}
.hb-EDITADO{background:#fef9e2;color:#9e7a00;}
.hb-PERMISOS{background:#f3e8ff;color:#6b21a8;}
/* Fase 113 (tema A): usan var() a proposito (como CREADO/ELIMINADO arriba)
   para que se adapten solas al tema oscuro, sin override aparte. */
.hb-LOGIN_OK{background:var(--c-success-bg);color:var(--c-success-dark);}
.hb-LOGIN_FALLIDO{background:var(--c-danger-bg);color:var(--c-danger-dark);}
.hist-footer{padding:12px 20px;border-top:1px solid var(--c-border-soft2);background:var(--c-surface-alt2);}
.hist-filter-sel{padding:7px 12px;border:1.5px solid var(--c-border);border-radius:7px;font-size:var(--fs-small);color:var(--c-primary);background:var(--c-bg);outline:none;cursor:pointer;}

/* ── USER DASHBOARD ── */
#user-page{display:none;min-height:100vh;background:linear-gradient(160deg,var(--c-primary-darker) 0%,var(--c-primary-mid) 55%,var(--c-primary-soft) 100%);}
.user-page-body{min-height:calc(100vh - 62px);display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px 24px;}
.user-welcome{text-align:center;margin-bottom:44px;}
.user-logo{width:320px;max-width:80vw;margin-bottom:24px;filter:drop-shadow(0 6px 20px rgba(0,0,0,0.2));}
.user-welcome h1{color:var(--c-on-brand);font-size:var(--fs-kpi);font-weight:700;margin-bottom:6px;text-shadow:0 2px 8px rgba(0,0,0,0.15);}
.user-welcome p{color:rgba(255,255,255,0.72);font-size:var(--fs-body-lg);}
.dash-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:20px;width:100%;max-width:700px;}
.dash-btn{background:rgba(255,255,255,0.1);backdrop-filter:blur(10px);border:1.5px solid rgba(255,255,255,0.2);border-radius:var(--r-pill);padding:38px 24px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;cursor:pointer;transition:all 0.22s;text-align:center;color:var(--c-on-brand);position:relative;overflow:hidden;}
.dash-btn:hover{background:rgba(255,255,255,0.22);transform:translateY(-5px);box-shadow:0 14px 36px rgba(0,0,0,0.2);border-color:rgba(255,255,255,0.45);}
.dash-icon{font-size:var(--fs-display);line-height:1;}
.dash-label{font-size:var(--fs-body-lg);font-weight:700;letter-spacing:0.02em;}
.dash-sub{font-size:var(--fs-note);color:rgba(255,255,255,0.62);margin-top:2px;}
.dash-btn.mod-calidad{border-color:rgba(39,174,96,0.5);background:rgba(39,174,96,0.15);}
.dash-btn.mod-calidad:hover{background:rgba(39,174,96,0.28);}
.dash-btn.mod-inventario{border-color:rgba(26,122,158,0.5);background:rgba(26,122,158,0.2);}
.dash-btn.mod-inventario:hover{background:rgba(26,122,158,0.35);}
.dash-btn.mod-gerencia{border-color:rgba(192,57,43,0.4);background:rgba(192,57,43,0.15);}
.dash-btn.mod-gerencia:hover{background:rgba(192,57,43,0.28);}
.dash-btn.mod-clientes{border-color:rgba(243,156,18,0.4);background:rgba(243,156,18,0.15);}
.dash-btn.mod-clientes:hover{background:rgba(243,156,18,0.28);}

/* ── CLIENTS MODAL ── */
.clients-modal-overlay{display:none;position:fixed;inset:0;background:rgba(10,50,70,0.6);z-index:500;align-items:center;justify-content:center;backdrop-filter:blur(3px);}
.clients-modal-overlay.show{display:flex;}
.clients-modal{background:var(--c-surface);border-radius:var(--r-xl);padding:32px;width:600px;max-width:95vw;max-height:85vh;overflow-y:auto;box-shadow:0 16px 56px rgba(var(--shadow-rgb),0.3);}
.clients-modal-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:24px;}
.clients-modal-header h3{color:var(--c-primary);font-size:var(--fs-h3);font-weight:700;}
.btn-close-modal{background:var(--c-bg);border:none;border-radius:8px;padding:6px 14px;color:var(--c-text-2);font-size:var(--fs-body);font-weight:600;cursor:pointer;}
.btn-close-modal:hover{background:var(--c-border);}
.clients-grid{display:flex;flex-direction:column;gap:8px;}
.client-card{border:1.5px solid var(--c-border);border-radius:var(--r-md);padding:14px 18px;background:var(--c-surface-subtle);cursor:pointer;transition:all 0.18s;display:flex;align-items:center;justify-content:space-between;}
.client-card:hover{border-color:var(--c-primary-mid);background:var(--c-hover-bg);box-shadow:0 3px 10px rgba(var(--shadow-rgb),0.1);}
.client-card .client-name{font-size:var(--fs-body);font-weight:700;color:var(--c-primary);}

/* ── MODALS ── */
.modal-overlay{display:none;position:fixed;inset:0;background:rgba(var(--shadow-rgb),0.45);z-index:1000;align-items:center;justify-content:center;}
.modal-overlay.show{display:flex;}
.modal{background:var(--c-surface);border-radius:var(--r-lg);padding:32px 32px 26px;width:520px;max-width:95vw;box-shadow:0 8px 40px rgba(var(--shadow-rgb),0.2);max-height:92vh;overflow-y:auto;}
.modal h3{color:var(--c-primary);font-size:var(--fs-body-lg);margin-bottom:20px;font-weight:700;}
.form-row{display:flex;gap:12px;}
.form-row .ig{flex:1;}
.modal-btns{display:flex;gap:10px;margin-top:20px;justify-content:flex-end;}
.btn-cancel{background:var(--c-bg);color:var(--c-text-2);border:none;border-radius:var(--r-sm);padding:8px 16px;font-size:var(--fs-body);font-weight:600;cursor:pointer;}
.client-perms-section{margin-top:16px;border:1.5px solid var(--c-border);border-radius:var(--r-md);padding:16px;background:var(--c-surface-subtle);display:none;}
.client-perms-section h4{color:var(--c-primary);font-size:var(--fs-small);font-weight:700;text-transform:uppercase;letter-spacing:0.06em;margin-bottom:12px;}
.client-perm-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:8px;}
.client-perm-item{display:flex;align-items:center;gap:8px;font-size:var(--fs-small);color:var(--c-text);background:var(--c-surface);border:1px solid #e0ecf3;border-radius:var(--r-sm);padding:7px 10px;}
.client-perm-item input[type=checkbox]{accent-color:var(--c-primary-mid);width:15px;height:15px;flex-shrink:0;cursor:pointer;}

.toast{position:fixed;bottom:24px;right:24px;background:var(--c-brand);color:var(--c-on-brand);padding:11px 18px;border-radius:9px;font-size:var(--fs-body);font-weight:500;box-shadow:0 4px 18px rgba(var(--shadow-rgb),0.28);z-index:3000;opacity:0;transition:opacity 0.3s;pointer-events:none;max-width:340px;}
.toast.show{opacity:1;}
.hidden{display:none;}

/* ── AURORA DASHBOARD MODAL ── */
#aurora-overlay{display:none;position:fixed;inset:0;background:rgba(8,38,52,0.75);z-index:600;align-items:center;justify-content:center;backdrop-filter:blur(4px);}
#aurora-overlay.show{display:flex;}
#aurora-modal{background:var(--c-bg);border-radius:var(--r-xl);width:97vw;max-width:1200px;max-height:94vh;overflow-y:auto;box-shadow:var(--shadow-xl);display:flex;flex-direction:column;}
.aurora-header{background:var(--c-brand);padding:16px 24px;display:flex;align-items:center;justify-content:space-between;border-radius:18px 18px 0 0;flex-shrink:0;}
.aurora-header h2{color:var(--c-on-brand);font-size:var(--fs-body-lg);font-weight:700;margin:0;}
.aurora-header .aurora-sub{color:var(--c-on-primary-soft);font-size:var(--fs-note);margin-top:2px;}
/* Fase 131 (Parte 4): "Ultima actualizacion" -- texto en --c-on-primary-soft
   (mismo token YA usado arriba para texto legible sobre el header, que es
   SIEMPRE fondo oscuro de marca -- --c-brand no cambia con el tema --
   contraste AA garantizado en claro/oscuro por construccion, sin inventar
   un color nuevo). El "resaltado" es el borde izquierdo de color: naranja
   para Mobilize, verde de marca InConexion (--c-brand-green, Fase 132)
   para el resto.*/
.gd-ultima-act{color:var(--c-on-primary-soft);font-size:var(--fs-note);font-weight:600;margin-top:4px;padding:3px 10px 3px 9px;border-left:3px solid var(--c-brand-green);background:rgba(255,255,255,0.08);border-radius:0 5px 5px 0;display:inline-block;}
.gd-ultima-act-mobilize{border-left-color:#f97316;}
/* Logo del cliente en el encabezado del dashboard (hoy solo Mobilize,
   Fase 132 Parte 8) -- oculto por defecto (display:none inline en el
   HTML), JS lo muestra solo para ese cliente. .aurora-header es SIEMPRE
   var(--c-brand) (teal fijo, no cambia con el tema) -- una sola version
   del logo (clara, para fondo oscuro), sin alternar con el tema. */
.gd-cliente-logo{display:block;max-width:140px;height:auto;margin-bottom:4px;}
.aurora-close{background:rgba(255,255,255,0.15);border:none;color:var(--c-on-brand);border-radius:var(--r-sm);padding:6px 14px;font-size:var(--fs-small);cursor:pointer;}
.aurora-close:hover{background:rgba(255,255,255,0.28);}

/* KPI strip */
.aurora-kpis{display:flex;gap:10px;padding:14px 18px;background:var(--c-surface);border-bottom:1px solid var(--c-border);flex-wrap:wrap;}
.aurora-kpi{flex:1;min-width:110px;background:var(--c-surface-subtle);border-radius:8px;padding:10px 12px;border-left:3px solid var(--c-primary-mid);}
.aurora-kpi .kv{font-size:var(--fs-h2);font-weight:700;color:var(--c-primary);line-height:1;}
.aurora-kpi .kl{font-size:var(--fs-note);color:var(--c-text-muted);text-transform:uppercase;letter-spacing:0.04em;margin-top:3px;}
.aurora-kpi.kpi-green{border-left-color:var(--c-success);}
.aurora-kpi.kpi-red{border-left-color:var(--c-danger);}
.aurora-kpi.kpi-org{border-left-color:var(--c-warning);}
.aurora-kpi.kpi-pur{border-left-color:var(--c-purple);}

/* Semaforo de umbrales_semaforo (color por dato): mismas clases kpi-green/
   kpi-org/kpi-red, aplicadas a celdas de tabla (ranking de asesores, resumen
   por campana) ademas de las tarjetas KPI de arriba — un solo vocabulario de
   color en toda la app. */
td.kpi-green{background:rgba(39,174,96,.14);color:var(--c-success-text);font-weight:700;}
td.kpi-org{background:rgba(230,126,34,.14);color:var(--c-warning-text);font-weight:700;}
td.kpi-red{background:rgba(231,76,60,.14);color:var(--c-danger-text);font-weight:700;}

/* Mismo vocabulario de color, esta vez para un <span> suelto (ej. estado de
   item/tipo de movimiento en Inventario) -- sin esta regla base, esas clases
   solo tenian estilo cuando iban prefijadas por "td." o ".aurora-kpi.", asi
   que el span quedaba sin ningun color en ningun tema. */
.kpi-green{color:var(--c-success-text);font-weight:700;}
.kpi-org{color:var(--c-warning-text);font-weight:700;}
.kpi-red{color:var(--c-danger-text);font-weight:700;}
.kpi-pur{color:var(--c-purple);font-weight:700;}

/* Oculto a la vista, legible por lector de pantalla -- tecnica estandar
   (no display:none/visibility:hidden, que lo sacan tambien del arbol de
   accesibilidad). Fase 133. */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;}

/* Simbolo del semaforo (WCAG 1.4.1) -- va DENTRO del texto coloreado, asi
   que hereda su color (verde/ambar/rojo) por construccion, nunca un color
   propio. Separado del valor con un pequeño margen; aria-hidden porque el
   texto accesible real es el .sr-only que lo acompaña. */
.gd-sem-simbolo{margin-right:4px;font-size:0.85em;}

/* Tabs */
.aurora-tabs{display:flex;background:var(--c-surface);border-bottom:2px solid var(--c-border);padding:0 18px;overflow-x:auto;flex-shrink:0;}
/* Fase 136 (F01, hallazgo real de la Fase 135: a 412px las pestañas se
   superponían ilegibles). Causa real: el min-width:24/44px de objetivos
   táctiles (línea ~930, Fase 133) es un valor EXPLÍCITO -- sustituye el
   min-width:auto que por defecto protege a un flex item de encogerse por
   debajo del ancho de su propio texto (que aquí no envuelve por
   white-space:nowrap). Sin esa protección, flex-shrink:1 (el default)
   comprimía cada pestaña hasta superponer su texto con la siguiente, en
   vez de activar el overflow-x:auto que .aurora-tabs ya tenía. Con
   flex-shrink:0 cada pestaña conserva su ancho natural y el scroll
   horizontal que ya existía pasa a funcionar de verdad. */
.atab{padding:10px 16px;font-size:var(--fs-small);font-weight:600;color:var(--c-text-2);border:none;background:none;cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-2px;white-space:nowrap;flex-shrink:0;transition:all 0.2s;}
.atab:hover{color:var(--c-primary);background:var(--c-surface-subtle);}
.atab.atab-active{color:var(--c-primary);border-bottom-color:var(--c-primary-mid);background:var(--c-info-bg);}

/* Tab panels */
.atab-panel{display:none;padding:16px 18px 20px;}
.atab-panel.visible{display:block;}
/* Fase 136 (PR 6): transicion de entrada al cambiar de pestana/sub-
   pestana (switchGenericTab/switchGenericSubtab en dashboard-generic.js
   llaman a motionEnter() despues de reemplazar el contenido via
   innerHTML -- el "exit" de la pestana vieja no se anima por separado,
   desaparece instantaneo junto con el innerHTML, que es aceptable: el
   contenido esta a punto de destruirse de todas formas, no hay nada que
   preservar visualmente). Solo opacity/transform (GPU), --dur-medium
   (200ms, tabla de la especificacion para pestanas) + --ease-out. */
#gd-panels{padding:16px 18px 20px;opacity:0;transform:translateY(4px);transition:opacity var(--dur-medium) var(--ease-out),transform var(--dur-medium) var(--ease-out);}
#gd-panels.motion-show{opacity:1;transform:translateY(0);}
#gd-panels .aurora-kpis{padding:0;background:none;border:none;margin-bottom:14px;}

/* Sub-pestanas dentro de una pestana (Fase 40) -- un nivel visualmente mas
   chico que .aurora-tabs/.atab para no confundirse con la navegacion
   principal, pero con los mismos tokens de tema claro/oscuro. */
.gd-subtabs{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:16px;}
.gd-subtab-btn{padding:6px 14px;font-size:var(--fs-note);font-weight:600;color:var(--c-text-2);border:1px solid var(--c-border);border-radius:999px;background:var(--c-surface);cursor:pointer;white-space:nowrap;transition:all 0.2s;}
.gd-subtab-btn:hover{color:var(--c-primary);border-color:var(--c-primary-mid);}
/* Fase 133 (Parte 6, hallazgo real de la prueba de regresion): texto
   blanco sobre --c-primary-mid daba 2.73:1 en tema oscuro (#3fa8cc, claro
   a proposito para servir de TEXTO sobre superficie oscura en otros usos
   -- aqui se usaba al reves, como FONDO, y el blanco encima ya no
   alcanzaba). --c-brand (fijo, 11.22:1 con blanco en los 2 temas) resuelve
   igual de bien el "seleccionado" sin crear un token nuevo. */
.gd-subtab-btn.on{color:var(--c-on-brand);background:var(--c-brand);border-color:var(--c-brand);}

/* Chart cards grid */
.aurora-grid-2{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:14px;}
.aurora-grid-1{display:grid;grid-template-columns:1fr;gap:14px;margin-bottom:14px;}
.aurora-grid-3{display:grid;grid-template-columns:1fr 1fr 1fr;gap:14px;margin-bottom:14px;}
.aurora-card{background:var(--c-surface);border-radius:var(--r-md);padding:16px;box-shadow:var(--shadow-sm);}
.aurora-card-title{font-size:var(--fs-small);font-weight:700;color:var(--c-primary);margin-bottom:12px;padding-bottom:6px;border-bottom:1px solid var(--c-bg);}
.aurora-chart-wrap{position:relative;}
.aurora-chart-wrap canvas{width:100%!important;}

/* Filtro de un panel (categorias o rango de fechas) — mismo patron visual
   que .trafico-filtros, generalizado a cualquier grafica de Gestion de
   base/Calidad que aprenda a filtrar (dashboard-generic.js). */
.gd-panel-filtro{display:flex;flex-wrap:wrap;gap:10px;align-items:flex-end;margin-bottom:10px;}
.gd-panel-filtro label{display:block;font-size:var(--fs-note);color:var(--c-text-muted);margin-bottom:3px;}
.gd-panel-filtro select,.gd-panel-filtro input{font-size:var(--fs-note);}
.gd-panel-filtro select[multiple]{min-width:130px;}

/* Ranking table */
.aurora-rank-table{width:100%;border-collapse:collapse;font-size:var(--fs-small);}
.aurora-rank-table th{background:var(--c-brand);color:var(--c-on-brand);padding:7px 10px;text-align:left;font-size:var(--fs-note);font-weight:600;}
.aurora-rank-table td{padding:7px 10px;border-bottom:1px solid var(--c-bg);color:var(--c-text);}
.aurora-rank-table tr:nth-child(even) td{background:var(--c-surface-subtle);}
.aurora-rank-table tr:first-child td{font-weight:700;color:var(--c-primary);}

/* Action plans box */
.aurora-plans{background:var(--c-accent-bg);border:1.5px solid var(--c-accent);border-radius:var(--r-md);padding:16px;margin-top:14px;}
.aurora-plans-title{font-size:var(--fs-small);font-weight:700;color:#e65100;margin-bottom:8px;}
.aurora-plans li{font-size:var(--fs-small);color:#5d4037;margin-bottom:4px;margin-left:16px;}

@media(max-width:700px){.aurora-grid-2,.aurora-grid-3{grid-template-columns:1fr;}.aurora-kpis{gap:6px;}}

/* ── KPI cards con analisis (M1 / Fase A6) ── */
.aurora-kpi.gd-kpi{position:relative;display:flex;flex-direction:column;gap:2px;padding:12px 14px;}
.gd-kpi .gd-kpi-trend{font-size:var(--fs-note);font-weight:700;margin-top:4px;display:flex;flex-wrap:wrap;align-items:baseline;gap:3px;}
.gd-kpi .gd-kpi-trend span{font-weight:500;color:var(--c-text-muted);font-size:var(--fs-note);text-transform:none;letter-spacing:0;}
.gd-kpi .gd-tr-up{color:var(--c-success-dark);}
.gd-kpi .gd-tr-down{color:var(--c-danger-dark);}
.gd-kpi .gd-tr-flat{color:var(--c-text-muted);}
.gd-kpi .gd-kpi-meta{font-size:var(--fs-note);font-weight:700;margin-top:5px;color:var(--c-text-2);display:flex;align-items:center;gap:6px;text-transform:none;letter-spacing:0;}
.gd-kpi .gd-meta-bar{flex:0 0 46px;height:5px;border-radius:3px;background:var(--c-border);overflow:hidden;display:inline-block;}
.gd-kpi .gd-meta-bar i{display:block;height:100%;border-radius:3px;background:currentColor;}
.gd-kpi .gd-meta-ok{color:var(--c-success-dark);}
.gd-kpi .gd-meta-warn{color:var(--c-warning-dark);}
.gd-kpi .gd-meta-bad{color:var(--c-danger-dark);}
.gd-kpi.gd-kpi-alerta{border-left-color:var(--c-danger)!important;background:var(--c-danger-bg);}
.gd-kpi .gd-kpi-flag{position:absolute;top:6px;right:8px;font-size:var(--fs-small);color:var(--c-danger-dark);line-height:1;}

/* "?" con tooltip nativo (title) que explica una formula -- Fase 98,
   Inasistencia de ORLANT. */
.gd-help{display:inline-flex;align-items:center;justify-content:center;width:15px;height:15px;border-radius:50%;background:var(--c-border);color:var(--c-text-2);font-size:var(--fs-note);font-weight:700;cursor:help;vertical-align:middle;}

/* ── Preferencia de grafico por panel (M2) ── */
.gd-panel-tools{display:flex;gap:3px;margin-left:auto;}
.gd-panel-tools button{border:1px solid var(--c-border);background:var(--c-surface-subtle);color:var(--c-text-2);font-size:var(--fs-note);font-weight:700;border-radius:4px;padding:2px 6px;cursor:pointer;line-height:1.4;}
.gd-panel-tools button.on{background:var(--c-brand);color:var(--c-on-brand);border-color:var(--c-brand);}
.aurora-card-title.gd-flex{display:flex;align-items:center;gap:8px;}

/* ── AURORA DASHBOARD MODAL ── */
#calidad-overlay,#detalle-monitoreo-overlay,#supervisar-lider-overlay,#cargas-overlay,#gd-overlay,#dashcfg-overlay{display:none;position:fixed;inset:0;background:rgba(8,38,52,0.7);z-index:600;align-items:center;justify-content:center;backdrop-filter:blur(4px);}
#calidad-overlay.show,#detalle-monitoreo-overlay.show,#supervisar-lider-overlay.show,#cargas-overlay.show,#gd-overlay.show,#dashcfg-overlay.show{display:flex;}
/* Fase 103: #gd-modal (dashboard de cliente) salio de esta regla -- ver su
   propia regla mas abajo, a pantalla completa. Los otros 5 modales de esta
   lista se quedan EXACTAMENTE igual (ventana flotante centrada). */
#calidad-modal,#detalle-monitoreo-modal,#supervisar-lider-modal,#cargas-modal,#dashcfg-modal{background:var(--c-bg);border-radius:var(--r-xl);width:96vw;max-width:1180px;max-height:93vh;overflow-y:auto;box-shadow:var(--shadow-xl);display:flex;flex-direction:column;}
/* Fase 136 (F02, corrige el numero real de CLS de la Fase 135 -- ver
   docs/auditoria-ui-fase135.md: el 0.81 reportado ahi NO aislaba la
   apertura de ORLANT, que en realidad ya mide 0.014; el salto real
   (CLS 0.14, medido con PerformanceObserver/layout-shift) esta aqui: la
   tabla "Cargas registradas" llega por fetch despues del primer pintado,
   #cargas-modal pasa de ~501px a 768px (pantalla completa) cuando se
   puebla. min-height reserva el espacio tipico de antemano. */
#cargas-modal{min-height:70vh;}

/* Fase 103 (pedido de InCo, "ver mejor las graficas"): el dashboard de
   cliente ya no es una ventana flotante -- ocupa toda la ventana del
   navegador. 100dvh (no 100vh) para que en movil no quede un recorte por
   la barra de direcciones (dvh = "dynamic viewport height", ya con buen
   soporte en los navegadores que usa esta plataforma). La cabecera queda
   "sticky" dentro del propio scroll del modal (ver mas abajo) para que el
   titulo/MES/Exportar/Cerrar sigan a la vista sin tener que subir scroll.
   Mismo fondo/overflow/flex que el resto -- solo cambian tamano, radio y
   sombra.
   Fase 105 (hallazgo real de la verificacion final en produccion de la
   Fase 104, con un Chrome real -- en headless nunca aparecio): `width`
   era `100vw`, que en un Chrome real (no headless) se calcula EXCLUYENDO
   el ancho de la barra de scroll de la pagina de fondo (si la hay) --
   el modal quedaba ~15px mas angosto que el viewport real, en las 3
   resoluciones x 2 temas probadas. `width:100%` en cambio hereda del
   padre (#gd-overlay, `position:fixed;inset:0` -- ESE si es inmune a la
   barra de scroll, su tamano lo fija el viewport visual directo, no una
   unidad `vw`), asi que siempre da el ancho real sin importar si hay
   barra de scroll de fondo o no. `height` se deja en `100dvh` (no
   `100%`): es el mismo caso vertical, pero nunca se vio el problema ahi
   (alto medido = alto real en las 3 resoluciones) y `dvh` resuelve un
   problema real aparte (recorte por la barra de direcciones en movil)
   que `100%` no resolveria igual de bien. */
#gd-modal{background:var(--c-bg);border-radius:0;width:100%;height:100dvh;max-width:none;max-height:none;overflow-y:auto;box-shadow:none;display:flex;flex-direction:column;}
#gd-modal .aurora-header{position:sticky;top:0;z-index:2;border-radius:0;}

/* Fase 136 (PR 8, F04): entrada/salida animada de los 6 modales/overlays
   -- CSS solo no puede animar el toggle display:none/flex que ya usan
   (ver docs/auditoria-ui-fase135.md, Paso 1), asi que motion-helpers.js
   (motionAbrirModal/motionCerrarModal) agrega/quita .motion-modal-hidden
   en el hijo real (#calidad-modal, etc.), SIN tocar la clase .show del
   overlay (el mecanismo de display:none/flex de arriba sigue exactamente
   igual, intacto). --dur-slow (320ms, la mas lenta de la especificacion
   -- tabla de la auditoria: modales 200-500ms) + --ease-out, scale(0.97)
   nunca scale(0) (review-animations: "nada aparece de la nada").
   Ajustado tras "OK modales" (2 condiciones del usuario, 2026-10-09): el
   estado BASE (sin la clase) es VISIBLE -- si motion-helpers.js no carga
   (red, CSP, bloqueo), motionAbrirModal() nunca se llama (cada caller ya
   se protege con "typeof motionAbrirModal === 'function'") y el modal se
   ve igual de bien, solo sin el efecto de entrada. Antes el estado base
   era invisible: un modal real dependia de que este script cargara para
   NO quedar invisible -- lo opuesto de progressive enhancement.
   .motion-modal-hidden es el estado TRANSITORIO (justo antes de entrar /
   al terminar de salir), nunca el estado por defecto. */
#calidad-modal,#detalle-monitoreo-modal,#supervisar-lider-modal,#cargas-modal,#dashcfg-modal,#gd-modal{
  opacity:1;transform:scale(1);transition:opacity var(--dur-slow) var(--ease-out),transform var(--dur-slow) var(--ease-out);
}
#calidad-modal.motion-modal-hidden,#detalle-monitoreo-modal.motion-modal-hidden,#supervisar-lider-modal.motion-modal-hidden,#cargas-modal.motion-modal-hidden,#dashcfg-modal.motion-modal-hidden,#gd-modal.motion-modal-hidden{
  opacity:0;transform:scale(0.97);
}

/* Overlays de los modulos Inventario / Gerencia / Gestion Humana.
   (Inventario y Gerencia no tenian regla propia — quedaban como bloque suelto
   al fondo; se corrige aqui junto con el nuevo de Gestion Humana. Fase 10.) */
#inventario-overlay,#gerencia-overlay,#gestionhumana-overlay{display:none;position:fixed;inset:0;background:rgba(8,38,52,0.72);z-index:600;align-items:center;justify-content:center;backdrop-filter:blur(4px);}
#inventario-overlay.show,#gerencia-overlay.show,#gestionhumana-overlay.show{display:flex;}
#inventario-modal,#gerencia-modal,#gestionhumana-modal{background:var(--c-bg);border-radius:var(--r-xl);width:96vw;max-width:1180px;max-height:93vh;overflow-y:auto;box-shadow:var(--shadow-xl);display:flex;flex-direction:column;padding-bottom:18px;}
.dm-readonly{width:100%;padding:11px 14px;border:1.5px solid #e0eaf0;border-radius:8px;font-size:var(--fs-body-lg);color:var(--c-primary);background:#eef3f6;min-height:20px;}
.qi-answer-badge{border-radius:var(--r-sm);padding:5px 12px;font-size:var(--fs-small);font-weight:700;min-width:60px;text-align:center;display:inline-block;}
.qi-answer-si{background:#e8f8f1;color:var(--c-success-dark);border:1.5px solid var(--c-success);}
.qi-answer-no{background:#fdecea;color:var(--c-danger-dark);border:1.5px solid var(--c-danger);}
.qi-answer-na{background:#eef1f2;color:var(--c-text-muted);border:1.5px solid #cfe0e8;}
.qi-answer-blank{background:var(--c-surface-subtle);color:#b8c9d1;border:1.5px dashed #cfe0e8;}
.qi-cat-header{background:var(--c-brand);color:var(--c-on-brand);font-weight:700;font-size:var(--fs-note);padding:8px 12px;border-radius:6px 6px 0 0;margin-top:14px;}
.qi-row{display:flex;align-items:center;justify-content:space-between;gap:10px;background:var(--c-surface);border:1px solid #e0eaf0;border-top:none;padding:9px 12px;font-size:var(--fs-small);color:var(--c-text);}
.qi-row .qi-label{flex:1;}
.qi-row .qi-crit{color:var(--c-danger-dark);font-weight:700;margin-right:4px;}
.qi-row .qi-weight{color:var(--c-text-muted);font-size:var(--fs-note);margin-left:6px;}
.qi-select{border:1.5px solid var(--c-border-control);border-radius:var(--r-sm);padding:5px 8px;font-size:var(--fs-small);font-family:inherit;background:var(--c-surface);color:var(--c-primary);font-weight:600;min-width:90px;}
.qi-select.qi-si{border-color:var(--c-success);}
.canal-toggle{display:flex;gap:8px;}
.canal-btn{flex:1;padding:10px 14px;border:1.5px solid var(--c-border-control);border-radius:8px;background:var(--c-surface-subtle);color:var(--c-text-muted);font-weight:700;font-size:var(--fs-body);cursor:pointer;transition:all 0.15s;font-family:inherit;}
.canal-btn:hover{border-color:var(--c-primary-mid);}
.canal-btn.active{background:var(--c-brand);border-color:var(--c-brand);color:var(--c-on-brand);}
.qi-preview{display:flex;flex-wrap:wrap;gap:10px;margin:14px 0;}
.qi-preview .qi-pill{flex:1;min-width:140px;background:var(--c-surface);border-radius:var(--r-md);padding:12px;text-align:center;box-shadow:0 2px 8px rgba(var(--shadow-rgb),0.08);}
.qi-preview .qi-pill .qv{font-size:var(--fs-h2);font-weight:700;color:var(--c-primary);}
.qi-preview .qi-pill .ql{font-size:var(--fs-note);color:var(--c-text-muted);font-weight:600;margin-top:2px;}
.qbar-wrap{background:#e6edf1;border-radius:var(--r-sm);height:8px;overflow:hidden;margin-top:4px;}
.qbar-fill{height:100%;border-radius:var(--r-sm);background:linear-gradient(90deg,var(--c-success),var(--c-primary-mid));}
.aurora-header{background:var(--c-brand);padding:18px 28px;display:flex;align-items:center;justify-content:space-between;border-radius:18px 18px 0 0;flex-shrink:0;}
.aurora-header h2{color:var(--c-on-brand);font-size:var(--fs-h3);font-weight:700;margin:0;}
.aurora-header .aurora-sub{color:var(--c-on-primary-soft);font-size:var(--fs-small);margin-top:2px;}
.aurora-close{background:rgba(255,255,255,0.15);border:none;color:var(--c-on-brand);border-radius:var(--r-sm);padding:6px 14px;font-size:var(--fs-small);cursor:pointer;transition:background 0.2s;}
.aurora-close:hover{background:rgba(255,255,255,0.28);}

/* Filter bar */
.aurora-filters{background:var(--c-surface);padding:14px 24px;display:flex;align-items:center;gap:14px;border-bottom:1px solid var(--c-border);flex-shrink:0;flex-wrap:wrap;}
.aurora-filters label{font-size:var(--fs-note);color:var(--c-text-2);font-weight:700;text-transform:uppercase;letter-spacing:0.05em;}
.aurora-filters select{padding:7px 12px;border:1.5px solid var(--c-border-control);border-radius:var(--r-sm);font-size:var(--fs-body);color:var(--c-primary);background:var(--c-surface-subtle);outline:none;cursor:pointer;}
.aurora-badge{background:var(--c-info-bg);color:var(--c-primary-mid);padding:3px 10px;border-radius:var(--r-pill);font-size:var(--fs-note);font-weight:700;}

/* KPI cards */
.aurora-kpis{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:12px;padding:20px 24px 0;}
/* Fase 136 (F02): #cal-kpis-strip (Modulo de Calidad) empieza vacio y se
   llena por fetch despues del primer pintado -- medido con
   PerformanceObserver/layout-shift: 108px de alto una vez poblado, 0 al
   abrir, CLS 0.67 (el mayor de toda la auditoria). min-height reserva
   ese espacio de antemano para que no empuje el resto del panel hacia
   abajo cuando llegan los datos. No toca #gd-kpis (dashboard de
   cliente): ORLANT y Mobilize hoy no usan esa franja (kpis:[], Fase 68)
   -- nada que reservar ahi todavia. */
#cal-kpis-strip{min-height:108px;}
.aurora-kpi{background:var(--c-surface);border-radius:var(--r-md);padding:16px 14px;box-shadow:var(--shadow-sm);border-left:3px solid var(--c-primary-mid);position:relative;overflow:hidden;}
.aurora-kpi .kpi-val{font-size:var(--fs-kpi);font-weight:700;color:var(--c-primary);line-height:1;}
.aurora-kpi .kpi-lbl{font-size:var(--fs-note);color:var(--c-text-muted);margin-top:5px;text-transform:uppercase;letter-spacing:0.05em;}
.aurora-kpi .kpi-sub{font-size:var(--fs-note);color:var(--c-text-2);margin-top:3px;}

/* Chart sections */
.aurora-charts{display:grid;grid-template-columns:1fr 1fr;gap:16px;padding:16px 24px;}
.aurora-chart-card{background:var(--c-surface);border-radius:12px;padding:20px;box-shadow:0 2px 10px rgba(var(--shadow-rgb),0.07);}
.aurora-chart-card.full{grid-column:1/-1;}
.aurora-chart-title{font-size:var(--fs-body);font-weight:700;color:var(--c-primary);margin-bottom:14px;}
.aurora-chart-wrap{position:relative;height:240px;}
.aurora-chart-wrap canvas{width:100%!important;height:100%!important;}

/* Data table */
.aurora-table-wrap{padding:0 24px 24px;}
.aurora-table-card{background:var(--c-surface);border-radius:12px;padding:20px;box-shadow:0 2px 10px rgba(var(--shadow-rgb),0.07);}
.aurora-table-title{font-size:var(--fs-body);font-weight:700;color:var(--c-primary);margin-bottom:14px;}
.aurora-table{width:100%;border-collapse:collapse;font-size:var(--fs-small);}
.aurora-table thead th{background:var(--c-brand);color:var(--c-on-brand);padding:9px 14px;text-align:center;font-weight:600;font-size:var(--fs-note);text-transform:uppercase;letter-spacing:0.04em;}
.aurora-table thead th:first-child{text-align:left;}
.aurora-table tbody tr:nth-child(even){background:var(--c-surface-subtle);}
.aurora-table tbody td{padding:9px 14px;color:var(--c-text);text-align:center;border-bottom:1px solid var(--c-bg);}
.aurora-table tbody td:first-child{text-align:left;font-weight:600;color:var(--c-primary);}
.aurora-table tbody tr.total-row{background:var(--c-info-bg)!important;}
.aurora-table tbody tr.total-row td{font-weight:700;color:var(--c-primary);}
.aurora-table .peak{color:var(--c-success-dark);font-weight:700;}
.aurora-table .low{color:var(--c-danger-dark);}

/* Variation cards */
.aurora-var-row{display:grid;grid-template-columns:repeat(5,1fr);gap:10px;padding:0 24px 24px;}
.aurora-var-card{background:var(--c-surface);border-radius:var(--r-md);padding:14px 10px;text-align:center;box-shadow:var(--shadow-sm);}
.aurora-var-card .vc-mes{font-size:var(--fs-note);font-weight:700;color:var(--c-primary);margin-bottom:6px;}
.aurora-var-card .vc-val{font-size:var(--fs-h2);font-weight:700;line-height:1;}
.aurora-var-card .vc-pct{font-size:var(--fs-small);margin-top:3px;}
.aurora-var-card .vc-sub{font-size:var(--fs-note);color:var(--c-text-muted);margin-top:2px;}
.aurora-var-card.pos{background:#f0fdf4;border:1.5px solid #bbf7d0;}
.aurora-var-card.neg{background:#fef2f2;border:1.5px solid #fca5a5;}
.aurora-var-card.pos .vc-val,.aurora-var-card.pos .vc-pct{color:#16a34a;}
.aurora-var-card.neg .vc-val,.aurora-var-card.neg .vc-pct{color:#dc2626;}

@media(max-width:700px){.aurora-charts{grid-template-columns:1fr;}.aurora-var-row{grid-template-columns:repeat(2,1fr);}}
@media(max-width:520px){.dash-grid{grid-template-columns:1fr;}.login-box{padding:36px 24px;}}
/* .form-row (constructor de dashboards, cargas, usuarios, umbrales, ...)
   es flex sin wrap -- con 4-5 campos en la misma fila (ej. Clave/Titulo/
   Cadencia/Periodo/Fila unica del constructor de dashboards) cada input
   queda de ~70px, ilegible en un telefono. Con wrap, cada campo respeta
   un ancho minimo y los que no entran bajan de linea en vez de
   comprimirse. Va DESPUES de ".form-row .ig{flex:1}" (arriba, sin media
   query) a proposito -- con la misma especificidad, la regla que gana es
   la que queda mas abajo en el archivo, sin importar el @media. */
@media(max-width:700px){
  .form-row{flex-wrap:wrap;}
  .form-row .ig{flex:1 1 150px;}
}

/* Fase 133 (hallazgo real de la Fase 132, mobile 412px, confirmado NO
   causado por la tipografia nueva -- ya pasaba con Segoe UI): .aurora-header
   (titulo + "Ultima actualizacion" + filtros + botones Exportar/Cerrar del
   dashboard de cliente) es flex SIN wrap -- con los filtros (MES, VISTA,
   COMPARAR) y los botones todos en la misma fila, en una pantalla angosta
   no hay espacio para las 3 lineas del bloque de titulo y el resto se monta
   encima. Con wrap, el grupo de filtros y el de botones bajan de linea en
   vez de comprimirse sobre el titulo; min-width:0 en el bloque de titulo
   deja que su texto haga su propio wrap de palabra en vez de forzar el
   ancho del flex item. */
@media(max-width:480px){
  .aurora-header{flex-wrap:wrap;row-gap:10px;}
  .aurora-header > div:first-child{min-width:0;}
  .aurora-header > div:first-child > div:first-child{min-width:0;}
}

/* Objetivos tactiles (Fase 133, Parte 5, WCAG 2.5.8/2.2 "target size
   minimum") -- escritorio >=24x24px (casi todo ya cumple, se deja
   explicito para no depender de que el padding de cada boton alcance por
   casualidad); movil (<=768px) >=44x44px en los controles PRINCIPALES
   nombrados en el prompt: cerrar/exportar del dashboard, el selector de
   MES, y las pestanas. Las sub-pestanas (.gd-subtab-btn) y los botones
   chicos de tabla (btn-sm) se quedan como estan -- son secundarios, muy
   numerosos en una fila, y agrandarlos a 44px rompe el layout de tabla. */
button,.btn-sm,.atab,.aurora-close,select,.theme-toggle,
.navbar-menu-toggle,.navbar-profile-toggle{min-height:24px;min-width:24px;}
@media(max-width:768px){
  .aurora-close,#gd-mes-sel,#gd-export-btn,.atab,
  .navbar-menu-toggle,.navbar-profile-toggle,.theme-toggle{
    min-height:44px;min-width:44px;
  }
  .atab{padding-top:14px;padding-bottom:14px;}
}

/* ═══════════════════════════════════════════════════════════
   COMPONENTES DE ESTADO REUTILIZABLES
   Un solo lugar para: "próximamente", cargando, vacío.
   Se usan igual en todas las pantallas para que el usuario
   reconozca siempre el mismo patrón visual.
   ═══════════════════════════════════════════════════════════ */

/* "Próximamente" — módulo/dashboard que existe en el menú pero aún no está construido */
.badge-soon{display:inline-block;background:var(--c-accent-bg);color:var(--c-warning-dark);
  border:1px solid var(--c-accent);border-radius:var(--r-pill);padding:2px 9px;
  font-size:var(--fs-note);font-weight:700;text-transform:uppercase;letter-spacing:0.04em;white-space:nowrap;}

/* "Método anterior" — sigue funcionando, ya no es el camino recomendado
   (ej. carga simple de Nivel de Servicio vs. la carga multi-skill de
   Trafico/Wolkvox). Tono neutro/apagado a propósito -- no es una alerta
   como .badge-soon, es solo una nota de prioridad. */
.badge-legacy{display:inline-block;background:var(--c-surface-alt);color:var(--c-text-muted);
  border:1px solid var(--c-border);border-radius:var(--r-pill);padding:2px 9px;
  font-size:var(--fs-note);font-weight:700;text-transform:uppercase;letter-spacing:0.04em;white-space:nowrap;}

/* Botón de módulo del dashboard marcado como "próximamente": atenuado pero aún clicable
   (al pulsarlo se explica que todavía no está disponible, en vez de no hacer nada). */
.dash-btn.mod-soon{opacity:0.55;}
.dash-btn.mod-soon:hover{transform:none;box-shadow:none;background:rgba(255,255,255,0.12);}
.dash-soon{position:absolute;top:12px;right:14px;background:var(--c-accent);color:var(--c-primary-darker);
  border-radius:var(--r-pill);padding:2px 9px;font-size:var(--fs-note);font-weight:700;
  text-transform:uppercase;letter-spacing:0.03em;}

/* Tarjeta de cliente marcada como "próximamente" (dashboard aún no construido) */
.client-card.client-soon{opacity:0.6;}
.client-card.client-soon:hover{border-color:var(--c-border);background:var(--c-surface-subtle);box-shadow:none;}
.client-card .client-soon-label{font-size:var(--fs-note);font-weight:700;text-transform:uppercase;
  letter-spacing:0.03em;color:var(--c-warning-dark);background:var(--c-accent-bg);
  border:1px solid var(--c-accent);border-radius:var(--r-pill);padding:2px 8px;}

/* Estado vacío — mensaje + siguiente paso, en vez de una tabla en blanco */
.empty-state{text-align:center;padding:var(--space-8) var(--space-5);color:var(--c-text-2);}
.empty-state .es-icon{font-size:var(--fs-display);opacity:0.5;margin-bottom:var(--space-2);}
.empty-state .es-title{font-size:var(--fs-body);font-weight:700;color:var(--c-text);margin-bottom:var(--space-1);}
.empty-state .es-hint{font-size:var(--fs-small);color:var(--c-text-muted);line-height:1.5;}

/* Cargando — spinner + texto, reutilizable dentro de tablas y tarjetas */
.loading-state{display:flex;align-items:center;justify-content:center;gap:var(--space-3);
  padding:var(--space-8) var(--space-5);color:var(--c-text-2);font-size:var(--fs-body);}
.spinner{width:18px;height:18px;border:2.5px solid var(--c-border);border-top-color:var(--c-primary-mid);
  border-radius:50%;animation:spin 0.7s linear infinite;flex-shrink:0;}
@keyframes spin{to{transform:rotate(360deg);}}

/* Botón en curso — el texto lo cambia el JS ("Guardando…"); aquí solo el aspecto deshabilitado */
.btn-primary[disabled],.btn-cancel[disabled],.btn-sm[disabled]{opacity:0.6;cursor:progress;}

/* ═══════════════════════════════════════════════════════════
   FOCO VISIBLE (Fase 133, Parte 5, WCAG 2.4.7/2.4.11) — va al
   final del archivo A PROPOSITO: varias reglas de arriba usan
   `outline:none` sin reemplazo (.ig input/select, .search-bar input,
   .hist-filter-sel, .aurora-filters select) -- :focus-visible aqui
   tiene la MISMA especificidad (0,1,0) que esas reglas de clase, asi
   que solo gana por venir despues en el archivo. Cubre cualquier
   elemento interactivo nativo (button/a/select/input) o con tabindex
   -- no hace falta listar cada clase de boton/pestaña/filtro a mano.
   ═══════════════════════════════════════════════════════════ */
a:focus-visible, button:focus-visible, input:focus-visible,
select:focus-visible, textarea:focus-visible, [tabindex]:focus-visible,
summary:focus-visible {
  outline: 2px solid var(--c-focus);
  outline-offset: 2px;
}
/* Sobre el navbar (fondo --c-brand, siempre oscuro, 2.31:1 contra
   --c-focus) el anillo pasa a --c-on-brand (blanco fijo, 11.22:1). */
.navbar *:focus-visible, .aurora-header *:focus-visible,
.theme-toggle-fab:focus-visible {
  outline-color: var(--c-on-brand);
}
/* Celdas/filas clicables (sin ser <button>/<a> reales) que ya usan
   tabindex="0" + onkeydown en el HTML -- mismo anillo, radio chico
   para no desbordar la celda. */
[role="button"]:focus-visible { outline: 2px solid var(--c-focus); outline-offset: 1px; border-radius: 3px; }

/* Movimiento reducido (WCAG 2.3.3) -- el unico @keyframes real de la
   plataforma es el spinner de carga (infinito); el resto son
   transiciones cortas de opacidad/color/borde, no movimiento grande.
   El snippet estandar (apagar duracion/iteracion de TODO) es inofensivo
   incluso para esas transiciones cortas -- simplemente se vuelven
   instantaneas en vez de animadas. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Fase 136 (PR 4): interruptor MANUAL de movimiento, aparte de
   prefers-reduced-motion (que ya cubre la preferencia del sistema
   operativo del usuario). Mismo mecanismo exacto, pero activable a mano
   -- ej. soporte/QA, o un apagado de emergencia si algo de movimiento
   falla en produccion -- con <html data-motion="off">. No reemplaza a
   prefers-reduced-motion, se suma: cualquiera de los 2 activos apaga el
   movimiento. */
html[data-motion="off"] *, html[data-motion="off"] *::before, html[data-motion="off"] *::after {
  animation-duration: 0.01ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0.01ms !important;
  scroll-behavior: auto !important;
}

/* Fase 136 (PR 5): feedback de prensado -- confirma que la interfaz
   "escuchó" el clic, sin esperar a que termine la accion (guardar,
   navegar). Acotado a los botones mas frecuentes (no .btn-reset/.btn-eye,
   acciones raras/iconos sueltos, para no animar por animar). Cada regla
   conserva la transicion que ya tenia (opacity/background) y le suma
   transform -- no la reemplaza por una generica "transition:all" (eso es
   justo lo que review-animations marca como hallazgo: anima propiedades
   sin querer y es mas caro para el navegador que nombrar las 2 que
   hacen falta). scale(0.97), no scale(0.9) -- sutil, el boton no "salta". */
.btn-primary, .btn-login, .btn-cancel, .btn-logout, .btn-sm, .btn-close-modal {
  transition: opacity var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}
.btn-primary:active, .btn-login:active, .btn-cancel:active, .btn-logout:active, .btn-sm:active, .btn-close-modal:active {
  transform: scale(0.97);
}
