/* ═══════════════════════════════════════════════════════════════════
   Crumb · Glass theme — lenguaje visual oficial (aprobado 2026-07-02)
   Origen: maqueta /{rut}/maqueta2 (cliente/maqueta/dashboard2.html),
   síntesis de referencias del usuario (glass estilo Apple actual).

   Principios:
   · lienzo greige cálido (#e9eae5) con brillos ambientales que derivan
   · tarjetas de vidrio esmerilado con tinte radial según el dato
   · Inter variable autoalojada: números enormes weight 200, títulos 600
   · acentos: verde #29bf12 · rojo #f4442e · azul #3c91e6 · lima #e3f380
   · tramas diagonales para series comparativas
   · coreografía de entrada (reveal escalonado) + microanimaciones

   Compatibilidad: reimplementa las clases públicas de admin/theme.css
   (.btn*, .form-*, .modal-*, .badge*, .banner, .filters, tablas,
   .empty-state) para que las páginas migren sin reescribir su JS.

   Modo ligero (POS / hardware modesto): <body class="glass-lite">
   desactiva backdrop-filter y brillos animados conservando el lenguaje.
   ═══════════════════════════════════════════════════════════════════ */

/* ── Inter variable autoalojada (el CSP bloquea Google Fonts) ─────── */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 200 700;
  font-display: swap;
  src: url("/static/fonts/inter-var-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 200 700;
  font-display: swap;
  src: url("/static/fonts/inter-var-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  /* Paleta glass */
  --ink: #272727;
  --ink-soft: #4a4a46;
  --muted: #82827c;
  --muted-2: #a6a69f;
  --green: #29bf12;
  --green-deep: #1f8c0e;
  --red: #f4442e;
  --red-deep: #cf2c18;
  --blue: #3c91e6;
  --blue-deep: #1f6cba;
  --lime: #e3f380;
  --lime-deep: #5a6414;
  /* FIX 2026-07-06 — ámbar de advertencia: la paleta usaba el lima como "amarillo"
     en banners/diálogos/semáforos, pero el lima es acento decorativo y no comunica
     alerta. Nuevo par para todos los estados de advertencia. */
  --amber: #f59e0b;
  --amber-deep: #7c4a03;
  --canvas: #e9eae5;
  --hairline: rgba(39, 39, 39, .08);
  --glass: rgba(255, 255, 255, .55);
  --glass-strong: rgba(255, 255, 255, .78);
  --glass-border: rgba(255, 255, 255, .65);

  /* Radios y sombras */
  --r-lg: 26px;
  --r-md: 18px;
  --r-sm: 12px;
  --r-pill: 999px;
  --shadow: 0 1px 2px rgba(35, 35, 30, .05), 0 14px 34px rgba(35, 35, 30, .06);
  --shadow-hover: 0 2px 4px rgba(35, 35, 30, .05), 0 22px 48px rgba(35, 35, 30, .10);
  --shadow-pop: 0 2px 6px rgba(35, 35, 30, .06), 0 26px 64px rgba(35, 35, 30, .18);

  /* Timing */
  --ease: cubic-bezier(.22, .61, .36, 1);
  --ease-spring: cubic-bezier(.34, 1.3, .5, 1);

  --font: "Inter", -apple-system, BlinkMacSystemFont, "Helvetica Neue", sans-serif;

  /* ── Aliases legacy (admin/theme.css) → equivalentes glass ────────
     Las páginas migradas pueden conservar referencias a estas variables
     en sus estilos inline sin romper la coherencia visual. */
  --color-bg: var(--glass);
  --color-bg-alt: rgba(39, 39, 39, .05);
  --color-text: var(--ink);
  --color-text-secondary: var(--muted);
  --color-text-tertiary: var(--muted-2);
  --color-border: rgba(39, 39, 39, .14);
  --color-border-light: var(--hairline);
  --color-accent: var(--blue-deep);
  --color-accent-hover: #185a9d;
  --color-success: var(--green-deep);
  --color-error: var(--red-deep);
  --color-focus-ring: rgba(60, 145, 230, .45);
  --color-etapa-pendiente-bg: rgba(244, 68, 46, .12);
  --color-etapa-pendiente-fg: var(--red-deep);
  --color-etapa-proceso-bg: rgba(60, 145, 230, .13);
  --color-etapa-proceso-fg: var(--blue-deep);
  --color-etapa-produccion-bg: rgba(41, 191, 18, .13);
  --color-etapa-produccion-fg: var(--green-deep);
  --font-stack: var(--font);
  --text-hero: clamp(38px, 4.6vw, 58px);
  --text-title: clamp(28px, 3vw, 38px);
  --text-heading: 22px;
  --text-subheading: 17px;
  --text-body-lg: 16px;
  --text-body: 14px;
  --text-caption: 13px;
  --text-small: 11.5px;
  --radius-sm: var(--r-sm);
  --radius-md: var(--r-md);
  --radius-lg: var(--r-lg);
  --radius-pill: var(--r-pill);
  --shadow-card: var(--shadow);
  --shadow-card-hover: var(--shadow-hover);
}

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

html, body {
  background: var(--canvas);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--text-body);
  line-height: 1.5;
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
/* Sticky footer: el body es columna flex; main se estira y el pie (.gfoot,
   inyectado a nivel de body) queda anclado abajo aunque haya poco contenido. */
body { overflow-x: hidden; display: flex; flex-direction: column; }

a { color: var(--blue-deep); text-decoration: none; transition: color .2s var(--ease); }
a:hover { color: var(--color-accent-hover); }

/* ── Brillos ambientales (los inyecta GlassUI.init) ───────────────── */
.ambient { position: fixed; inset: -20%; z-index: -1; pointer-events: none; filter: blur(60px); }
.ambient i { position: absolute; border-radius: 50%; opacity: 0; transition: opacity 1.8s ease; }
.ambient.on i { opacity: 1; }
.ambient .a1 { width: 55vw; height: 55vw; top: -18%; right: -12%; background: radial-gradient(circle, rgba(41, 191, 18, .10), transparent 65%); animation: drift1 46s ease-in-out infinite alternate; }
.ambient .a2 { width: 48vw; height: 48vw; top: 32%; left: -16%; background: radial-gradient(circle, rgba(60, 145, 230, .09), transparent 65%); animation: drift2 58s ease-in-out infinite alternate; }
.ambient .a3 { width: 40vw; height: 40vw; bottom: -22%; right: 18%; background: radial-gradient(circle, rgba(244, 68, 46, .06), transparent 65%); animation: drift3 52s ease-in-out infinite alternate; }
@keyframes drift1 { to { transform: translate(-9vw, 7vh); } }
@keyframes drift2 { to { transform: translate(7vw, -6vh); } }
@keyframes drift3 { to { transform: translate(-6vw, -8vh); } }

/* ── Layout ────────────────────────────────────────────────────────── */
main, .page, .page-wide { max-width: 1180px; margin: 0 auto; padding: 20px 28px 48px; width: 100%; flex: 1 0 auto; }
.page { max-width: 980px; }

/* ── Barra superior glass (la construye GlassUI.topbar) ───────────── */
.gtop { display: flex; align-items: center; gap: 14px; padding: 10px 0 26px; }
.brand { display: flex; align-items: center; gap: 9px; color: var(--ink); }
/* Logo oficial: /static/logocrumbpos.svg (wordmark tinta, viewBox 138.95x36.74).
   34px = ajuste "+30%" pedido por el usuario (2026-07-02). */
/* FIX 2026-07-15 — logo Nanuc +30% en todo el sitio (pedido del usuario). */
.brand img { height: 44px; width: auto; display: block; }

.navpills {
  position: relative; display: flex; gap: 2px;
  background: var(--glass);
  backdrop-filter: blur(20px) saturate(1.4);
  -webkit-backdrop-filter: blur(20px) saturate(1.4);
  border: 1px solid var(--glass-border);
  border-radius: var(--r-pill);
  padding: 5px; box-shadow: var(--shadow); margin: 0 auto;
}
.navpills a {
  position: relative; z-index: 1;
  font-size: 13.5px; font-weight: 500; color: var(--muted);
  padding: 9px 17px; border-radius: var(--r-pill);
  text-decoration: none; cursor: pointer; white-space: nowrap;
  transition: color .35s var(--ease);
}
.navpills a:hover { color: var(--ink); }
.navpills a.active { color: #fff; }
.navpills a.soon { color: var(--muted-2); cursor: default; }
.navpills .thumb {
  position: absolute; top: 5px; bottom: 5px; z-index: 0;
  background: var(--ink); border-radius: var(--r-pill);
  transition: left .45s var(--ease-spring), width .45s var(--ease-spring);
}

.icirc {
  width: 42px; height: 42px; border-radius: 50%; flex: none;
  background: var(--glass); border: 1px solid var(--glass-border);
  backdrop-filter: blur(20px) saturate(1.4);
  -webkit-backdrop-filter: blur(20px) saturate(1.4);
  display: grid; place-items: center; cursor: pointer; position: relative;
  box-shadow: var(--shadow);
  transition: transform .3s var(--ease), box-shadow .3s var(--ease);
}
.icirc:hover { transform: translateY(-2px); box-shadow: var(--shadow-hover); }
.icirc:active { transform: scale(.94); }
.icirc svg { width: 18px; height: 18px; stroke: var(--ink); fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.icirc .dot { position: absolute; top: 10px; right: 11px; width: 7px; height: 7px; border-radius: 50%; background: var(--red); border: 1.5px solid #fff; transition: opacity .4s var(--ease); }
.avatar {
  width: 42px; height: 42px; border-radius: 50%; flex: none;
  background: var(--ink); color: var(--lime);
  display: grid; place-items: center;
  font-size: 14px; font-weight: 700; letter-spacing: .02em;
  box-shadow: var(--shadow); cursor: pointer;
  transition: transform .3s var(--ease);
}
.avatar:hover { transform: translateY(-2px); }

/* Volver: círculo con chevron a la izquierda del brand en subpáginas.
   FIX 2026-07-06 — pedido del usuario: 20% más chico que el .icirc
   estándar (42px → 34px; el chevron baja proporcional 16px → 13px). */
.gback { text-decoration: none; width: 34px; height: 34px; }
.gback svg { width: 13px; height: 13px; }

/* ── Panel lateral flotante (PRUEBA 2026-07-22, referencia del usuario) ──
   Disposición alternativa a las píldoras superiores: los módulos viven en
   un panel izquierdo FLOTANTE — tarjeta glass con puntas redondeadas,
   separada de los bordes del viewport (no un rectángulo blanco pegado al
   costado) — con botón para plegarlo a un riel de solo iconos (estado
   persistido en localStorage). Solo desktop (>1020px): en móvil sigue el
   menú hamburguesa de siempre. Lo construye GlassUI.sidebar(); el body
   recibe .gside-on (+ .gside-min al plegar). Con el panel activo, la
   barra superior conserva solo volver + campana + avatar (el brand y las
   píldoras se ocultan en desktop). */
:root { --gside-w: 244px; --gside-w-min: 76px; --gside-gap: 14px; }
.gside {
  position: fixed; left: var(--gside-gap); top: var(--gside-gap); bottom: var(--gside-gap);
  width: var(--gside-w); z-index: 50;
  display: flex; flex-direction: column;
  background: var(--glass-strong);
  backdrop-filter: blur(24px) saturate(1.4);
  -webkit-backdrop-filter: blur(24px) saturate(1.4);
  border: 1px solid var(--glass-border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow);
  padding: 18px 12px 14px;
  overflow: hidden;
  transition: width .45s var(--ease-spring);
}
body.gside-min .gside { width: var(--gside-w-min); }
/* Brand con crossfade (FIX 2026-07-22, pedido del usuario): el wordmark y
   el isotipo viven superpuestos dentro del mismo <a>; al plegar, el ancho
   del contenedor se contrae con el panel y las opacidades se CRUZAN en
   sincronía (fade out del wordmark, fade in del iso) — cambio sutil, no
   un salto de un logo al otro. */
/* FIX 2026-07-22 — pedido del usuario (2ª iteración): el wordmark
   desplegado se JUSTIFICA con los textos de los ítems del menú (mismo
   borde izquierdo que "Dashboard", "Facturación"…). Ese borde queda a
   44px del padding del panel (2px nav + 11px padding del ítem + 19px de
   icono + 12px de gap). Al plegar, el padding vuelve a 0 y el contenedor
   se centra (transición incluida para que no salte). */
.gside-brand {
  display: flex; align-items: center; justify-content: flex-start;
  padding: 2px 6px 16px 44px;
  transition: padding .45s var(--ease-spring);
}
body.gside-min .gside-brand { justify-content: center; padding: 2px 0 16px; }
.gside-brand .brand {
  position: relative; display: block; overflow: hidden;
  width: 158px; height: 52px;
  transition: width .45s var(--ease-spring);
}
.gside-brand .gword {
  position: absolute; left: 0; top: 50%; transform: translateY(-50%);
  height: 48px; width: auto; opacity: 1; transition: opacity .32s var(--ease);
}
.gside-brand .giso {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  height: 42px; width: auto; opacity: 0; transition: opacity .32s var(--ease);
}
body.gside-min .gside-brand .brand { width: 40px; }
body.gside-min .gside-brand .gword { opacity: 0; }
body.gside-min .gside-brand .giso { opacity: 1; }
/* El botón de plegado se ancla al panel en absoluto: así su viaje desde la
   esquina superior derecha (desplegado) hasta bajo el isotipo (plegado)
   también se anima, en vez de saltar de un layout a otro. */
.gside-toggle {
  position: absolute; top: 28px; right: 14px;
  width: 30px; height: 30px; border-radius: 50%;
  border: 1px solid var(--hairline); background: rgba(255, 255, 255, .7);
  display: grid; place-items: center; cursor: pointer;
  transition: top .45s var(--ease-spring), right .45s var(--ease-spring),
              transform .3s var(--ease), box-shadow .3s var(--ease);
}
.gside-toggle:hover { box-shadow: var(--shadow); transform: translateY(-1px); }
.gside-toggle:active { transform: scale(.92); }
.gside-toggle svg {
  width: 14px; height: 14px; stroke: var(--ink); fill: none;
  stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
  transition: transform .45s var(--ease-spring);
}
body.gside-min .gside-toggle { top: 78px; right: 23px; }
body.gside-min .gside-toggle svg { transform: rotate(180deg); }
.gside-nav {
  flex: 1; display: flex; flex-direction: column; gap: 3px;
  overflow-y: auto; overflow-x: hidden; padding: 2px;
  transition: padding-top .45s var(--ease-spring);
}
body.gside-min .gside-nav { padding-top: 40px; }
/* NUEVO 2026-07-23 — HOVER-PEEK (pedido del usuario): con el panel PLEGADO,
   pasar el mouse lo despliega temporalmente como OVERLAY (el contenido de
   la página no se mueve: .gside es fixed y flota encima con sombra pop) y
   al salir se repliega solo. Si el usuario lo dejó desplegado, no aplica.
   GlassUI pone/quita body.gside-peek en mouseenter/mouseleave. */
body.gside-min.gside-peek .gside { width: var(--gside-w); box-shadow: var(--shadow-pop); }
body.gside-min.gside-peek .gside-brand { justify-content: flex-start; padding: 2px 6px 16px 44px; }
body.gside-min.gside-peek .gside-brand .brand { width: 158px; }
body.gside-min.gside-peek .gside-brand .gword { opacity: 1; }
body.gside-min.gside-peek .gside-brand .giso { opacity: 0; }
body.gside-min.gside-peek .gside-toggle { top: 28px; right: 14px; }
body.gside-min.gside-peek .gside-nav { padding-top: 2px; }
/* FIX 2026-07-23 (2ª iteración del peek) — faltaba revertir las reglas que
   ocultan las ETIQUETAS y centran los ítems al plegar (el panel crecía
   pero seguía viéndose plegado: solo iconos e isotipo). */
body.gside-min.gside-peek .gside .sitem { justify-content: flex-start; padding: 10px 11px; }
body.gside-min.gside-peek .gside .slabel,
body.gside-min.gside-peek .gside .soon-tag { display: block; }
.gside .sitem {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 11px; border-radius: 14px;
  font-size: 13.5px; font-weight: 500; color: var(--muted);
  white-space: nowrap; cursor: pointer; text-decoration: none;
  transition: background .25s var(--ease), color .25s var(--ease);
}
.gside .sitem svg { width: 19px; height: 19px; flex: none; stroke: currentColor; fill: none; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.gside .sitem:hover { color: var(--ink); background: rgba(39, 39, 39, .05); }
.gside .sitem.active { background: var(--ink); color: #fff; }
.gside .sitem.active:hover { color: #fff; }
.gside .sitem.soon { color: var(--muted-2); cursor: default; }
.gside .sitem.soon:hover { background: none; color: var(--muted-2); }
.gside .sitem .slabel { overflow: hidden; text-overflow: ellipsis; }
.gside .sitem .soon-tag {
  margin-left: auto; flex: none; font-size: 9.5px; font-weight: 600; color: var(--muted-2);
  background: rgba(39, 39, 39, .06); padding: 2px 7px; border-radius: var(--r-pill);
}
body.gside-min .gside .sitem { justify-content: center; padding: 11px 0; }
body.gside-min .gside .slabel, body.gside-min .gside .soon-tag { display: none; }

/* Con el panel activo, el contenido se corre a la derecha del panel. */
body.gside-on { padding-left: calc(var(--gside-w) + var(--gside-gap) * 2); transition: padding-left .45s var(--ease-spring); }
body.gside-on.gside-min { padding-left: calc(var(--gside-w-min) + var(--gside-gap) * 2); }
body.gside-on .gtop .brand, body.gside-on .gtop .navpills { display: none; }
body.gside-on .gtop { justify-content: flex-end; }
body.gside-on .gtop .gback { margin-right: auto; }
@media (max-width: 1020px) {
  .gside { display: none; }
  body.gside-on { padding-left: 0; }
  body.gside-on .gtop { justify-content: flex-start; }
  body.gside-on .gtop .brand { display: flex; }
}

/* ── Encabezado de página ──────────────────────────────────────────── */
.page-head { display: flex; justify-content: space-between; align-items: flex-end; gap: 32px; margin: 8px 0 30px; flex-wrap: wrap; }
.eyebrow, .page-eyebrow { font-size: 13px; font-weight: 500; color: var(--muted); margin-bottom: 12px; letter-spacing: .01em; text-transform: none; }
h1.title, .page-title, .page-header h1 {
  font-size: clamp(30px, 3.6vw, 44px);
  font-weight: 300; letter-spacing: -.03em; line-height: 1.08;
  color: var(--ink); text-wrap: balance; margin-bottom: 0;
}
h1.title-hero { font-size: clamp(38px, 4.6vw, 58px); }
.page-subtitle, .page-header .subtitle, .page-header p {
  margin-top: 10px; font-size: 14.5px; color: var(--muted); max-width: 640px;
}
.page-actions { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; padding-bottom: 6px; }
/* Regla de UX (2026-07-02): en desktop el bloque del título (saludo incluido)
   no pasa del 50% del ancho — un saludo largo envuelve en vez de estirarse. */
@media (min-width: 1021px) {
  .page-head > :first-child { max-width: 50%; }
}

/* ── Tarjetas de vidrio ────────────────────────────────────────────── */
.card {
  position: relative;
  background: var(--glass);
  border: 1px solid var(--glass-border);
  border-radius: var(--r-lg);
  backdrop-filter: blur(26px) saturate(1.5);
  -webkit-backdrop-filter: blur(26px) saturate(1.5);
  box-shadow: var(--shadow);
  padding: 24px;
  overflow: hidden;
  transition: transform .4s var(--ease), box-shadow .4s var(--ease);
}
.card.lift:hover, .card-hover:hover { transform: translateY(-3px); box-shadow: var(--shadow-hover); }
.card-hover { cursor: pointer; }
.card::before { content: ""; position: absolute; inset: 0; pointer-events: none; border-radius: inherit; }
/* FIX 2026-07-02 — tintes radiales reforzados a pedido del usuario (los
   originales de la maqueta se percibían muy tenues en pantalla real). */
.card.tint-green::before { background: radial-gradient(70% 60% at 78% 85%, rgba(41, 191, 18, .16), transparent 72%); }
.card.tint-red::before { background: radial-gradient(70% 60% at 80% 15%, rgba(244, 68, 46, .13), transparent 72%); }
.card.tint-blue::before { background: radial-gradient(70% 60% at 20% 85%, rgba(60, 145, 230, .14), transparent 72%); }
/* FIX 2026-07-06 — tinte lima quedó demasiado intenso vs los otros tintes → revertido al valor original de la maqueta.
   Historial: en el refuerzo del 2026-07-02 el lima se subió de rgba(227,243,128,.35) a rgba(217,238,90,.60).
   Causa raíz: ese refuerzo lo dejó mucho más opaco/saturado que green/red/blue (.13–.16); se nota sobre todo en
   la tarjeta "Emitir documento" de facturación. Solución: volver al color/opacidad originales (el .35 es mayor en
   número porque el lima es un color muy claro; ese era el balance visual original). */
.card.tint-lime::before { background: radial-gradient(70% 60% at 80% 20%, rgba(227, 243, 128, .35), transparent 70%); }
/* FIX 2026-07-06 — faltaba un tinte de advertencia real en la paleta glass.
   Historial: el KPI "Bajo stock ideal" de insumos usaba tint-lime como sustituto del amarillo de la espec,
   pero el lima es un acento decorativo (mismo de "Emitir documento") y no comunica alerta.
   Causa raíz: la paleta glass no tenía ámbar/naranjo; se reutilizó el color más parecido.
   Solución: nuevo tint-amber al mismo nivel de intensidad que green/red/blue para estados de advertencia. */
.card.tint-amber::before { background: radial-gradient(70% 60% at 80% 20%, rgba(245, 158, 11, .15), transparent 72%); }
.card > * { position: relative; }
.card-disabled { opacity: .55; cursor: default; }
.card-disabled:hover { transform: none; box-shadow: var(--shadow); }

.chead { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; margin-bottom: 4px; }
.ctitle, .card-title { font-size: 16px; font-weight: 600; letter-spacing: -.01em; color: var(--ink); margin-bottom: 0; }
.csub, .card-desc { font-size: 12.5px; color: var(--muted); margin-top: 3px; font-weight: 400; line-height: 1.45; }
.card-footer { margin-top: 20px; color: var(--blue-deep); font-size: 13px; font-weight: 500; }
.clink {
  font-size: 12.5px; font-weight: 600; color: var(--ink);
  background: var(--glass-strong); border: 1px solid var(--glass-border);
  border-radius: var(--r-pill); padding: 7px 14px; cursor: pointer;
  display: inline-flex; align-items: center; gap: 6px;
  transition: transform .3s var(--ease), box-shadow .3s var(--ease);
  text-decoration: none;
}
.clink:hover { transform: translateY(-1px); box-shadow: var(--shadow); color: var(--ink); }
.clink svg { width: 12px; height: 12px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; }

/* Números protagonistas */
.bignum { font-size: clamp(44px, 4.4vw, 62px); font-weight: 200; letter-spacing: -.03em; line-height: 1; font-variant-numeric: tabular-nums; display: flex; align-items: baseline; gap: 6px; }
.bignum .unit { font-size: .45em; font-weight: 300; color: var(--ink-soft); }
.midnum { font-size: clamp(34px, 3vw, 42px); font-weight: 200; letter-spacing: -.025em; line-height: 1; font-variant-numeric: tabular-nums; }
/* NUEVO 2026-07-15 — pedido del usuario: en los dashboards el número del
   KPI va SIEMPRE anclado al borde inferior de la caja, para que los
   números queden alineados entre sí aunque los títulos/subtítulos ocupen
   1 o 2 líneas (los items de una grid comparten altura por defecto). */
.card.kpi { display: flex; flex-direction: column; }
.card.kpi .midnum, .card.kpi .bignum { margin-top: auto; padding-top: 14px; }
.delta { display: inline-flex; align-items: center; gap: 5px; font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums; }
.delta .tri { width: 0; height: 0; border-left: 5px solid transparent; border-right: 5px solid transparent; }
.delta.up { color: var(--green-deep); }
.delta.up .tri { border-bottom: 7px solid var(--green); }
.delta.down { color: var(--red); }
.delta.down .tri { border-top: 7px solid var(--red); }

/* ── Rejillas ──────────────────────────────────────────────────────── */
.grid { display: grid; gap: 16px; margin-bottom: 16px; }
.options-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
@media (max-width: 900px) { .options-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 600px) { .options-grid { grid-template-columns: 1fr; } }

/* ── Botones ───────────────────────────────────────────────────────── */
.btn, .qbtn {
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  font-family: inherit; font-size: 13.5px; font-weight: 500;
  padding: 12px 20px; border-radius: var(--r-pill); cursor: pointer;
  background: var(--glass); color: var(--ink);
  border: 1px solid var(--glass-border);
  backdrop-filter: blur(20px) saturate(1.4);
  -webkit-backdrop-filter: blur(20px) saturate(1.4);
  box-shadow: var(--shadow);
  transition: transform .3s var(--ease), box-shadow .3s var(--ease), background .3s var(--ease);
  white-space: nowrap; text-decoration: none;
}
.btn:hover, .qbtn:hover { transform: translateY(-2px); box-shadow: var(--shadow-hover); color: var(--ink); }
.btn:active, .qbtn:active { transform: scale(.96); }
.btn svg, .qbtn svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.btn-primary, .qbtn.dark { background: var(--ink); color: #fff; border-color: transparent; position: relative; overflow: hidden; }
.btn-primary:hover, .qbtn.dark:hover { color: #fff; }
.btn-primary svg, .qbtn.dark svg { stroke: var(--lime); }
/* NUEVO 2026-07-02 — destello al hover en los botones oscuros.
   FIX 2026-07-02 — ajuste del usuario: el fondo NO se aclara al hover
   (antes pasaba a #3a3a36) y el barrido se repite mientras dure el hover,
   a la misma velocidad del shimmer de los skeleton (1.8s ease-in-out
   infinite). Antes era un barrido único que costaba percibir.
   FIX 2026-07-07 — banda 30% más ancha (37%→63%, antes 40%→60%): en las
   cápsulas chicas del menú se veía muy delgada (pedido del usuario). */
.btn-primary::after, .qbtn.dark::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  /* FIX 2026-07-07 (iteración 3) — núcleo en MESETA (brillo pleno
     sostenido 45%→55%), no un pico puntual: el pico se leía delgado. */
  background: linear-gradient(105deg,
    transparent 35%, rgba(255, 255, 255, .16) 41%,
    rgba(255, 255, 255, .45) 45%, rgba(255, 255, 255, .45) 55%,
    rgba(255, 255, 255, .16) 59%, transparent 65%);
  /* FIX 2026-07-02 (iteración final con el usuario) — aparición instantánea
     + barrido rápido y fluido con ease-in-out ("easy ease"):
     · reposo: banda totalmente oculta (-130%);
     · al hover el keyframe inicial la pone tocando el borde (-55%) → asoma
       en los primeros frames aunque la curva arranque suave;
     · 0.9s por barrido, en loop mientras dure el hover. */
  transform: translateX(-130%);
}
.btn-primary:not(:disabled):hover::after, .qbtn.dark:not(:disabled):hover::after {
  animation: btn-shine 1.7s ease-in-out infinite;
}
/* FIX 2026-07-07 — pausa entre barridos (pedido del usuario: "se repite
   muy rápido"). El ciclo pasa de 0.9s continuo a 1.7s donde el barrido
   ocupa el 53% (≈0.9s, misma velocidad y arranque instantáneo de
   siempre) y el resto es descanso con la banda fuera de vista. El
   ease-in-out aplica por segmento → el barrido conserva su curva. */
@keyframes btn-shine {
  0% { transform: translateX(-55%); }
  53% { transform: translateX(130%); }
  100% { transform: translateX(130%); }
}
.btn-secondary { /* hereda el glass por defecto */ }
.btn-danger { color: var(--red-deep); }
.btn-danger:hover { color: var(--red-deep); background: rgba(244, 68, 46, .10); }
.btn-ghost { background: transparent; border-color: transparent; box-shadow: none; color: var(--muted); }
.btn-ghost:hover { color: var(--ink); box-shadow: none; transform: none; }
.btn-small { padding: 8px 15px; font-size: 12.5px; }
.btn:disabled, .qbtn:disabled { opacity: .45; cursor: not-allowed; transform: none; box-shadow: var(--shadow); }
.qbtn.soon { color: var(--muted-2); }
.qbtn.soon:hover { transform: none; box-shadow: var(--shadow); }

/* Microanimaciones de iconos en botones */
.qbtn svg, .btn svg, .clink svg, .icirc svg { transition: transform .45s var(--ease-spring); }
.qbtn:not(.soon):hover .ic-plus, .btn:not(:disabled):hover .ic-plus { transform: rotate(90deg); }
.qbtn:not(.soon):hover .ic-box { transform: translateY(-2px) rotate(-8deg); }
.clink:hover svg { transform: translate(2px, -2px); }
.icirc:hover .ic-bell { transform-origin: 50% 12%; animation: bell .8s var(--ease); }
@keyframes bell {
  0% { transform: rotate(0); } 25% { transform: rotate(13deg); }
  50% { transform: rotate(-10deg); } 74% { transform: rotate(5deg); } 100% { transform: rotate(0); }
}

/* ── Popovers ──────────────────────────────────────────────────────── */
.pop-wrap { position: relative; }
.pop {
  position: absolute; top: calc(100% + 12px); right: 0; z-index: 60;
  width: max-content; min-width: 296px; max-width: min(360px, calc(100vw - 40px));
  background: var(--glass-strong);
  backdrop-filter: blur(32px) saturate(1.6);
  -webkit-backdrop-filter: blur(32px) saturate(1.6);
  border: 1px solid var(--glass-border);
  border-radius: 20px;
  box-shadow: var(--shadow-pop);
  padding: 8px;
  opacity: 0; transform: translateY(-10px) scale(.93); transform-origin: top right;
  pointer-events: none;
  transition: opacity .28s var(--ease), transform .42s var(--ease-spring);
}
.pop.open { opacity: 1; transform: none; pointer-events: auto; }
.pop .stag { opacity: 0; transform: translateY(-7px); transition: opacity .35s var(--ease), transform .4s var(--ease); }
.pop.open .stag { opacity: 1; transform: none; }
.pop.open .stag:nth-child(2) { transition-delay: 40ms; }
.pop.open .stag:nth-child(3) { transition-delay: 80ms; }
.pop.open .stag:nth-child(4) { transition-delay: 120ms; }
.pop.open .stag:nth-child(5) { transition-delay: 160ms; }
.pop.open .stag:nth-child(6) { transition-delay: 200ms; }
.pop.open .stag:nth-child(7) { transition-delay: 240ms; }
.pop-head { display: flex; justify-content: space-between; align-items: center; padding: 10px 12px 8px; }
.pop-title { font-size: 14px; font-weight: 600; }
.pop-action { font: inherit; font-size: 12px; font-weight: 600; color: var(--blue-deep); background: none; border: 0; padding: 0; cursor: pointer; }
.pop-action:hover { text-decoration: underline; }
.pop-sep { height: 1px; background: var(--hairline); margin: 6px 10px; }
.pop-user { padding: 12px 12px 10px; }
.pop-user b { display: block; font-size: 14px; font-weight: 600; }
.pop-user span { font-size: 12px; color: var(--muted); }
.rol-chip { display: inline-block; margin-top: 8px; font-size: 11px; font-weight: 600; background: rgba(39, 39, 39, .07); color: var(--ink-soft); padding: 3px 10px; border-radius: var(--r-pill); }
/* Variante inline: el rol acompaña al nombre, discreto (pedido 2026-07-02) */
.rol-chip.rol-inline { margin: 0 0 0 8px; font-size: 10px; padding: 2px 8px; vertical-align: 1px; color: var(--muted); }
.pop-empty { padding: 26px 18px; text-align: center; font-size: 13px; color: var(--muted); }

.notif { display: flex; gap: 12px; align-items: flex-start; padding: 11px 12px; border-radius: 14px; cursor: pointer; transition: background .25s var(--ease); }
.notif:hover { background: rgba(255, 255, 255, .72); }
.notif .nicon { width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center; flex: none; }
.notif .nicon svg { width: 16px; height: 16px; fill: none; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.notif .nicon.g { background: rgba(41, 191, 18, .13); } .notif .nicon.g svg { stroke: var(--green-deep); }
.notif .nicon.r { background: rgba(244, 68, 46, .12); } .notif .nicon.r svg { stroke: var(--red-deep); }
.notif .nicon.b { background: rgba(60, 145, 230, .13); } .notif .nicon.b svg { stroke: var(--blue-deep); }
.notif .nicon.n { background: rgba(39, 39, 39, .07); } .notif .nicon.n svg { stroke: var(--muted); }
.notif .ntxt { flex: 1; min-width: 0; }
.notif .ntitle { font-size: 13px; font-weight: 600; line-height: 1.35; display: block; }
.notif .nsub { font-size: 12px; color: var(--muted); margin-top: 2px; display: block; }
.notif .ntime { font-size: 11px; color: var(--muted-2); white-space: nowrap; padding-top: 2px; }
.notif .undot { width: 7px; height: 7px; border-radius: 50%; background: var(--blue); margin-top: 6px; flex: none; }
.notif.read .undot { visibility: hidden; }

.mitem { display: flex; align-items: center; gap: 12px; padding: 11px 12px; border-radius: 14px; font-size: 13.5px; font-weight: 500; color: var(--ink); cursor: pointer; text-decoration: none; transition: background .25s var(--ease); }
.mitem:hover { background: rgba(255, 255, 255, .72); color: var(--ink); }
.mitem svg { width: 17px; height: 17px; flex: none; stroke: var(--muted); fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.mitem small { display: block; font-size: 11.5px; font-weight: 400; color: var(--muted); margin-top: 1px; }
.mitem.danger { color: var(--red-deep); }
.mitem.danger svg { stroke: var(--red-deep); }
.mitem.on { background: rgba(39, 39, 39, .05); font-weight: 600; }
.mitem .soon-tag { margin-left: auto; font-size: 10.5px; font-weight: 600; color: var(--muted-2); background: rgba(39, 39, 39, .06); padding: 2px 8px; border-radius: var(--r-pill); }
.only-mobile { display: none; }

/* ── Toggle segmentado ─────────────────────────────────────────────── */
.toggle { display: flex; position: relative; background: rgba(39, 39, 39, .06); border-radius: var(--r-pill); padding: 3px; }
.toggle button { position: relative; z-index: 1; font: inherit; font-size: 12px; font-weight: 500; color: var(--muted); background: none; border: 0; padding: 6px 14px; border-radius: var(--r-pill); cursor: pointer; transition: color .3s var(--ease); }
.toggle button.on { color: var(--ink); }
.toggle .tthumb { position: absolute; top: 3px; bottom: 3px; z-index: 0; background: #fff; border-radius: var(--r-pill); box-shadow: 0 2px 8px rgba(35, 35, 30, .10); transition: left .4s var(--ease-spring), width .4s var(--ease-spring); }

/* ── Formularios (contrato admin/theme.css, re-estilizado) ────────── */
.form-card {
  background: var(--glass);
  border: 1px solid var(--glass-border);
  border-radius: var(--r-lg);
  backdrop-filter: blur(26px) saturate(1.5);
  -webkit-backdrop-filter: blur(26px) saturate(1.5);
  box-shadow: var(--shadow);
  padding: 28px;
  margin-bottom: 16px;
}
.form-card-title { font-size: 16px; font-weight: 600; color: var(--ink); margin-bottom: 4px; }
.form-card-desc { font-size: 12.5px; color: var(--muted); margin-bottom: 22px; line-height: 1.45; }
.form-row { margin-bottom: 14px; }
/* FIX 2026-07-23 — pedido del usuario: los botones de acción quedaban
   pegados al campo que los precede (login y varias secciones). Aire
   estándar entre el último elemento de formulario y el botón que sigue. */
.form-row + .btn, .form-row + .btn-primary, form > .btn:last-child { margin-top: 28px; }
.form-row:last-of-type { margin-bottom: 0; }
.form-row.full { grid-column: 1 / -1; }
.form-label, .field label { display: block; font-size: 12.5px; color: var(--muted); font-weight: 500; margin-bottom: 6px; }
.form-input, .form-select, .field input, .field select, .field textarea, textarea.form-input {
  width: 100%; font: inherit; font-size: 14px; color: var(--ink);
  padding: 11px 14px;
  background: rgba(255, 255, 255, .72);
  border: 1px solid var(--hairline);
  border-radius: 14px;
  outline: none;
  transition: border-color .25s var(--ease), box-shadow .25s var(--ease), background .25s var(--ease);
}
.form-input:hover, .form-select:hover { background: rgba(255, 255, 255, .85); }
.form-input:focus, .form-select:focus, .field input:focus, .field select:focus, .field textarea:focus {
  border-color: rgba(60, 145, 230, .55);
  box-shadow: 0 0 0 3px var(--color-focus-ring);
  background: #fff;
}
.form-input.error, .form-select.error { border-color: var(--red); }
.form-error { font-size: 11.5px; color: var(--red-deep); margin-top: 5px; display: none; }
.form-error.visible { display: block; }
.form-hint, .field .hint { font-size: 11.5px; color: var(--muted-2); margin-top: 5px; }
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.form-grid .full { grid-column: 1 / -1; }
@media (max-width: 480px) { .form-grid { grid-template-columns: 1fr; } }
.field { display: flex; flex-direction: column; }

/* Filtros */
.filters { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 20px; align-items: center; }
.filter-select, .filter-input {
  padding: 8px 13px; height: 36px;
  border: 1px solid var(--hairline); border-radius: var(--r-pill);
  font-size: 12.5px; font-family: inherit; color: var(--ink);
  background: rgba(255, 255, 255, .72);
  transition: border-color .25s var(--ease), box-shadow .25s var(--ease);
}
.filter-select:focus, .filter-input:focus { outline: none; border-color: rgba(60, 145, 230, .55); box-shadow: 0 0 0 3px var(--color-focus-ring); }
/* Buscador con lupa (NUEVO 2026-07-06, pedido del usuario): envolver el
   .filter-input en .filter-search y anteponer el svg de lupa. La lupa es
   del INPUT de una lista — no confundir con la decisión de producto de
   NO tener buscador global en la topbar. */
.filter-search { position: relative; display: inline-flex; align-items: center; }
.filter-search > svg {
  position: absolute; left: 12px; width: 14px; height: 14px;
  stroke: var(--muted); fill: none; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round; pointer-events: none;
}
.filter-search .filter-input { padding-left: 33px; }

/* ── Tablas ────────────────────────────────────────────────────────── */
.table-wrap {
  background: var(--glass);
  border: 1px solid var(--glass-border);
  border-radius: var(--r-lg);
  backdrop-filter: blur(26px) saturate(1.5);
  -webkit-backdrop-filter: blur(26px) saturate(1.5);
  box-shadow: var(--shadow);
  overflow: hidden; overflow-x: auto; -webkit-overflow-scrolling: touch;
}
table { width: 100%; border-collapse: collapse; font-size: 13px; }
thead th {
  background: transparent;
  padding: 12px 14px; text-align: left;
  font-size: 12px; font-weight: 500; color: var(--muted);
  border-bottom: 1px solid var(--hairline);
  text-transform: none; white-space: nowrap;
}
tbody td { padding: 13px 14px; border-bottom: 1px solid var(--hairline); color: var(--ink); font-variant-numeric: tabular-nums; }
tbody tr { transition: background .25s var(--ease); }
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: rgba(255, 255, 255, .5); }
.td-right, td.num { text-align: right; font-variant-numeric: tabular-nums; }
.cell-primary { color: var(--ink); font-size: 13.5px; font-weight: 600; }
.cell-sub { display: block; color: var(--muted-2); font-size: 11.5px; margin-top: 2px; font-weight: 400; }

/* ── Badges ────────────────────────────────────────────────────────── */
.badge {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 11px; border-radius: var(--r-pill);
  font-size: 12px; font-weight: 600; white-space: nowrap;
}
.badge::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.badge.ok, .badge-produccion { background: rgba(41, 191, 18, .12); color: var(--green-deep); }
.badge.proc, .badge-proceso { background: rgba(60, 145, 230, .13); color: var(--blue-deep); }
.badge.no, .badge-pendiente { background: rgba(244, 68, 46, .12); color: var(--red-deep); }
.badge-plan { background: rgba(39, 39, 39, .06); color: var(--muted); }
.badge-plan::before { display: none; }
.ambiente-badge { display: inline-block; font-size: 11px; font-weight: 600; padding: 4px 12px; border-radius: var(--r-pill); }
.ambiente-badge.cert { background: rgba(60, 145, 230, .13); color: var(--blue-deep); }
.ambiente-badge.prod { background: rgba(41, 191, 18, .13); color: var(--green-deep); }
/* NUEVO 2026-07-20 — tercer ambiente demo */
.ambiente-badge.demo { background: rgba(39, 39, 39, .10); color: var(--ink); }

/* ── Banners y alertas ─────────────────────────────────────────────── */
.banner, .alert, .form-banner {
  padding: 12px 16px; border-radius: 14px; font-size: 12.5px; font-weight: 500;
  margin-bottom: 20px; border: 1px solid transparent;
}
.banner, .form-banner { display: none; }
.banner.visible, .form-banner.visible { display: block; }
.banner.success, .form-banner.success, .alert-success { background: rgba(41, 191, 18, .12); color: var(--green-deep); border-color: rgba(41, 191, 18, .18); }
.banner.error, .form-banner.error, .alert-error { background: rgba(244, 68, 46, .10); color: var(--red-deep); border-color: rgba(244, 68, 46, .16); }
.banner.info, .alert-info { background: rgba(60, 145, 230, .11); color: var(--blue-deep); border-color: rgba(60, 145, 230, .18); }
/* FIX 2026-07-06 — warning pasa de lima a ámbar (el lima queda como acento decorativo). */
.banner.warning { background: rgba(245, 158, 11, .14); color: var(--amber-deep); border-color: rgba(245, 158, 11, .25); }

/* ── Modales glass ─────────────────────────────────────────────────── */
.modal-backdrop {
  position: fixed; inset: 0; z-index: 1000;
  background: rgba(35, 35, 30, .32);
  backdrop-filter: blur(10px) saturate(1.2);
  -webkit-backdrop-filter: blur(10px) saturate(1.2);
  display: none; align-items: center; justify-content: center; padding: 20px;
}
.modal-backdrop.visible { display: flex; animation: backdrop-in .3s var(--ease); }
@keyframes backdrop-in { from { opacity: 0; } }
/* FIX 2026-07-23 — con un modal abierto la rueda scrolleaba la PÁGINA de
   fondo (reporte del usuario en "Nuevo producto" con variantes: el cuerpo
   del modal no desborda y el wheel se encadena al body). Con :has se
   bloquea el scroll del body mientras haya un modal visible — aplica a
   todos los modales del software sin tocar JS. */
body:has(.modal-backdrop.visible) { overflow: hidden; }
.modal-card { overscroll-behavior: contain; }
.modal-card {
  background: rgba(255, 255, 255, .92);
  backdrop-filter: blur(34px) saturate(1.6);
  -webkit-backdrop-filter: blur(34px) saturate(1.6);
  border: 1px solid var(--glass-border);
  border-radius: 24px;
  padding: 30px 30px 26px;
  width: 100%; max-width: 500px; max-height: 90vh; overflow-y: auto;
  box-shadow: var(--shadow-pop);
}
.modal-backdrop.visible .modal-card { animation: modal-in .5s var(--ease-spring); }
@keyframes modal-in { from { opacity: 0; transform: translateY(16px) scale(.95); } }
.modal-title { font-size: 21px; font-weight: 600; letter-spacing: -.02em; margin-bottom: 6px; color: var(--ink); }
.modal-subtitle { color: var(--muted); font-size: 13px; margin-bottom: 22px; }
.modal-footer { display: flex; justify-content: flex-end; gap: 10px; margin-top: 28px; }
.modal-divider { border: none; border-top: 1px solid var(--hairline); margin: 18px 0; }
.modal-section { border-top: 1px solid var(--hairline); padding-top: 20px; margin-top: 20px; }
.modal-section:first-of-type { border-top: 0; padding-top: 0; margin-top: 0; }
.modal-section-label, .modal-section-title { font-size: 12.5px; font-weight: 600; color: var(--muted); text-transform: none; margin-bottom: 12px; }

/* ── Diálogos del sistema (reemplazo de confirm/alert/prompt nativos) ─
   NUEVO 2026-07-02 — pedido del usuario: las ventanas emergentes del
   navegador rompen el lenguaje visual. GlassUI.confirmar/avisar/pedir
   generan este modal compacto. */
.gdlg-card { max-width: 400px; padding: 26px 26px 22px; position: relative; }
/* FIX 2026-07-02 — tinte de atención en los diálogos (pedido del usuario);
   los destructivos (.peligro) llevan tinte rojo.
   FIX 2026-07-06 — el tinte de atención pasa de lima a ámbar: el lima es acento
   decorativo de la marca y no comunica advertencia (mismo criterio que banner.warning). */
.gdlg-card::before { content: ""; position: absolute; inset: 0; pointer-events: none; border-radius: inherit; background: radial-gradient(75% 65% at 82% 12%, rgba(245, 158, 11, .16), transparent 72%); }
/* FIX 2026-07-16 — pedido del usuario: la LUZ de fondo del diálogo de
   error debe verse claramente ROJA (antes .13 era indistinguible de la
   ámbar por defecto .16): más alpha y más cobertura. */
.gdlg-card.peligro::before { background: radial-gradient(85% 75% at 82% 12%, rgba(244, 68, 46, .24), transparent 76%); }
.gdlg-card > * { position: relative; }
/* Título en la voz tipográfica de los titulares (delgada), tamaño contenido */
.gdlg-title { font-size: 24px; font-weight: 300; letter-spacing: -.02em; line-height: 1.15; color: var(--ink); margin-bottom: 10px; }
/* FIX 2026-07-17 — white-space pre-line: los diálogos con mensajes de
   varios párrafos (\n\n) los renderizan como tales; los de una línea
   no cambian. */
.gdlg-msg { font-size: 13.5px; color: var(--ink-soft); line-height: 1.55; white-space: pre-line; }
.gdlg-input { margin-top: 14px; }
.gdlg-footer { display: flex; justify-content: flex-end; gap: 10px; margin-top: 22px; }
.btn-peligro-solido { background: var(--red-deep); color: #fff; border-color: transparent; }
.btn-peligro-solido:hover { background: #b32413; color: #fff; }

/* ── Estado vacío ──────────────────────────────────────────────────── */
.empty-state {
  text-align: center; padding: 72px 24px;
  background: var(--glass); border: 1px solid var(--glass-border);
  border-radius: var(--r-lg);
  backdrop-filter: blur(26px) saturate(1.5);
  -webkit-backdrop-filter: blur(26px) saturate(1.5);
  box-shadow: var(--shadow);
}
.empty-state-title { font-size: 17px; font-weight: 600; color: var(--ink); margin-bottom: 8px; }
.empty-state-desc { font-size: 13px; color: var(--muted); margin-bottom: 22px; }

/* ── Trama diagonal (series comparativas / segmentos destacados) ──── */
.hatch {
  background:
    repeating-linear-gradient(-55deg, rgba(39, 39, 39, .28) 0 2px, transparent 2px 5.5px),
    rgba(39, 39, 39, .07);
}

/* ── Pie de página (lo inyecta GlassUI.init en todas las páginas) ──── */
.gfoot {
  text-align: center;
  font-size: 12px;
  font-weight: 500;
  color: var(--muted-2);
  flex: none;
  padding: 18px 28px 26px;
}
.gfoot a { color: var(--muted); font-weight: 600; }
.gfoot a:hover { color: var(--ink); }

/* ── Cruce de vistas (switch dentro de una página) ─────────────────
   NUEVO 2026-07-03 — parámetros APROBADOS por el usuario en el switch
   Catálogo ↔ Productos variables ("quedó muy buena en velocidad y
   efectos"): ESTÁNDAR para todo elemento futuro con este tipo de
   transición. La vista saliente recibe .swap-izq o .swap-der (según la
   dirección), al terminar se le pone [hidden]; la entrante parte con la
   clase del lado contrario (sin transición, forzando reflow) y se le
   retira para animar a neutro. El JS que oculta al terminar debe usar
   220 ms (calzar con la duración de aquí). Solo anima opacity/transform/
   filter (GPU, 60 fps). Referencia viva: inventario.html (cambiarVista). */
.vista-swap {
  transition: opacity .22s var(--ease), transform .22s var(--ease), filter .22s var(--ease);
}
.vista-swap[hidden] { display: none !important; }
.vista-swap.swap-izq { opacity: 0; transform: translateX(-18px); filter: blur(4px); pointer-events: none; }
.vista-swap.swap-der { opacity: 0; transform: translateX(18px); filter: blur(4px); pointer-events: none; }
/* Variante para ACCIONES contextuales (FIX 2026-07-03, regla del usuario):
   los botones NO se desplazan — solo fade in/out EN SU LUGAR (el
   translateX del saliente reducía el ancho ocupado y el entrante caía de
   línea). El contenido (listas) mantiene el deslizamiento de arriba.
   Mismo JS: acepta swap-izq/swap-der pero aquí solo animan opacidad. */
.vista-fade { transition: opacity .22s var(--ease); }
/* FIX 2026-07-03 (raíz del salto reportado): el grupo oculto NO sale del
   layout — visibility en vez de display:none. Así la celda de grid donde
   se apilan las acciones SIEMPRE mide como el grupo más ancho y el
   contenedor nunca cambia de línea al cruzar (a anchos medios, el grupo
   ancho envolvía bajo el título y el angosto no → el contenedor entero
   saltaba 254↔174px). Reproducido y verificado a 1150px. */
.vista-fade[hidden] { display: flex !important; visibility: hidden !important; pointer-events: none; }
.vista-fade.swap-izq, .vista-fade.swap-der { opacity: 0; pointer-events: none; }

/* ── Despliegue animado (expansión de detalle en listas) ───────────
   NUEVO 2026-07-05 — pedido del usuario: el detalle de una fila (p. ej.
   artículos de una bodega) se despliega fluido en vez de saltar. Patrón
   grid-template-rows 0fr→1fr (anima altura auto sin medir con JS).
   Uso: <div class="despliegue [abierto]"><div class="despliegue-inner">…
   El JS agrega .abierto en un rAF tras render (para que la transición
   corra) y al cerrar la quita y re-renderiza tras ~300ms. */
.despliegue {
  display: grid; grid-template-rows: 0fr;
  opacity: 0;
  transition: grid-template-rows .28s var(--ease), opacity .22s var(--ease);
}
.despliegue.abierto { grid-template-rows: 1fr; opacity: 1; }
.despliegue > .despliegue-inner { overflow: hidden; min-height: 0; }

/* ── Menú de módulos (filas de opción) ──────────────────────────────
   NUEVO 2026-07-06 — rediseño aprobado por el usuario en el hub de
   Insumos y replicado a todos los hubs: las cards verticales de
   submódulos se leían igual que los KPIs del dashboard y todo se
   mezclaba. La navegación ahora son FILAS DE OPCIÓN — horizontales,
   superficie más sólida y radio menor que las cards de datos, icono en
   cápsula + chevron. Estructura:
     .menu-list > a.menu-item > .mi-icon(svg) + .mi-text(.mi-title +
     .mi-desc) + .mi-chev(svg chevron) — deshabilitado: div.menu-item
     .disabled con .coming-badge en lugar del chevron. */
.menu-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); gap: 12px; }
.menu-item {
  display: flex; align-items: center; gap: 16px;
  padding: 15px 18px; text-decoration: none; cursor: pointer;
  background: rgba(255, 255, 255, .72);
  border: 1px solid rgba(255, 255, 255, .7);
  border-radius: 16px;
  box-shadow: 0 1px 2px rgba(39, 39, 39, .04);
  transition: background .22s var(--ease), border-color .22s var(--ease),
              transform .22s var(--ease);
}
.menu-item:hover { background: rgba(255, 255, 255, .94); border-color: rgba(39, 39, 39, .12); }
.menu-item:hover .mi-chev svg { transform: translateX(3px); }
.menu-item:hover .mi-icon { background: var(--ink); color: #fff; }
.mi-icon {
  flex: none; width: 42px; height: 42px; border-radius: 12px;
  display: grid; place-items: center;
  position: relative; overflow: hidden;   /* contiene el destello */
  background: rgba(39, 39, 39, .06); color: var(--ink-soft);
  transition: background .22s var(--ease), color .22s var(--ease);
}
.mi-icon svg { width: 21px; height: 21px; stroke-width: 1.6; stroke: currentColor; fill: none; stroke-linecap: round; stroke-linejoin: round; position: relative; }
/* Destello en la cápsula cuando se pone negra (pedido del usuario
   2026-07-06): mismo timing del estándar de botones oscuros (btn-shine
   0.9s ease-in-out en loop, aparición instantánea).
   FIX 2026-07-07 — la banda del gradiente es RELATIVA AL ANCHO del
   elemento: los stops de los botones (37%→63%) en una cápsula de 42px
   daban ~11px de banda — se seguía viendo delgada tras el primer
   ensanche. En elementos chicos los stops se abren mucho más
   (28%→72% ≈ 18px) para que el barrido se perciba igual de grueso. */
/* FIX 2026-07-07 (iteración 2, verificada con capturas cuadro a cuadro) —
   abrir los stops SOLO diluía la luz: banda ancha pero tenue, se seguía
   viendo "delgada". La percepción de grosor necesita un NÚCLEO brillante
   (.55) con hombros suaves — un brillo definido, no un velo. */
/* FIX 2026-07-07 (iteración 3) — el núcleo era un pico que tocaba el
   máximo solo en el 50%: seguía leyéndose delgado. Ahora es una MESETA
   (brillo pleno sostenido del 42% al 58% ≈ un tercio de la cápsula). */
.mi-icon::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(105deg,
    transparent 24%, rgba(255, 255, 255, .20) 34%,
    rgba(255, 255, 255, .55) 42%, rgba(255, 255, 255, .55) 58%,
    rgba(255, 255, 255, .20) 66%, transparent 76%);
  transform: translateX(-130%);
}
.menu-item:not(.disabled):hover .mi-icon::after {
  animation: btn-shine 1.7s ease-in-out infinite;
}
.mi-text { min-width: 0; }
.mi-title { display: block; font-size: 14.5px; font-weight: 600; color: var(--ink); }
.mi-desc {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  font-size: 12px; color: var(--muted); line-height: 1.4; margin-top: 2px;
  overflow: hidden;
}
.mi-chev { flex: none; margin-left: auto; line-height: 0; color: var(--muted-2); }
.mi-chev svg { width: 16px; height: 16px; stroke-width: 2; stroke: currentColor; fill: none; stroke-linecap: round; stroke-linejoin: round; transition: transform .22s var(--ease); }
.menu-item.disabled { opacity: .55; pointer-events: none; cursor: default; }
.menu-item .coming-badge {
  flex: none; margin-left: auto;
  font-size: 10.5px; font-weight: 600; color: var(--muted-2);
  background: rgba(39, 39, 39, .06); padding: 3px 10px; border-radius: var(--r-pill);
}

/* ── Foco visible ──────────────────────────────────────────────────── */
button:focus-visible, a:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid rgba(60, 145, 230, .55); outline-offset: 2px;
}
.form-input:focus-visible, .form-select:focus-visible { outline: none; }

/* ── Revelado de entrada ───────────────────────────────────────────
   Al final del stylesheet a propósito: mientras el elemento tiene la
   clase .reveal esta transición debe ganarle a la de .card (hover) —
   a igual especificidad decide el orden. GlassUI retira .reveal al
   terminar la entrada. */
/* FIX 2026-07-03 — coreografía de entrada acelerada para el día a día
   (pedido del usuario: "más fluido, tipo 60 fps"): duraciones .45/.5s
   (antes .8/.9s) y los retardos escalonados de TODAS las páginas se
   escalan a la mitad con calc() — una sola palanca, sin tocar los --d
   inline de cada página. */
.reveal { opacity: 0; transform: translateY(18px); filter: blur(6px); transition: opacity .45s var(--ease), transform .5s var(--ease), filter .45s var(--ease); transition-delay: calc(var(--d, 0ms) * 0.5); }
.reveal.in { opacity: 1; transform: none; filter: none; }

/* ── Responsive ────────────────────────────────────────────────────── */
@media (max-width: 1020px) {
  .navpills { display: none; }
  .only-mobile { display: block; }
  .gtop .only-mobile { margin-left: auto; }
  .page-head { align-items: flex-start; flex-direction: column; }
}
@media (max-width: 700px) {
  main, .page, .page-wide { padding: 14px 18px 56px; }
}

/* ── Modo ligero (POS / hardware modesto) ──────────────────────────
   Mantiene el lenguaje (greige, radios, tipografía, acentos) pero
   elimina el costo GPU: sin blur real ni brillos animados. */
.glass-lite .ambient { display: none; }
.glass-lite .card, .glass-lite .table-wrap, .glass-lite .empty-state,
.glass-lite .navpills, .glass-lite .icirc, .glass-lite .btn, .glass-lite .qbtn,
.glass-lite .pop, .glass-lite .modal-card, .glass-lite .modal-backdrop {
  backdrop-filter: none; -webkit-backdrop-filter: none;
}
.glass-lite .card, .glass-lite .table-wrap, .glass-lite .empty-state { background: rgba(255, 255, 255, .82); }
.glass-lite .pop, .glass-lite .modal-card { background: #fdfdfc; }

/* ── MODO OSCURO — fase 1: tokens duales (NUEVO 2026-07-23) ─────────
   Pedido del usuario: claro/oscuro en todo el sitio. El atributo
   [data-theme="dark"] en <html> (lo pone GlassUI leyendo la MISMA clave
   localStorage del POS, ttpos_pos_theme) redefine la paleta y ajusta las
   superficies con blancos quemados. Fase 2 pendiente: barrer los estilos
   inline de las páginas hacia tokens. Los impresos (PDF) no cambian. */
[data-theme="dark"] {
  color-scheme: dark;
  --canvas: #0d0d0d;
  --ink: #f4f4f2;
  --ink-soft: #cfcfca;
  --muted: #8f8f88;
  --muted-2: #6a6a63;
  --hairline: rgba(255, 255, 255, .09);
  --glass: rgba(26, 26, 26, .92);
  --glass-strong: rgba(32, 32, 32, .96);
  --glass-border: rgba(255, 255, 255, .10);
  --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 14px 34px rgba(0, 0, 0, .5);
  --shadow-hover: 0 2px 4px rgba(0, 0, 0, .45), 0 22px 48px rgba(0, 0, 0, .6);
  --shadow-pop: 0 2px 6px rgba(0, 0, 0, .5), 0 26px 64px rgba(0, 0, 0, .7);
  --color-focus-ring: rgba(96, 165, 235, .5);
  --color-bg-alt: rgba(255, 255, 255, .06);
  --color-border: rgba(255, 255, 255, .14);
  --color-border-light: var(--hairline);
}
/* Superficies con blanco quemado (se llevan a tokens en la fase 2). */
[data-theme="dark"] .form-input, [data-theme="dark"] .form-select,
[data-theme="dark"] .field input, [data-theme="dark"] .field select,
[data-theme="dark"] .field textarea, [data-theme="dark"] textarea.form-input,
[data-theme="dark"] .filter-select, [data-theme="dark"] .filter-input {
  background: rgba(255, 255, 255, .07);
  border-color: rgba(255, 255, 255, .12);
}
[data-theme="dark"] .form-input:hover, [data-theme="dark"] .form-select:hover { background: rgba(255, 255, 255, .10); }
[data-theme="dark"] .form-input:focus, [data-theme="dark"] .form-select:focus,
[data-theme="dark"] .field input:focus, [data-theme="dark"] .field select:focus,
[data-theme="dark"] .field textarea:focus { background: #242422; }
[data-theme="dark"] .modal-card { background: rgba(34, 34, 32, .96); }
[data-theme="dark"] .pop { background: rgba(34, 34, 32, .97); }
[data-theme="dark"] .notif:hover, [data-theme="dark"] .mitem:hover { background: rgba(255, 255, 255, .07); }
/* FIX 2026-07-23 — badges de estado apagados en dark: los tonos -deep
   (pensados para fondo claro) no contrastan sobre las superficies oscuras.
   En dark el texto usa el tono brillante de la paleta. */
[data-theme="dark"] .badge.ok, [data-theme="dark"] .badge-produccion { color: var(--green); }
[data-theme="dark"] .badge.proc, [data-theme="dark"] .badge-proceso { color: var(--blue); }
[data-theme="dark"] .badge.no, [data-theme="dark"] .badge-pendiente { color: var(--red); }
[data-theme="dark"] tbody tr:hover { background: rgba(255, 255, 255, .04); }
[data-theme="dark"] .toggle .tthumb { background: #3c3c3a; }
[data-theme="dark"] .gside-toggle { background: rgba(255, 255, 255, .08); }
[data-theme="dark"] .icirc .dot { border-color: #222; }
[data-theme="dark"] .menu-item { background: rgba(34, 34, 32, .85); border-color: rgba(255, 255, 255, .08); }
[data-theme="dark"] .menu-item:hover { background: rgba(48, 48, 46, .95); border-color: rgba(255, 255, 255, .16); }
/* Elementos "tinta sólida": en oscuro --ink es claro → el texto blanco
   fijo se invierte a oscuro para mantener el contraste. */
[data-theme="dark"] .btn-primary, [data-theme="dark"] .qbtn.dark,
[data-theme="dark"] .menu-item:hover .mi-icon { color: #141414; }
/* FIX 2026-07-23 (pedido del usuario) — los iconos LIMA de los botones
   tinta casi no se ven en oscuro (el botón se invierte a superficie
   clara): pasan al par oscuro del lima (--lime-deep). */
[data-theme="dark"] .btn-primary svg, [data-theme="dark"] .qbtn.dark svg { stroke: var(--lime-deep); }
[data-theme="dark"] .navpills a.active, [data-theme="dark"] .gside .sitem.active { color: #141414; }
/* Patrón "tinta sólida" repetido en las páginas (background: var(--ink) +
   color #fff): en oscuro --ink es claro → el texto pasa a tinta oscura.
   Clases por página: filter-pill/badge-dark (usuarios), sec-pill/filter-tab
   (finanzas), tipo-dte-btn/btn-dark (emisión), fam-item (maestra). */
[data-theme="dark"] .filter-pill.active, [data-theme="dark"] .badge-dark,
[data-theme="dark"] .sec-pill.active, [data-theme="dark"] .filter-tab.active,
[data-theme="dark"] .tipo-dte-btn.active, [data-theme="dark"] .btn-dark,
[data-theme="dark"] .fam-item.active { color: #141414; }
[data-theme="dark"] .rol-chip { background: rgba(255, 255, 255, .09); }
[data-theme="dark"] .avatar { color: #5a6414; }
/* Logos Nanuc/Crumb (tinta negra) → blanco. Provisional con invert();
   cuando exista la variante blanca del asset se reemplaza. */
[data-theme="dark"] img[src$="nanuc.svg"],
[data-theme="dark"] img[src$="nanuc-iso.svg"],
[data-theme="dark"] img[src$="club-full.svg"],
[data-theme="dark"] img[src$="club-n.svg"],
[data-theme="dark"] img[src$="logocrumbpos.svg"] { filter: invert(1); }

/* Los IMPRESOS no conocen el modo oscuro (regla del usuario 2026-07-23):
   ante cualquier window.print(), los tokens vuelven a la paleta clara y
   los logos dejan de invertirse. Los PDFs generados en el servidor nunca
   se ven afectados por el theme. */
@media print {
  [data-theme="dark"] {
    --canvas: #ffffff;
    --ink: #272727;
    --ink-soft: #4a4a46;
    --muted: #82827c;
    --muted-2: #a6a69f;
    --hairline: rgba(39, 39, 39, .08);
    --glass: rgba(255, 255, 255, .55);
    --glass-strong: rgba(255, 255, 255, .78);
    --glass-border: rgba(255, 255, 255, .65);
  }
  [data-theme="dark"] img[src$="nanuc.svg"],
  [data-theme="dark"] img[src$="nanuc-iso.svg"],
  [data-theme="dark"] img[src$="club-full.svg"],
  [data-theme="dark"] img[src$="club-n.svg"],
  [data-theme="dark"] img[src$="logocrumbpos.svg"] { filter: none; }
}

/* ── Movimiento reducido ───────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .reveal { transition: none; opacity: 1; transform: none; filter: none; }
  .ambient i { animation: none; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}
