/* ============================================================
   ÍNDICE — dónde está cada cosa. El archivo ya venía dividido por
   pantalla/función con estos mismos títulos; esto solo los junta acá
   arriba para no tener que hacer Ctrl+F a ciegas. Los números de línea
   son del principio de cada bloque, no un rango cerrado.

   1     TOKENS — alias hacia tokens.css
   98    SHELL — navbar, riel de navegación, topbar
   254   PIEZAS COMUNES — botones, pills, inputs, tabla.dt, paneles
   288   CATÁLOGO
   343   CAJÓN FICHA TÉCNICA
   444   CLIENTE / SCORE
   508   SIMULADOR
   536   TABLERO
   584   INVENTARIO / OPERACIÓN
   716   FACTURACIÓN
   796   AÑADIDOS DE LA APLICACIÓN — bolsa grande: estados de carga/vacío,
         score de cliente, escaneo/recepción, fotos de producto, promos,
         galería con lupa, fichas y facetas de catálogo, POS/mostrador,
         carrito, cobro, tiquete. Cada tema tiene su "── título ──" (ahí
         adentro, no repetido en este índice). NO se reordenó físicamente:
         hay parches que dependen de ir después de lo que corrigen —p.ej.
         dos reglas .dev-body con justify-content distinto, la segunda
         "retirando" la primera a propósito— y una regla que tiene que ir
         ANTES de su propio @media o el botón que activa nunca aparece.
         Mover eso "por tema" invierte cuál gana en silencio. Lo seguro y
         lo que se hizo fue agregar subtítulos SIN mover una sola regla.
   1895  MÓVIL — navegación táctil, breakpoints 480/720/900, tabla→tarjeta
   ============================================================ */

/* ============================================================
   TOKENS — alias hacia wwwroot/css/tokens.css (se carga antes que
   este archivo). Ya no hay valores BMW acá: todo color/radio/sombra
   vive en tokens.css; esto solo traduce los nombres viejos que usa
   el resto de payjoy.css (--accent, --ink, --r-m, --good…) a los
   nuevos, para no tener que tocar ~2000 líneas de golpe.
   Los alias que ya no se usen en ningún selector se pueden retirar
   más adelante (ver checklist de la fase 1); los que sigan en uso
   se quedan sin plazo.
   ============================================================ */
:root{
  --plane: var(--bg);
  --surface-2: var(--surface-muted);
  --surface-3: var(--border);
  --ink: var(--text);
  --ink-2: var(--text-secondary);
  --ink-3: var(--text-tertiary);
  --line: var(--border);
  --line-2: var(--border-strong);
  --accent: var(--primary-600);
  --accent-2: var(--primary-700);
  --accent-on: #ffffff;
  --accent-soft: var(--primary-50);
  --good: var(--success-fill);
  --warn: var(--warning-fill);
  --crit: var(--danger-fill);
  --good-soft: var(--success-bg);
  --warn-soft: var(--warning-bg);
  --crit-soft: var(--danger-bg);
  /* Sin equivalente en el sistema nuevo — son del tablero (gráficos) y
     no forman parte de este rediseño; se dejan con sus valores de antes. */
  --plasma: #1a2129;
  --plasma-soft: rgba(26, 33, 41, 0.1);
  --series-1: var(--primary-600);
  --series-2: #eb6834;
  --series-3: var(--success-fill);
  --grid: var(--border);
  --axis: var(--border-strong);
  --glass: rgba(255, 255, 255, 0.72);
  --screen-tint: #1a2129;

  --font-cond: var(--font-ui); /* alias temporal — ver auditoría G1, elimina sus usos cuando se pueda */

  --r-s: var(--radius-sm);
  --r-m: var(--radius-md);
  --r-l: var(--radius-lg);
  --r-xl: var(--radius-lg);

  /* Alto y tipografía de controles. bmw-DESIGN.md ya documentaba 48px/16px —
     el CSS nunca los aplicó. 48px es el mínimo táctil recomendado; 16px es el
     umbral bajo el cual iOS hace zoom automático al enfocar un campo. */
  --ctl-h: 38px;
  --ctl-fs: 13.5px;
}
/* Único juego de breakpoints para todo lo nuevo de aquí en más: 480 (teléfono
   angosto) · 720 (teléfono) · 900 (donde el riel ya se vuelve hamburguesa).
   El resto del archivo trae ocho valores sueltos de antes — no se tocan para
   no arriesgar layouts de escritorio que ya funcionan. */
@media (pointer:coarse), (max-width:720px){
  :root{ --ctl-h: 48px; --ctl-fs: 16px; }
}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]){
    --plasma:#2CC7DA;
    --plasma-soft:rgba(44,199,218,.14);
    --series-1:#3987e5;
    --series-2:#d95926;
    --series-3:#199e70;
    --glass:rgba(27,30,40,.72);
    --screen-tint:#05060A;
    /* --primary-600 se aclara en oscuro (para leerse como TEXTO sobre
       fondo oscuro) -- pero eso es lo opuesto de lo que necesita un
       relleno sólido con texto blanco encima (botón .btn-p, chip activo):
       blanco sobre ese azul claro da 3.38:1. Oscuro sobre él sí cumple. */
    --accent-on: var(--bg);
  }
}
:root[data-theme="dark"]{
  --plasma:#2CC7DA; --plasma-soft:rgba(44,199,218,.14);
  --series-1:#3987e5; --series-2:#d95926; --series-3:#199e70;
  --glass:rgba(27,30,40,.72);
  --screen-tint:#05060A;
  --accent-on: var(--bg);
}

*{box-sizing:border-box}
body{margin:0;background:var(--plane);color:var(--ink);font-family:var(--font-ui);
  font-size:14px;line-height:1.5;-webkit-font-smoothing:antialiased;font-feature-settings:"cv11","ss01"}
h1,h2,h3,h4{margin:0;text-wrap:balance}
button,input,select{font:inherit;color:inherit}
/* Antes las cifras caían en --font-cond (condensada) para distinguirse del
   texto normal; ahora todo es Inter, así que la distinción es tabular-nums
   (los dígitos no bailan de ancho fila a fila) en vez de un tipo de letra
   aparte. Ver auditoría G1. */
.num,.price,[data-num]{font-variant-numeric:tabular-nums}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}
@media (prefers-reduced-motion: reduce){*{animation-duration:.01ms !important;transition-duration:.01ms !important}}

.num{font-family:var(--font-cond);font-variant-numeric:tabular-nums;letter-spacing:.01em}
.mono{font-family:var(--font-mono);font-size:.88em}
.eyebrow{font-family:var(--font-cond);text-transform:uppercase;letter-spacing:.13em;
  font-size:11px;color:var(--ink-3);font-weight:600}

/* ============================================================
   SHELL — Navbar horizontal con dropdowns
   ============================================================ */
/* Riel a la izquierda, marco a la derecha. Antes eran dos barras apiladas
   (56 + 44 = 100 px) que se cobraban en TODA vista; el riel devuelve ese alto,
   que en el POS es la diferencia entre ver 2 filas de venta o ver 5. */
.app{display:grid;grid-template-columns:60px minmax(0,1fr);min-height:100vh;min-height:100dvh}
.marco{display:flex;flex-direction:column;min-width:0}

/* ── Riel ────────────────────────────────────────────────── */
/* Siempre de 60 px con iconos. Al posar el cursor se ensancha a 224 px por
   ENCIMA del contenido: la columna de la grilla sigue en 60, así que la vista
   activa no se mueve ni un píxel -- el riel simplemente la tapa mientras se
   elige. Los rótulos ya están en el DOM con opacidad 0 y el mismo alto que
   tendrán al verse, para que nada salte verticalmente al desplegarse.

   El retardo de 80 ms en la entrada evita que se abra cuando el cursor solo
   pasa de largo camino a otra cosa; la salida es inmediata. */
.rail{position:sticky;top:0;height:100vh;width:60px;z-index:45;
  overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin;
  display:flex;flex-direction:column;padding:10px 8px 16px;
  background:var(--surface);border-right:1px solid var(--line);
  transition:width .16s ease,box-shadow .16s ease,z-index 0s linear .16s}
/* Desplegado sube a 65: por encima de un campo enfocado (.field:focus-within
   está en 60 -- el escáner del POS lo está siempre) y de la barra de comparar
   (55), pero debajo de modales (70) y scrims (71). Plegado vuelve a 45 para que
   la tira no se vea a través del oscurecido de un modal. El z-index cae al
   TERMINAR de cerrarse, no al empezar: si cayera antes, el riel pasaría por
   debajo del campo durante la animación de cierre. */
.rail:hover{width:224px;z-index:65;transition-delay:.08s;box-shadow:var(--shadow-3)}

.rail-marca .logo-full,.rail-grp,.rail-btn span{opacity:0;transition:opacity .1s ease}
.rail:hover .rail-marca .logo-full,.rail:hover .rail-grp,.rail:hover .rail-btn span{opacity:1;transition-delay:.1s}
.rail:hover .rail-marca .logo-icon{opacity:0}

/* Marca: dos imágenes superpuestas. Plegado se ve el isotipo, centrado en la
   misma columna que los iconos; al desplegar se funde con el logo completo. */
.rail-marca{position:relative;height:40px;margin-bottom:6px}
.rail-marca img{position:absolute;top:50%;transform:translateY(-50%);width:auto;transition:opacity .1s ease}
.rail-marca .logo-icon{left:22px;height:22px;transform:translate(-50%,-50%)}
.rail-marca .logo-full{left:8px;height:38px}

/* Logo del NEGOCIO (cargado en Configuración → Negocio): a diferencia del de
   CeluCellApp, este va SIEMPRE visible — es quien vende, no la herramienta.
   Plegado: solo el isotipo, centrado en la columna de iconos. Desplegado:
   el logo crece y el NOMBRE aparece debajo como texto -- el logo puede ser
   un isotipo sin letras, así que el nombre no depende de que la imagen ya
   lo traiga escrito. Por eso el alto del bloque crece al desplegar (40 a
   64px): con logo + nombre en dos renglones no entran en la misma altura
   que un logo solo. */
.rail-marca.tiene-logo{display:flex;flex-direction:column;justify-content:center;
  align-items:center;height:40px;overflow:hidden;transition:height .16s ease}
/* Anula el position:absolute de ".rail-marca img" de arriba (pensado para las
   dos imágenes superpuestas de CeluCellApp) -- acá el logo tiene que fluir en
   la columna junto al nombre, no flotar centrado encima de él. */
.rail-marca.tiene-logo .logo-negocio{position:static;top:auto;left:auto;transform:none;
  height:32px;max-width:40px;object-fit:contain;transition:all .16s ease}
.rail-marca.tiene-logo .marca-nombre{display:block;width:100%;margin-top:6px;
  font-weight:650;font-size:12.5px;color:var(--ink);text-align:center;padding:0 2px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  opacity:0;max-height:0;transition:opacity .1s ease}

.rail:hover .rail-marca.tiene-logo,.rail.open .rail-marca.tiene-logo{
  height:64px;align-items:center;padding:4px 2px 0}
.rail:hover .rail-marca.tiene-logo .logo-negocio,
.rail.open .rail-marca.tiene-logo .logo-negocio{max-width:100%;height:34px}
.rail:hover .rail-marca.tiene-logo .marca-nombre,
.rail.open .rail-marca.tiene-logo .marca-nombre{opacity:1;max-height:20px;transition-delay:.1s}

/* Crédito de CeluCellApp: bajó del lugar del logo a esto, con el mismo
   patrón de dos imágenes superpuestas que tenía la marca — plegado el
   isotipo solo, desplegado el logo completo (con el nombre ya escrito en la
   imagen) y la leyenda debajo. */
.rail-foot{margin-top:auto;padding:10px 6px 0;border-top:1px solid var(--line);overflow:hidden}
.rail-foot-marca{position:relative;height:22px}
.rail-foot-marca img{position:absolute;top:50%;transform:translateY(-50%);width:auto;
  opacity:.75;transition:opacity .1s ease}
.rail-foot-marca .logo-icon{left:20px;height:18px;transform:translate(-50%,-50%)}
.rail-foot-marca .logo-full{left:2px;height:24px;opacity:0}
.rail:hover .rail-foot-marca .logo-full,.rail.open .rail-foot-marca .logo-full{opacity:.85}
.rail:hover .rail-foot-marca .logo-icon,.rail.open .rail-foot-marca .logo-icon{opacity:0}
.rail-foot span{display:block;margin-top:5px;font-size:10.5px;color:var(--ink-3);
  white-space:nowrap;opacity:0;transition:opacity .1s ease}
.rail:hover .rail-foot span,.rail.open .rail-foot span{opacity:1;transition-delay:.1s}

/* El título de grupo ocupa su renglón siempre. Plegado, ese renglón muestra
   una línea fina de separación; desplegado, el texto. Un solo elemento para
   los dos estados: así el alto del riel es idéntico y nada se reacomoda. */
.rail-grp{position:relative;margin:16px 0 4px;padding:0 10px;height:13px;line-height:13px;
  font-size:10.5px;letter-spacing:.11em;text-transform:uppercase;color:var(--ink-3);font-weight:600;
  white-space:nowrap}
.rail-grp::before{content:"";position:absolute;left:12px;right:12px;top:50%;
  border-top:1px solid var(--line);opacity:1;transition:opacity .1s}
.rail:hover .rail-grp::before{opacity:0}
.rail-group:first-of-type .rail-grp{margin-top:4px}
.rail-group:first-of-type .rail-grp::before{display:none}

/* ── Barra superior ──────────────────────────────────────── */
.navbar{position:sticky;top:0;z-index:40;display:flex;align-items:center;gap:10px;
  height:52px;padding:0 20px;background:var(--glass);backdrop-filter:blur(14px);
  border-bottom:1px solid var(--line)}

/* ── Botones del riel ────────────────────────────────────── */
/* Fila compacta, texto atenuado, icono más atenuado todavía. El activo es un
   relleno neutro con el texto a pleno y UN solo toque de acento, en el icono:
   la píldora azul entera gritaba más que el contenido de la página. */
.rail-group{display:flex;flex-direction:column;gap:1px}
.rail-btn{display:flex;align-items:center;gap:11px;height:34px;padding:0 0 0 13px;
  border:0;background:none;border-radius:var(--r-s);cursor:pointer;justify-content:flex-start;
  color:var(--ink-2);transition:background .12s,color .12s;width:100%;text-align:left;
  white-space:nowrap;overflow:hidden}
.rail-btn svg{width:17px;height:17px;stroke-width:1.7;flex:none;color:var(--ink-3);transition:color .12s}
.rail-btn span{font-size:13.5px;font-weight:500;white-space:nowrap;line-height:1}
.rail-btn:hover{background:var(--surface-2);color:var(--ink)}
.rail-btn:hover svg{color:var(--ink-2)}
.rail-btn.on{background:var(--surface-3);color:var(--ink)}
.rail-btn.on span{font-weight:600}
.rail-btn.on svg{color:var(--accent)}

/* ── Nav spacer & right items ────────────────────────────── */
.nav-spacer{flex:1}
/* En escritorio la marca ya vive arriba del riel; repetirla acá sería la
   misma imagen dos veces en la misma pantalla. Solo se enciende bajo 900px. */
.nav-marca{display:none}

/* El título de la vista vive en la barra superior: era la única información
   de la segunda fila y repetía la sección ya marcada en el riel. */
.crumb{display:flex;align-items:baseline;gap:9px;min-width:0;overflow:hidden}
.crumb b{font-size:15.5px;font-weight:650;letter-spacing:-.01em;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}
.crumb span{color:var(--ink-3);font-size:12.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* ── Topbar items kept for compatibility ─────────────────── */
.chip-maqueta{margin-left:auto;display:flex;align-items:center;gap:7px;font-family:var(--font-cond);
  font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-3);
  border:1px dashed var(--line-2);border-radius:999px;padding:5px 11px}
.dot-live{width:6px;height:6px;border-radius:50%;background:var(--warn)}
/* Ahora es un <button> (abre el menú de usuario) — se resetea lo que
   trae un botón nativo (fondo, borde propio, fuente) para que se siga
   viendo igual que cuando era un <div> decorativo. */
.asesor{display:flex;align-items:center;gap:9px;padding:6px 4px 6px 14px;margin:-6px -4px -6px 0;
  border-left:1px solid var(--line);background:none;border-top:0;border-right:0;border-bottom:0;
  font:inherit;color:inherit;cursor:pointer;border-radius:10px;text-align:left}
.asesor:hover{background:var(--surface-2)}
.avatar{width:32px;height:32px;border-radius:50%;background:var(--accent);color:var(--accent-on);
  display:grid;place-items:center;font-family:var(--font-cond);font-size:13px;font-weight:700;flex:none}
.asesor small{display:block;color:var(--ink-3);font-size:11px;line-height:1.25}
.asesor b{font-size:12.5px;font-weight:600}
.icon-btn{width:34px;height:34px;border-radius:10px;border:1px solid var(--line);
  background:var(--surface);display:grid;place-items:center;cursor:pointer;color:var(--ink-2)}
.icon-btn:hover{background:var(--surface-2);color:var(--ink)}
.icon-btn svg{width:17px;height:17px;stroke-width:1.7}
.icon-btn.sm{width:28px;height:28px;border-radius:8px}
.icon-btn.sm svg{width:15px;height:15px}
.icon-btn.crit:hover{background:var(--crit-soft);color:var(--danger-text);border-color:var(--crit)}
.icon-btn:disabled{opacity:.45;cursor:default}
.acciones{display:flex;gap:5px;justify-content:flex-end}
.ico-off{display:inline-flex;color:var(--ink-3);flex:none}
.ico-off svg{width:15px;height:15px;stroke-width:1.7}

.hamburger{display:none;width:36px;height:36px;border:0;background:none;
  border-radius:8px;cursor:pointer;color:var(--ink-2);place-items:center}
.tabbar{display:none}
.hamburger svg{width:20px;height:20px;stroke-width:1.8}
.hamburger:hover{background:var(--surface-2);color:var(--ink)}

/* Por debajo de 900 no hay cursor que posar: el riel se reemplaza por la
   barra táctil de abajo + la hoja de «Más» (ver .tabbar/.sheet). El botón
   de menú y el riel deslizante quedan de reserva -- ocultos, no borrados --
   por si algún día hace falta un acceso alterno; hoy no lo dispara nadie.
   ponytail: maquinaria del riel off-canvas sin uso bajo 900px, retirarla
   si en unos meses nadie la reactivó. */
@media (max-width:900px){
  .app{grid-template-columns:minmax(0,1fr)}
  .rail{position:fixed;left:0;top:0;bottom:0;width:224px;height:100dvh;
    transform:translateX(-100%);transition:transform .18s ease;box-shadow:var(--shadow-3)}
  .rail.open{transform:none}
  .rail .rail-marca .logo-full,.rail .rail-grp,.rail .rail-btn span{opacity:1}
  .rail .rail-marca .logo-icon{opacity:0}
  .rail .rail-grp::before{opacity:0}
  .rail .rail-marca.tiene-logo{height:64px;align-items:center;padding:4px 2px 0}
  .rail .rail-marca.tiene-logo .logo-negocio{max-width:100%;height:34px}
  .rail .rail-marca.tiene-logo .marca-nombre{opacity:1;max-height:20px}
  .rail .rail-foot-marca .logo-full{opacity:.85}
  .rail .rail-foot-marca .logo-icon{opacity:0}
  .rail .rail-foot span{opacity:1}

  .tabbar{display:flex}

  /* La marca, con una línea que la separa del título de la sección: sin ella
     el logo y el texto se leen como una sola cosa. El separador es el borde
     del propio contenedor — un elemento aparte solo para pintar una raya es
     nodo de más.

     Va ajustado a propósito. En 375px el título «Catálogo de equipos» pide
     147px y la barra deja unos 130: cada píxel que se lleva la marca se lo
     quita al nombre de la sección, que es lo que le dice a la persona dónde
     está parada. Con 22px de alto y este relleno, entra completo desde 390px;
     por debajo se corta con puntos suspensivos, que es lo correcto. */
  .nav-marca{display:flex;align-items:center;flex:none;
    padding-right:9px;border-right:1px solid var(--line)}
  .nav-marca img{display:block;height:22px;width:auto;max-width:80px;object-fit:contain}
}

.view{display:none;padding:22px}
/* Arriba del catálogo, 22px de relleno son productos que se pierden del
   primer pantallazo. En pantalla angosta se deja como estaba. */
@media (min-width:901px){#view-catalogo{padding-top:14px}}
.view.on{display:block;animation:fade .28s ease}
@keyframes fade{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}

/* ============================================================
   PIEZAS COMUNES
   ============================================================ */
.card{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-l);
  box-shadow:var(--shadow-1)}
.pad{padding:18px}
.card-h{display:flex;align-items:center;gap:10px;padding:15px 18px;border-bottom:1px solid var(--line)}
.card-h h3{font-size:13.5px;font-weight:650;letter-spacing:-.005em}
.card-h .eyebrow{margin-left:auto}

.btn{border:1px solid transparent;border-radius:10px;padding:9px 15px;font-size:var(--ctl-fs,13px);
  min-height:var(--ctl-h,38px);font-weight:600;cursor:pointer;display:inline-flex;align-items:center;
  justify-content:center;gap:7px;transition:transform .12s, background .15s, box-shadow .15s}
.btn:active{transform:translateY(1px)}
.btn svg{width:16px;height:16px;stroke-width:1.8;flex:none}
.btn-p{background:var(--accent);color:var(--accent-on)}
.btn-p:hover{background:var(--accent-2)}
.btn-g{background:var(--surface-2);color:var(--ink);border-color:var(--line)}
.btn-g:hover{background:var(--surface-3)}
/* Fija, no sigue a --ctl-h: vive en toolbars densas (tabla, ledger) que
   todavía no se rediseñaron para dedo — subirla ahí rompería esas filas. */
.btn-sm{padding:6px 11px;font-size:12px;min-height:32px;border-radius:8px}
/* .btn-lg: la acción principal de una pantalla (Cobrar). --ctl-h ya sube a
   48px en móvil por sí solo; acá el piso es 52px en cualquier ancho, porque
   esta es LA acción, no un control más de una fila densa. */
.btn-lg{min-height:52px;padding:0 18px;font-size:15px;border-radius:var(--radius-md)}
/* Texto plano, sin fondo ni borde — para acciones de baja frecuencia que no
   deben competir visualmente con las de verdad (ver .ledger-act #posLimpiar,
   que sigue este mismo patrón desde antes). */
.btn-ghost{background:transparent;border-color:transparent;color:var(--ink-2)}
.btn-ghost:hover{background:var(--surface-2);color:var(--ink)}
.btn-danger{background:var(--danger-fill);color:#fff}
.btn-danger:hover{background:var(--danger-text)}
/* Botón destructivo pero de bajo peso (p.ej. "Vaciar venta" en un menú) —
   mismo espíritu que .btn-ghost, tinte de peligro solo al pasar el mouse. */
.btn-ghost.btn-danger-hint{background:transparent;border-color:transparent;color:var(--ink-2)}
.btn-ghost.btn-danger-hint:hover{background:var(--danger-bg);color:var(--danger-text)}
/* Estado de carga: PJ.btnCargando() fija el ancho actual (para que el botón
   no salte al cambiar el texto), reemplaza el contenido por el spinner +
   el texto nuevo, y marca disabled + aria-busy. */
.btn[aria-busy="true"]{cursor:default;opacity:.85}
.pj-spinner{width:16px;height:16px;border-radius:50%;flex:none;
  border:2px solid rgba(255,255,255,.35);border-top-color:currentColor;
  animation:pj-girar .7s linear infinite}
.btn-g .pj-spinner,.btn-ghost .pj-spinner{border-color:var(--border);border-top-color:var(--ink-2)}
@keyframes pj-girar{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){.pj-spinner{animation-duration:1.6s !important}}

/* 12px es el piso para mayúsculas en un badge (ver sección 5.2 del rediseño);
   11.5px se leía apretado en pantallas chicas. */
.pill{display:inline-flex;align-items:center;gap:5px;border-radius:999px;padding:3px 9px;
  font-family:var(--font-ui);font-size:12px;letter-spacing:.03em;text-transform:uppercase;font-weight:600}
.pill-good{background:var(--good-soft);color:var(--success-text)}
.pill-warn{background:var(--warn-soft);color:var(--warning-text)}
.pill-crit{background:var(--crit-soft);color:var(--danger-text)}
.pill-acc{background:var(--accent-soft);color:var(--accent)}

.spec-chip{display:inline-flex;align-items:center;gap:5px;background:var(--surface-2);
  border:1px solid var(--line);border-radius:7px;padding:3px 7px;font-size:11.5px;color:var(--ink-2)}
.spec-chip b{font-family:var(--font-cond);color:var(--ink);font-weight:600}

/* ============================================================
   CATÁLOGO
   ============================================================ */
.filters{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:18px}
/* Etiqueta + input como un solo bloque: si no, flex-wrap puede partirlos en
   filas distintas y "Hasta" queda flotando sin su campo (visto en Ventas). */
.filters-fecha{display:flex;align-items:center;gap:8px}
.search{position:relative;flex:1;min-width:220px;max-width:340px}
.search svg{position:absolute;left:11px;top:50%;transform:translateY(-50%);width:16px;height:16px;
  stroke-width:1.8;color:var(--ink-3)}
.search input{width:100%;height:var(--ctl-h,38px);padding:0 12px 0 34px;border-radius:11px;
  border:1px solid var(--line);background:var(--surface);color:var(--ink);font-size:var(--ctl-fs,14px)}
.search input::placeholder{color:var(--ink-3)}
.search input:focus{outline:2px solid var(--primary-500);outline-offset:2px;border-color:var(--primary-500)}
.fchip{border:1px solid var(--line);background:var(--surface);border-radius:999px;
  padding:7px 13px;font-size:12.5px;cursor:pointer;color:var(--ink-2);white-space:nowrap}
.fchip:hover{border-color:var(--line-2);color:var(--ink)}
.fchip.on{background:var(--ink);color:var(--surface);border-color:var(--ink)}

/* .chip — base unificada para markup nuevo. .cat-tab/.fchip/.spec-chip
   existentes NO se tocaron: ya tienen ajustes finos de móvil (scroll-snap,
   flechas en .cat-tab) que no vale la pena arriesgar por unificar solo el
   look. Cuando se migre su marcado, apuntan acá. */
.chip{display:inline-flex;align-items:center;gap:7px;min-height:36px;padding:0 14px;
  border-radius:var(--radius-full);border:1px solid var(--border);background:var(--surface);
  color:var(--text-secondary);font-size:var(--fs-caption);font-weight:500;cursor:pointer;
  white-space:nowrap;transition:background .15s,border-color .15s,color .15s}
.chip svg{width:18px;height:18px;flex:none}
.chip:hover{border-color:var(--border-strong);color:var(--text)}
.chip--sel{background:var(--primary-600);border-color:var(--primary-600);color:#fff;font-weight:600}
.chip--sel:hover{background:var(--primary-700);border-color:var(--primary-700);color:#fff}
.chip .chip-n{display:inline-grid;place-items:center;min-width:18px;height:18px;padding:0 5px;
  border-radius:var(--radius-full);background:var(--surface-muted);color:var(--text-tertiary);
  font-size:var(--fs-micro);font-weight:700;line-height:1}
.chip--sel .chip-n{background:rgba(255,255,255,.25);color:#fff}
.chip--quitable{padding-right:8px}
.chip--quitable .chip-x{display:inline-grid;place-items:center;width:20px;height:20px;
  border-radius:50%;background:none;border:0;color:inherit;opacity:.75;cursor:pointer}
.chip--quitable .chip-x:hover{opacity:1;background:rgba(0,0,0,.08)}
.fsel{height:var(--ctl-h,38px);border-radius:11px;border:1px solid var(--line);background:var(--surface);
  padding:0 10px;font-size:var(--ctl-fs,12.5px);cursor:pointer}
.fsel:focus{outline:2px solid var(--primary-500);outline-offset:2px;border-color:var(--primary-500)}
.fcount{margin-left:auto;color:var(--ink-3);font-size:12.5px}

/* El ancho mínimo de tarjeta es una variable para que la densidad la pueda
   cambiar. auto-fill se sigue encargando de cuántas caben: no hay una cuenta
   de columnas por breakpoint que mantener sincronizada. */
.grid-dev{display:grid;gap:var(--card-gap,16px);
  grid-template-columns:repeat(auto-fill,minmax(var(--card-min,252px),1fr))}
.dev{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-l);
  overflow:hidden;display:flex;flex-direction:column;cursor:pointer;
  transition:transform .18s cubic-bezier(.2,.7,.3,1), box-shadow .18s, border-color .18s}
.dev:hover{transform:translateY(-3px);box-shadow:var(--shadow-2);border-color:var(--line-2)}
.dev-stage{position:relative;height:196px;display:grid;place-items:center;
  background:radial-gradient(120% 90% at 50% 8%, var(--surface-2) 0%, var(--surface) 62%);
  border-bottom:1px solid var(--line)}
.dev-stage svg{height:172px;width:auto;filter:drop-shadow(0 10px 18px rgba(10,12,20,.20))}
.dev-badges{position:absolute;top:10px;left:10px;right:10px;display:flex;gap:6px;align-items:flex-start}
.dev-badges .right{margin-left:auto}
.dev-body{padding:13px 14px 14px;display:flex;flex-direction:column;gap:9px;flex:1}
.dev-brand{font-family:var(--font-cond);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-3)}
.dev-name{font-size:14.5px;font-weight:650;letter-spacing:-.01em;line-height:1.25}
.dev-specs{display:flex;flex-wrap:wrap;gap:5px}
.dev-price{margin-top:auto;padding-top:11px;border-top:1px dashed var(--line);
  display:flex;align-items:flex-end;justify-content:space-between;gap:8px}
.p-cash{font-family:var(--font-cond);font-size:12px;color:var(--ink-3);letter-spacing:.02em}
.p-cuota{font-family:var(--font-cond);font-size:23px;font-weight:700;line-height:1;color:var(--ink)}
.p-cuota i{font-style:normal;font-size:12px;color:var(--ink-3);font-weight:600}
.p-right{text-align:right}
.stock-line{display:flex;align-items:center;gap:5px;font-size:11.5px;color:var(--ink-3)}
.stock-dot{width:7px;height:7px;border-radius:50%;flex:none}
.cmp-tick{display:flex;align-items:center;gap:6px;font-size:11.5px;color:var(--ink-3);cursor:pointer}
.cmp-tick input{accent-color:var(--accent);width:14px;height:14px}

.swatches{display:flex;gap:5px}
.sw{width:15px;height:15px;border-radius:50%;border:1px solid var(--line-2);cursor:pointer;padding:0}
.sw.on{box-shadow:0 0 0 2px var(--surface),0 0 0 3.5px var(--accent)}

/* ============================================================
   CAJÓN FICHA TÉCNICA
   ============================================================ */
.scrim{position:fixed;inset:0;background:rgba(8,9,14,.42);backdrop-filter:blur(2px);
  z-index:60;opacity:0;pointer-events:none;transition:opacity .25s}
.scrim.on{opacity:1;pointer-events:auto}
.drawer{position:fixed;top:0;right:0;bottom:0;width:min(560px,100%);background:var(--surface);
  border-left:1px solid var(--line);z-index:61;transform:translateX(102%);
  transition:transform .3s cubic-bezier(.2,.75,.25,1);display:flex;flex-direction:column;
  box-shadow:var(--shadow-3)}
.drawer.on{transform:none}
/* Variante centrada: para FORMULARIOS de creación (orden, producto). El
   lateral queda para fichas y detalles; crear algo con muchos campos se
   siente mejor en un diálogo centrado y más ancho. */
.drawer.centrado{top:50%;left:50%;right:auto;bottom:auto;width:min(880px,94vw);
  max-height:92vh;border:1px solid var(--line);border-radius:14px;overflow:hidden;
  transform:translate(-50%,-46%) scale(.97);opacity:0;
  transition:transform .28s cubic-bezier(.2,.75,.25,1),opacity .22s}
.drawer.centrado.on{transform:translate(-50%,-50%);opacity:1}
/* Por encima de la ficha lateral del catálogo, que vive en la capa 61. */
.drawer.centrado{z-index:72}
.scrim.scrim-alto{z-index:71}
.drawer-h{display:flex;align-items:center;gap:12px;padding:14px 18px;border-bottom:1px solid var(--line)}
.drawer-body{overflow-y:auto;padding:18px;display:flex;flex-direction:column;gap:18px}
/* Las secciones NO se encogen: al ser un contenedor flexible, cuando el
   contenido no cabía el navegador las comprimía, y como .spec-group tiene
   overflow:hidden el resultado era una tarjeta cortada por la mitad — se
   veían las etiquetas de las características pero no sus valores. */
.drawer-body>*{flex-shrink:0}
.drawer-f{border-top:1px solid var(--line);padding:14px 18px;display:flex;gap:10px;background:var(--surface)}
.drawer-f .btn{flex:1;justify-content:center}
/* Manija de arrastre: solo existe (y solo se ve) en un .drawer.hoja bajo
   720px — ver el @media junto a .drawer.centrado más abajo. En escritorio
   opts.hoja se ve como un modal centrado normal, sin nada que arrastrar. */
.drawer-manija{display:none}

.hero-dev{display:grid;grid-template-columns:150px 1fr;gap:18px;align-items:center}
.hero-dev .stage{background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-m);
  height:210px;display:grid;place-items:center}
.hero-dev .stage svg{height:186px;filter:drop-shadow(0 10px 18px rgba(10,12,20,.22))}

.spec-group{border:1px solid var(--line);border-radius:var(--r-m);overflow:hidden}
.spec-group>h4{background:var(--surface-2);padding:8px 12px;font-family:var(--font-cond);
  font-size:11.5px;letter-spacing:.13em;text-transform:uppercase;color:var(--ink-2);font-weight:600}
/* ── Características en tarjetas ──────────────────────────────
   La lista de pares etiqueta/valor se apretaba y se leía como un
   bloque de texto. En tarjetas, cada dato se distingue de un vistazo
   y crece a dos o tres columnas según el ancho disponible. */
.spec-cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));
  gap:1px;background:var(--line)}
.spec-card{background:var(--surface);padding:10px 12px;min-width:0}
.spec-card .k{display:block;font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;
  color:var(--ink-3);font-family:var(--font-cond);margin-bottom:3px}
.spec-card .v{display:block;font-size:14px;font-weight:600;color:var(--ink);
  overflow-wrap:anywhere}

/* ── Colores agotados ─────────────────────────────────────────
   Un color sin existencias se muestra pero apagado y tachado: el
   cliente lo va a preguntar igual, y el asesor tiene que poder
   responder "ese no hay" sin salir de la pantalla. */
/* Un color sin unidades ya no se pinta como punto tachado (a 40 % y con una
   raya roja encima, a 15 px todos se veían rosados). En la tarjeta no se
   ofrece; en la ficha se nombra en texto con esta muestra al lado. La clase
   .agotado queda por si alguna vista vieja la usa: color real, aro apagado. */
.sw.agotado{opacity:.55;border-style:dashed}
.sw-mini{display:inline-block;width:9px;height:9px;border-radius:50%;
  border:1px solid var(--line-2);vertical-align:middle;margin:0 4px 1px 0}
.sw-lbl{display:block;font-size:10px;color:var(--ink-3);text-align:center;margin-top:2px;
  font-family:var(--font-cond)}

/* ── Disponibilidad por sede ──────────────────────────────────
   Resumen corto + botón que abre el buscador de sedes. Con quince
   ubicaciones, listarlas todas en la ficha era ruido. */
.dispo-top{display:flex;gap:10px;align-items:center;flex-wrap:wrap;padding:11px 12px}
.dispo-mia{display:flex;align-items:baseline;gap:7px;min-width:0}
.dispo-mia b{font-size:19px;font-family:var(--font-cond)}
.dispo-otras{border-top:1px solid var(--line);padding:9px 12px;display:flex;
  gap:8px;align-items:center;flex-wrap:wrap}

.sede-lista{display:flex;flex-direction:column;gap:1px;background:var(--line);
  border:1px solid var(--line);border-radius:var(--r-m);overflow:hidden;
  max-height:46vh;overflow-y:auto}
.sede-row{display:flex;gap:10px;align-items:center;padding:10px 12px;background:var(--surface)}
.sede-row.cero{opacity:.62}
.sede-row.mia{background:var(--accent-soft)}
.sede-row .nom{flex:1;min-width:0}
.sede-row .nom small{display:block;color:var(--ink-3);font-size:11px}
.sede-row .cant{font-family:var(--font-cond);font-size:16px;font-weight:700;
  min-width:34px;text-align:right}

.spec-row{display:grid;grid-template-columns:128px 1fr;gap:12px;padding:8px 12px;
  border-top:1px solid var(--line);font-size:12.5px}
.spec-row dt{color:var(--ink-3)}
.spec-row dd{margin:0;color:var(--ink);font-weight:500}

table.plan{width:100%;border-collapse:collapse;font-size:12.5px}
table.plan th{font-family:var(--font-cond);font-size:11px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--ink-3);text-align:right;padding:7px 10px;border-bottom:1px solid var(--line);font-weight:600}
table.plan th:first-child{text-align:left}
table.plan td{padding:8px 10px;border-bottom:1px solid var(--line);text-align:right;
  font-variant-numeric:tabular-nums}
table.plan td:first-child{text-align:left;font-weight:600}
table.plan tr:hover td{background:var(--surface-2)}
table.plan tr.best td{background:var(--accent-soft)}

/* ============================================================
   CLIENTE / SCORE
   ============================================================ */
.cli-layout{display:grid;grid-template-columns:300px 1fr;gap:16px;align-items:start}
.cli-list{display:flex;flex-direction:column;gap:8px}
/* Fila de la lista de Clientes — corta y todas con la misma forma a
   propósito (ver cliente.js:lista()): antes esto era PJ.tabla() con 6
   columnas, y tabla→tarjeta en móvil apilaba cada una en su propia línea
   (~380px de alto). Ciudad y fecha de registro ya se ven en el panel de
   "Ver" (ficha() más abajo) — no se repiten acá. */
.cli-item{display:flex;gap:11px;align-items:flex-start;padding:11px;border-radius:var(--radius-md);
  border:1px solid var(--border);background:var(--surface);margin-bottom:8px}
.cli-item.zero{opacity:.6}
.cli-av{width:36px;height:36px;border-radius:50%;display:grid;place-items:center;flex:none;
  margin-top:1px;font-weight:700;font-size:13px;background:var(--surface-3);color:var(--ink-2)}
/* Ventas, fila compacta de pantalla angosta (ventas.js:filaVentaMovil) —
   mismo espíritu que .cli-item: nunca más de 3 líneas por venta. */
.vta-dia-h{position:sticky;top:var(--nav-h,52px);background:var(--bg);z-index:2;
  padding:10px 2px 6px;font-size:11px;font-weight:700;letter-spacing:.05em;
  text-transform:uppercase;color:var(--text-tertiary)}
.vta-item{padding:11px;border-radius:var(--radius-md);border:1px solid var(--border);
  background:var(--surface);margin-bottom:8px;display:flex;flex-direction:column;gap:4px;cursor:pointer}
.vta-item-fila{display:flex;align-items:center;gap:8px}
.vta-item-fila:first-child{justify-content:space-between}
.vta-item-fila small{color:var(--ink-3);font-size:11.5px;flex:1;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.vta-item-total{margin-left:auto;flex:none;font-size:13.5px;font-weight:700;
  color:var(--ink);font-variant-numeric:tabular-nums}

/* Cada fila (nombre+fecha, documento/teléfono+acciones) reparte el espacio
   así: el texto variable se encoge con ellipsis, lo de ancho fijo (fecha,
   botones) nunca — por eso antes "Andrea Milena…" se cortaba tan pronto:
   todo vivía en una sola fila de 3 ítems y el bloque de 3 botones ganaba
   el pulso por espacio contra el nombre. */
.cli-item-cuerpo{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px;padding-top:2px}
.cli-item-fila{display:flex;align-items:center;gap:8px}
.cli-item-nombre{flex:1;min-width:0;font-size:13.5px;font-weight:600;line-height:1.3;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cli-item-fila small{color:var(--ink-3);font-size:11.5px;white-space:nowrap}
.cli-item-fila:first-child small{flex:none}
.cli-item-fila:last-child small{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis}
.cli-item-acc{flex:none;display:flex;gap:6px}
.cli-score{margin-left:auto;font-family:var(--font-cond);font-size:17px;font-weight:700;text-align:right}

.verdict{display:grid;grid-template-columns:auto 1fr auto;gap:18px;align-items:center;
  padding:18px;border-radius:var(--r-l);border:1px solid var(--line);background:var(--surface)}
.verdict.v-good{border-left:4px solid var(--good)}
.verdict.v-warn{border-left:4px solid var(--warn)}
.verdict.v-crit{border-left:4px solid var(--crit)}
.verdict h2{font-size:19px;font-weight:650;letter-spacing:-.015em;margin-bottom:3px}
.verdict p{margin:0;color:var(--ink-2);font-size:13px;max-width:62ch}

.tiles{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(158px,1fr))}
.tile{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-m);padding:13px 14px}
.tile .eyebrow{margin-bottom:6px}
.tile .v{font-family:var(--font-cond);font-size:26px;font-weight:700;line-height:1.05;letter-spacing:-.01em}
.tile .sub{font-size:11.5px;color:var(--ink-3);margin-top:3px}
.tile .v small{font-size:13px;color:var(--ink-3);font-weight:600}

.hm{display:flex;flex-direction:column;gap:9px}
.hm-row{display:flex;align-items:center;gap:8px}
.hm-year{font-family:var(--font-cond);font-size:11px;color:var(--ink-3);width:34px;letter-spacing:.06em}
.hm-cells{display:grid;grid-template-columns:repeat(12,1fr);gap:4px;flex:1}
.hm-c{aspect-ratio:1;border-radius:5px;display:grid;place-items:center;font-family:var(--font-cond);
  font-size:12px;font-weight:700;cursor:default;position:relative;color:#1A1206}
.hm-c[data-s="ok"]{background:var(--good);color:#fff}
.hm-c[data-s="tarde"]{background:var(--warn);color:#3A2800}
.hm-c[data-s="mora"]{background:var(--crit);color:#fff}
.hm-c[data-s="na"]{background:var(--surface-2);color:var(--ink-3);border:1px dashed var(--line)}
.hm-c.corte{outline:2px solid var(--ink);outline-offset:-2px}
.hm-months{display:grid;grid-template-columns:repeat(12,1fr);gap:4px;flex:1}
.hm-months span{text-align:center;font-family:var(--font-cond);font-size:10px;color:var(--ink-3)}
.legend{display:flex;flex-wrap:wrap;gap:14px;font-size:11.5px;color:var(--ink-2);align-items:center}
.legend i{width:13px;height:13px;border-radius:4px;display:inline-grid;place-items:center;
  font-style:normal;font-size:9px;font-weight:700;margin-right:5px;vertical-align:-2px}

.factors{display:flex;flex-direction:column;gap:11px}
.factor{display:grid;grid-template-columns:1fr 46px;gap:10px;align-items:center}
.factor .fl{font-size:12.5px}
.factor .fl small{display:block;color:var(--ink-3);font-size:11px}
.fbar{height:7px;border-radius:4px;background:var(--surface-3);overflow:hidden;margin-top:5px}
.fbar i{display:block;height:100%;border-radius:4px;background:var(--series-1)}
.factor .fv{font-family:var(--font-cond);font-size:13.5px;font-weight:700;text-align:right;
  font-variant-numeric:tabular-nums}

.alert{display:flex;gap:9px;padding:10px 12px;border-radius:var(--r-m);font-size:12.5px;
  border:1px solid var(--line);background:var(--surface-2);align-items:flex-start}
.alert svg{width:16px;height:16px;flex:none;margin-top:1px;stroke-width:1.8}
.alert.a-crit{background:var(--crit-soft);border-color:transparent;color:var(--danger-text)}
.alert.a-warn{background:var(--warn-soft);border-color:transparent;color:var(--warning-text)}
.alert.a-good{background:var(--good-soft);border-color:transparent;color:var(--success-text)}

/* ============================================================
   SIMULADOR
   ============================================================ */
.sim-layout{display:grid;grid-template-columns:1fr 380px;gap:16px;align-items:start}
.slider-row{margin-bottom:18px}
.slider-row .top{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:8px}
.slider-row .top label{font-size:12.5px;color:var(--ink-2)}
.slider-row .top b{font-family:var(--font-cond);font-size:17px;font-weight:700}
input[type=range]{width:100%;accent-color:var(--accent);height:22px}
.seg{display:flex;gap:4px;background:var(--surface-2);border-radius:11px;padding:4px;
  border:1px solid var(--line)}
.seg button{flex:1;border:0;background:none;border-radius:8px;padding:7px 6px;font-size:12.5px;
  cursor:pointer;color:var(--ink-2);font-weight:600}
.seg button.on{background:var(--surface);color:var(--ink);box-shadow:var(--shadow-1)}

.quote{background:var(--ink);color:var(--surface);border-radius:var(--r-l);padding:20px;
  display:flex;flex-direction:column;gap:14px}
.quote .eyebrow{color:rgba(255,255,255,.55)}
:root[data-theme="dark"] .quote{background:var(--surface-3)}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]) .quote{background:var(--surface-3)}}
.quote-big{font-family:var(--font-cond);font-size:44px;font-weight:700;line-height:1;letter-spacing:-.02em}
.quote-big i{font-style:normal;font-size:15px;opacity:.6;font-weight:600}
.quote-rows{display:flex;flex-direction:column;gap:8px;font-size:12.5px}
.quote-rows div{display:flex;justify-content:space-between;gap:12px}
.quote-rows span{opacity:.62}
.quote-rows b{font-family:var(--font-cond);font-variant-numeric:tabular-nums;font-weight:600;font-size:13.5px}
.quote hr{border:0;border-top:1px solid rgba(255,255,255,.16);margin:0}

/* ============================================================
   TABLERO
   ============================================================ */
.dash-grid{display:grid;gap:16px;grid-template-columns:repeat(12,1fr)}
.c4{grid-column:span 4}.c6{grid-column:span 6}.c8{grid-column:span 8}.c12{grid-column:span 12}

.rank{display:flex;flex-direction:column;gap:12px}
.rank-row{display:grid;grid-template-columns:22px 1fr 74px;gap:11px;align-items:center;font-size:12.5px}
.rank-row .pos{font-family:var(--font-cond);font-size:12px;color:var(--ink-3);text-align:center}
.rank-row .bar{height:7px;border-radius:4px;background:var(--surface-3);margin-top:5px;overflow:hidden}
.rank-row .bar i{display:block;height:100%;background:var(--series-1);border-radius:4px}
.rank-row .val{font-family:var(--font-cond);font-weight:700;text-align:right;font-variant-numeric:tabular-nums}

/* tooltip de gráficos */
.tt{position:fixed;z-index:90;pointer-events:none;background:var(--ink);color:var(--surface);
  border-radius:8px;padding:7px 10px;font-size:11.5px;box-shadow:var(--shadow-2);
  opacity:0;transition:opacity .12s;white-space:nowrap}
.tt.on{opacity:1}
.tt b{font-family:var(--font-cond);font-size:13px;display:block}
:root[data-theme="dark"] .tt{background:var(--surface-3);color:var(--ink);border:1px solid var(--line)}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]) .tt{background:var(--surface-3);color:var(--ink);border:1px solid var(--line)}}

/* barra comparador */
.cmpbar{position:fixed;left:50%;bottom:18px;transform:translate(-50%,140%);z-index:55;
  background:var(--ink);color:var(--surface);border-radius:14px;padding:10px 12px 10px 16px;
  display:flex;align-items:center;gap:14px;box-shadow:var(--shadow-3);transition:transform .28s cubic-bezier(.2,.75,.25,1)}
.cmpbar.on{transform:translate(-50%,0)}
.cmpbar b{font-family:var(--font-cond);font-size:13px}
:root[data-theme="dark"] .cmpbar{background:var(--surface-3);color:var(--ink);border:1px solid var(--line)}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]) .cmpbar{background:var(--surface-3);color:var(--ink);border:1px solid var(--line)}}

/* modal comparar */
.modal{position:fixed;inset:0;z-index:70;display:none;place-items:center;padding:24px}
.modal.on{display:grid}
.modal-box{background:var(--surface);border-radius:var(--r-xl);border:1px solid var(--line);
  box-shadow:var(--shadow-3);max-width:900px;width:100%;max-height:86vh;overflow:auto}

.tblwrap{overflow-x:auto}
table.cmp{width:100%;border-collapse:collapse;font-size:12.5px;min-width:560px}
table.cmp th,table.cmp td{padding:9px 12px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}
table.cmp th{font-family:var(--font-cond);font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-3)}
table.cmp td.k{color:var(--ink-3);width:150px}
table.cmp td.win{font-weight:700;color:var(--accent)}

.note{font-size:11.5px;color:var(--ink-3);line-height:1.55}
.two{display:grid;grid-template-columns:1fr 1fr;gap:16px}
.split{display:grid;grid-template-columns:minmax(0,300px) 1fr;gap:16px;align-items:start}

/* ============================================================
   INVENTARIO / OPERACIÓN
   ============================================================ */

.tabs{display:flex;gap:2px;border-bottom:1px solid var(--line);margin-bottom:16px;overflow-x:auto}
.tabs button{border:0;background:none;padding:10px 14px;font-size:13px;font-weight:600;
  color:var(--ink-3);cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-1px;white-space:nowrap}
.tabs button:hover{color:var(--ink-2)}
.tabs button.on{color:var(--ink);border-bottom-color:var(--accent)}

table.dt{width:100%;border-collapse:collapse;font-size:12.5px}
table.dt th{font-family:var(--font-cond);font-size:11px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--ink-3);text-align:left;padding:9px 11px;border-bottom:1px solid var(--line);
  font-weight:600;white-space:nowrap;position:sticky;top:0;background:var(--surface);z-index:2}
table.dt td{padding:9px 11px;border-bottom:1px solid var(--line);vertical-align:middle}
table.dt td a{color:inherit;text-decoration:none}
table.dt td a:hover{color:var(--accent)}
table.dt td.n{text-align:right;font-family:var(--font-cond);font-variant-numeric:tabular-nums;font-weight:600}
table.dt th.n{text-align:right}
table.dt tbody tr:hover td{background:var(--surface-2)}
table.dt tr.zero td.n{color:var(--ink-3);font-weight:400}
.scroller{max-height:520px;overflow:auto;border-radius:var(--r-m)}

.tag{display:inline-flex;align-items:center;gap:5px;border-radius:6px;padding:2px 7px;
  font-family:var(--font-cond);font-size:11px;letter-spacing:.05em;text-transform:uppercase;font-weight:600;
  border:1px solid transparent;white-space:nowrap}
/* Contador dentro de un botón: píldora centrada por caja, no por línea base --
   la fuente condensada del .tag no comparte métrica con la del botón. */
.btn .tag{padding:0 6px;min-width:20px;height:20px;justify-content:center;border-radius:999px;
  background:var(--surface-3);font:600 11.5px var(--font-mono);letter-spacing:0;text-transform:none}
.t-in{background:var(--good-soft);color:var(--success-text)}
.t-out{background:var(--crit-soft);color:var(--danger-text)}
.t-mov{background:var(--accent-soft);color:var(--accent)}
.t-adj{background:var(--warn-soft);color:var(--warning-text)}
.t-neu{background:var(--surface-3);color:var(--ink-2)}

.locked{display:flex;flex-direction:column;align-items:center;gap:10px;padding:44px 20px;
  text-align:center;color:var(--ink-3);border:1px dashed var(--line-2);border-radius:var(--r-l)}
.locked svg{width:28px;height:28px;stroke-width:1.5;color:var(--ink-3)}
.locked b{color:var(--ink);font-size:14px}
.mask{color:var(--ink-3);letter-spacing:.14em;font-family:var(--font-mono);font-size:11px}

.tl{display:flex;flex-direction:column;gap:0;position:relative;padding-left:6px}
.tl-item{display:grid;grid-template-columns:22px 1fr;gap:13px;padding-bottom:18px;position:relative}
.tl-item:not(:last-child)::before{content:"";position:absolute;left:10.5px;top:20px;bottom:0;
  width:1px;background:var(--line-2)}
.tl-dot{width:21px;height:21px;border-radius:50%;display:grid;place-items:center;
  background:var(--surface-3);color:var(--ink-2);z-index:1}
.tl-dot svg{width:12px;height:12px;stroke-width:2.2}
.tl-dot.acc{background:var(--accent);color:var(--accent-on)}
.tl-dot.good{background:var(--good);color:#fff}
.tl-dot.crit{background:var(--crit);color:#fff}
.tl-item b{font-size:13px;font-weight:600;display:block;line-height:1.35}
.tl-item small{color:var(--ink-3);font-size:11.5px;display:block;margin-top:2px}

/* dropzone de fotos */
.dz{border:2px dashed var(--line-2);border-radius:var(--r-l);padding:26px;text-align:center;
  color:var(--ink-3);cursor:pointer;transition:border-color .15s,background .15s}
.dz:hover,.dz.over{border-color:var(--accent);background:var(--accent-soft);color:var(--accent)}
.dz svg{width:26px;height:26px;stroke-width:1.6;margin-bottom:7px}
.dz b{display:block;color:var(--ink);font-size:13.5px;margin-bottom:3px}
.dz.over b{color:var(--accent)}
.photos{display:grid;grid-template-columns:repeat(auto-fill,minmax(104px,1fr));gap:10px}
.ph{position:relative;aspect-ratio:3/4;border-radius:var(--r-m);border:1px solid var(--line);
  overflow:hidden;background:var(--surface-2);display:grid;place-items:center}
.ph img{width:100%;height:100%;object-fit:cover}
.ph svg{height:82%;width:auto}
.ph .ph-tools{position:absolute;inset:auto 0 0 0;display:flex;gap:4px;padding:5px;
  background:linear-gradient(transparent,rgba(8,9,14,.72));opacity:0;transition:opacity .15s}
.ph:hover .ph-tools{opacity:1}
.ph-tools button{flex:1;border:0;border-radius:5px;background:rgba(255,255,255,.9);color:#14161F;
  font-size:10.5px;padding:4px 2px;cursor:pointer;font-weight:600}
.ph-main{position:absolute;top:6px;left:6px;background:var(--accent);color:var(--accent-on);
  font-family:var(--font-cond);font-size:10px;letter-spacing:.08em;text-transform:uppercase;
  padding:2px 6px;border-radius:5px;font-weight:700}
.ph-prov{position:absolute;top:6px;right:6px;background:var(--warn);color:#3A2800;
  font-family:var(--font-cond);font-size:9.5px;letter-spacing:.06em;text-transform:uppercase;
  padding:2px 5px;border-radius:5px;font-weight:700}

/* matriz de permisos */
table.perm{width:100%;border-collapse:collapse;font-size:12.5px;min-width:660px}
table.perm th{font-family:var(--font-cond);font-size:11px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--ink-3);padding:10px 8px;border-bottom:1px solid var(--line);text-align:center;font-weight:600}
table.perm th:first-child{text-align:left;width:230px}
table.perm td{padding:9px 8px;border-bottom:1px solid var(--line);text-align:center}
table.perm td:first-child{text-align:left;color:var(--ink-2)}
table.perm tr:hover td{background:var(--surface-2)}
table.perm td.on{color:var(--success-text);font-weight:700}
table.perm td.off{color:var(--ink-3)}
table.perm col.hi{background:var(--accent-soft)}

.rolecard{border:1px solid var(--line);border-radius:var(--r-m);padding:13px;background:var(--surface)}
.rolecard.on{border-color:var(--accent);background:var(--accent-soft)}
.rolecard b{font-size:13px;display:block}
.rolecard small{color:var(--ink-3);font-size:11.5px;display:block;margin-top:3px;line-height:1.5}

.rep-card{border:1px solid var(--line);border-radius:var(--r-m);padding:14px;background:var(--surface);
  cursor:pointer;text-align:left;transition:border-color .15s,transform .15s,box-shadow .15s;width:100%}
.rep-card:hover{border-color:var(--line-2);transform:translateY(-2px);box-shadow:var(--shadow-1)}
.rep-card.on{border-color:var(--accent);background:var(--accent-soft)}
.rep-card b{display:block;font-size:13px;margin-bottom:3px}
.rep-card small{color:var(--ink-3);font-size:11.5px;line-height:1.5}
.rep-ic{width:30px;height:30px;border-radius:9px;background:var(--surface-3);display:grid;
  place-items:center;margin-bottom:9px;color:var(--ink-2)}
.rep-ic svg{width:16px;height:16px;stroke-width:1.7}
/* La portada de informes: tarjetas en cuadrícula por categoría. Escala a
   veinte informes sin volverse una columna infinita. */
.rep-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:10px}
.rep-cat{margin:22px 0 10px 2px;display:flex;align-items:center;gap:10px}
.rep-cat .eyebrow{margin:0}
.rep-cat::after{content:'';flex:1;height:1px;background:var(--line)}
.rep-head{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:14px}
.rep-head h3{margin:0;font-size:17px}
.rep-head .note{margin:2px 0 0}
/* Barra de paginación — la misma en todas las listas del módulo. */
.pager{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-top:12px;
  padding-top:12px;border-top:1px solid var(--line)}
.pager .note{margin:0}
.pager button[disabled]{opacity:.45;cursor:default}

.kv{display:flex;justify-content:space-between;gap:12px;padding:7px 0;border-bottom:1px solid var(--line);font-size:12.5px}
.kv:last-child{border-bottom:0}
.kv span{color:var(--ink-3)}
.kv b{font-weight:600;text-align:right}

.toolbar{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-bottom:14px}
.toolbar .right{margin-left:auto;display:flex;gap:8px;align-items:center}

.sla{display:inline-flex;align-items:center;gap:6px;font-family:var(--font-cond);font-weight:700;
  font-variant-numeric:tabular-nums}
.sla i{width:7px;height:7px;border-radius:50%;display:inline-block}

/* ============================================================
   FACTURACIÓN
   ============================================================ */
.flow{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}
.flowc{border:1px solid var(--line);border-radius:var(--r-l);background:var(--surface);
  padding:16px;display:flex;flex-direction:column;gap:11px;position:relative}
.flowc.hi{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}
.flowc .when{font-family:var(--font-cond);font-size:11px;letter-spacing:.13em;text-transform:uppercase;
  color:var(--accent);font-weight:700}
.flowc h4{font-size:14.5px;font-weight:650;letter-spacing:-.01em}
.flowc .who{font-size:11.5px;color:var(--ink-3)}
.money{display:flex;justify-content:space-between;gap:10px;font-size:12.5px;
  padding:7px 10px;border-radius:8px;background:var(--surface-2)}
.money b{font-family:var(--font-cond);font-variant-numeric:tabular-nums;font-weight:700}
.money.pos{background:var(--good-soft);color:var(--success-text)}
.money.neu{background:var(--accent-soft);color:var(--accent)}
.money.off{background:var(--surface-2);color:var(--ink-3)}

table.led{width:100%;border-collapse:collapse;font-size:12.5px;min-width:460px}
table.led th{font-family:var(--font-cond);font-size:11px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--ink-3);padding:8px 10px;border-bottom:1px solid var(--line);text-align:right;font-weight:600}
table.led th:first-child,table.led th:nth-child(2){text-align:left}
table.led td{padding:7px 10px;border-bottom:1px solid var(--line);text-align:right;
  font-variant-numeric:tabular-nums;font-family:var(--font-cond);font-weight:600}
table.led td:first-child,table.led td:nth-child(2){text-align:left;font-family:var(--font-ui);font-weight:400}
table.led td:first-child{color:var(--ink-3);font-size:11.5px;font-family:var(--font-mono)}
table.led tr.tot td{border-top:2px solid var(--line-2);border-bottom:0;font-weight:700}

.giro{border:1px solid var(--line);border-radius:var(--r-m);overflow:hidden;margin-bottom:10px}
.giro-h{display:grid;grid-template-columns:auto 1fr auto;gap:14px;align-items:center;
  padding:13px 15px;cursor:pointer;background:var(--surface)}
.giro-h:hover{background:var(--surface-2)}
.giro-h .cev{transition:transform .2s;color:var(--ink-3)}
.giro.open .cev{transform:rotate(90deg)}
.giro-b{display:none;border-top:1px solid var(--line);background:var(--surface-2)}
.giro.open .giro-b{display:block}

@media (max-width:900px){ .flow{grid-template-columns:1fr} }

@media (max-width:1180px){
  .cli-layout{grid-template-columns:1fr}
  .two,.split{grid-template-columns:1fr}
  .sim-layout{grid-template-columns:1fr}
  .c4,.c6,.c8{grid-column:span 12}
}
@media (max-width:760px){
  /* El riel ya se volvió cajón en la regla de 900 px; acá solo se aprieta lo
     que queda en la barra superior. */
  .navbar{padding:0 12px;gap:6px}
  .asesor div:last-child{display:none}
  .asesor{padding-left:8px;border:0}
  .crumb span{display:none}
  .view{padding:14px}
  .hero-dev{grid-template-columns:1fr}
}
@media (max-width:480px){
  /* A este ancho, avatar + nombre ya no caben junto a los tres icon-btn y el
     título de la vista sin desbordar. Solo queda el avatar. */
  .asesor > div:not(.avatar){display:none}
  .asesor{padding-left:6px;gap:0}

  /* Y con la marca sumada a la izquierda, un título como «Catálogo de
     equipos» (147px) se quedaba sin los últimos píxeles y salía con puntos
     suspensivos. Los 12px del margen lateral son aire que a este ancho no
     usa nadie.
     Pero lo que de verdad lo apretaba era el separador flexible: con flex:1
     se quedaba TODO el sobrante antes de que el título pudiera usarlo. Acá
     sobra tan poco que no hay nada que separar — el título crece y empuja
     los botones a la derecha él solo. */
  .navbar{padding:0 14px}
  .nav-spacer{display:none}
  .crumb{flex:1}
}
/* El riel se ensancha con :hover para previsualizar sin comprometerse a abrir.
   En táctil no hay "pasar el cursor": un toque deja el hover pegado y el riel
   queda abierto encima del contenido hasta el próximo toque en otro lado. */
@media (hover:none){
  .rail:hover{width:60px;z-index:45;box-shadow:none;transition-delay:0s}
}

/* ============================================================
   AÑADIDOS DE LA APLICACIÓN
   Todo lo anterior es la maqueta tal cual. De aquí en adelante,
   lo que necesita el módulo real y la maqueta no traía.
   ============================================================ */

/* ── Estados de carga, vacío y tonos de tabla.dt ──────────── */
/* Estados de carga, vacío y error dentro de una vista */
.empty{padding:34px 20px;text-align:center;color:var(--ink-3);font-size:13px;
  background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-md)}
.empty b{display:block;color:var(--ink);font-size:14px;margin-bottom:4px}
/* Variante con ícono/acción de PJ.emptyBox({tipo,titulo,texto,accion}) */
.empty-v{display:flex;flex-direction:column;align-items:center;gap:4px}
.empty-ico{width:40px;height:40px;color:var(--text-tertiary);margin-bottom:6px}
.empty-v p{margin:0;color:var(--ink-3)}
.empty-v [data-empty-accion]{margin-top:10px}

/* Tono del tile según lo que comunica.
   't-mute' es el caso de "PayJoy por cobrar": informativo, NO entra al arqueo. */
.tile.t-ok   .v{color:var(--success-text)}
.tile.t-warn .v{color:var(--warning-text)}
.tile.t-bad  .v{color:var(--danger-text)}
.tile.t-mute{background:var(--surface-2);border-style:dashed}
.tile.t-mute .v{color:var(--ink-3)}

/* Totales al pie de una tabla */
table.dt tfoot td{padding:10px 11px;border-top:2px solid var(--line-2);border-bottom:0;
  font-weight:700;background:var(--surface-2)}
table.dt tfoot td.n{font-family:var(--font-cond);text-align:right;font-variant-numeric:tabular-nums}

/* Fila de un movimiento reversado: la plata entró y salió, se ve en negativo */
table.dt tr.reversado td{color:var(--danger-text);background:var(--crit-soft)}
table.dt tr.reversado td.n{font-weight:700}

/* Fila vencida — desembolso que PayJoy no ha girado dentro del plazo */
table.dt tr.vencida td.n{color:var(--danger-text);font-weight:700}
table.dt tr.reclamar td{background:var(--crit-soft)}

/* ── Encabezado de sección, score de cliente y formularios ── */
/* Encabezado de sección dentro de una vista con pestañas */
.sec-h{display:flex;align-items:baseline;gap:10px;margin:22px 0 10px}
.sec-h h4{font-size:13.5px;font-weight:650;margin:0}
.sec-h .eyebrow{margin:0}

/* Medidor de score + datos, lado a lado */
.score-top{display:flex;gap:20px;align-items:center;flex-wrap:wrap}

/* Insignia de puntaje en la lista de clientes */
.cli-score{margin-left:auto;font-family:var(--font-cond);font-size:13px;font-weight:700;
  border-radius:7px;padding:3px 8px;flex:none}
.sc-exc{background:var(--good-soft);color:var(--success-text)}
.sc-bue{background:var(--accent-soft);color:var(--accent)}
.sc-acc{background:var(--warn-soft);color:#8A5D00}
.sc-rie{background:var(--crit-soft);color:var(--danger-text)}

/* Formularios de captura (cliente externo, ficha de producto) */
.form-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(210px,1fr))}
.field{display:flex;flex-direction:column;gap:5px}
.field .eyebrow{margin:0}
.field input,.field select,.field textarea{width:100%}
/* Fila con casilla o radio: .field estira los inputs al 100% y una casilla
   estirada descuadra toda la fila. Esta clase es para listas marcables. */
.check-row{display:flex;gap:10px;align-items:flex-start;cursor:pointer}
.check-row input[type=checkbox],.check-row input[type=radio]{
  width:auto;flex:none;margin-top:3px;accent-color:var(--accent)}
.field textarea{min-height:76px;resize:vertical;border-radius:10px;padding:9px 11px;
  background:var(--surface-2);border:1px solid var(--line)}

/* ── Escaneo y recepción de unidades (Ventas) ─────────────── */
/* Escanear la unidad que se entrega (Ventas).
   El campo es grande y con borde de acento a propósito: es la primera acción
   de la pantalla, no un buscador secundario. La pistola escribe aquí. */
.scan-uni{position:relative}
.scan-uni input{width:100%;padding:15px 14px 15px 46px;font-family:var(--font-mono,monospace);
  font-size:18px;letter-spacing:1px;border:2px solid var(--accent);border-radius:11px;
  background:var(--surface);color:var(--ink);outline:none}
.scan-uni input::placeholder{font-family:inherit;font-size:13.5px;letter-spacing:0;color:var(--ink-3)}
.scan-uni .scan-ico{position:absolute;left:14px;top:50%;transform:translateY(-50%);
  width:20px;height:20px;color:var(--ink-3);pointer-events:none}

.uni-fila{display:flex;align-items:center;gap:11px;padding:10px 13px;border:1px solid var(--line);
  border-radius:10px;margin-bottom:6px;background:var(--surface);cursor:pointer}
.uni-fila:hover{border-color:var(--accent)}
.uni-fila.on{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent) inset}
.uni-imei{font-family:var(--font-mono,monospace);font-size:15px;letter-spacing:.5px}

.btn-mas{display:none;width:100%;margin-top:2px;padding:9px;border:1px dashed var(--line);
  border-radius:10px;background:none;color:var(--ink-2);font-size:13px;cursor:pointer}
.btn-mas:hover{border-color:var(--accent);color:var(--accent)}

/* ── Historial de pagos (heatmap) y carga de fotos ────────── */
/* Pie del historial de pago: resumen + convenciones */
.hm-foot{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-top:12px;
  font-size:12px;color:var(--ink-2)}
.hm-leg{display:flex;gap:12px;flex-wrap:wrap;margin-left:auto;color:var(--ink-3);font-size:11.5px}
.hm-leg span{display:inline-flex;align-items:center;gap:5px}
.hm-leg .hm-c{width:12px;height:12px;aspect-ratio:auto;flex:none;border-radius:3px}

/* Zona de carga de fotos de producto */
.dropzone{display:flex;flex-direction:column;align-items:center;gap:5px;padding:26px 18px;
  border:1.5px dashed var(--line-2);border-radius:var(--r-m);background:var(--surface-2);
  cursor:pointer;text-align:center;transition:border-color .15s,background .15s}
.dropzone:hover,.dropzone.on{border-color:var(--accent);background:var(--accent-soft)}
.dropzone b{font-size:13.5px}

/* ── Promociones con vigencia ─────────────────────────────── */
.promo-flag{position:absolute;top:10px;right:10px;z-index:2;
  background:var(--crit);color:#fff;border-radius:999px;padding:4px 10px;
  font-family:var(--font-cond);font-size:12px;font-weight:700;letter-spacing:.02em;
  box-shadow:var(--shadow-1)}

.promo{margin-top:10px;padding:9px 11px;border-radius:var(--r-m);
  background:var(--crit-soft);border:1px solid rgba(208,59,59,.22)}
.promo-top{display:flex;align-items:center;gap:8px}
.promo-antes{color:var(--ink-3);text-decoration:line-through;font-size:12px}
.promo-pct{margin-left:auto;font-family:var(--font-cond);font-weight:700;font-size:12px;
  color:var(--danger-text)}
.promo-hoy{font-size:13px;margin-top:2px}
.promo-hoy b{font-family:var(--font-cond);font-size:18px;font-weight:700;color:var(--danger-text)}
.promo-pie{display:flex;align-items:center;gap:8px;margin-top:4px;
  font-size:11.5px;color:var(--ink-2)}
.promo-reloj{margin-left:auto;display:inline-flex;align-items:center;gap:5px;
  font-family:var(--font-cond);letter-spacing:.03em}
.promo-reloj::before{content:'';width:6px;height:6px;border-radius:50%;background:var(--ink-3)}
.promo-reloj.urge{color:var(--danger-text);font-weight:700}
.promo-reloj.urge::before{background:var(--crit);animation:pulso 1.6s ease-in-out infinite}
@keyframes pulso{0%,100%{opacity:1}50%{opacity:.25}}

/* La tarjeta necesita ser el ancla del distintivo de descuento */
.dev-stage{position:relative}

/* ════════════════════════════════════════════════════════════════════
   Galería de producto con lupa
   --------------------------------------------------------------------
   Imita lo que hacen las tiendas grandes: imagen grande, tira de
   miniaturas, y al pasar el mouse una lente que amplía la zona.

   La lupa usa background-position sobre la MISMA imagen a mayor escala.
   No se carga un segundo archivo: la foto original ya viene a 1000 px y
   ampliar 2,5× sobre ella se ve nítido sin pedir nada más al servidor.
   ════════════════════════════════════════════════════════════════════ */

/* Una sola columna: la foto grande arriba y la tira de miniaturas debajo.
   Antes las miniaturas iban en una columna de 76 px a la izquierda; en el
   cajón de 560 px eso dejaba la foto grande en ~180 px con un tercio del
   ancho vacío al lado. La foto tiene que ocupar todo el ancho disponible. */
.gal{display:grid;grid-template-columns:1fr;gap:12px;width:100%}

/* ── El escenario ───────────────────────────────────────────────────
   Cuadrado y a todo el ancho: las fotos de producto son cuadradas (1000×1000)
   y así la foto llena el escenario sin franjas. El fondo es el mismo halo
   radial de las tarjetas para que ficha y catálogo se sientan de la misma
   familia. El tope de alto es para monitores grandes, donde un cajón de
   560 px daría un cuadrado de 520 y empujaría el precio fuera de la vista. */
/* El marco: todo el ancho, cuadrado, con tope de alto para monitores grandes.
   Por dentro NO es un lienzo con cosas flotando encima: es una rejilla de
   zonas —contador arriba, flechas a los lados, «Ampliar» abajo— y la foto
   vive en la celda del centro (.gal-foto), que se ajusta sola al espacio que
   dejan las demás. Así la foto nunca queda detrás de un botón ni de una
   pastilla, y si un día se agrega otra cosa al marco, la foto cede espacio
   en vez de taparse. */
.gal-main{position:relative;width:100%;aspect-ratio:1/1;max-height:460px;
  border-radius:var(--r-xl);overflow:hidden;cursor:zoom-in;
  display:grid;grid-template-rows:auto 1fr auto;grid-template-columns:auto 1fr auto;
  gap:6px 8px;padding:10px;
  background:radial-gradient(120% 90% at 50% 8%, var(--surface-2) 0%, var(--surface) 68%);
  border:1px solid var(--line);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.55), var(--shadow-1);
  transition:box-shadow .2s ease,border-color .2s ease}
.gal-main:hover{border-color:var(--line-2);box-shadow:inset 0 1px 0 rgba(255,255,255,.55),var(--shadow-2)}

/* La celda de la foto: ocupa lo que queda y la imagen se acomoda dentro por
   posición absoluta —la única forma de que "100 %" signifique siempre "el
   tamaño de esta celda" sin depender de cómo el navegador resuelva alturas
   derivadas de aspect-ratio. */
.gal-foto{grid-row:2;grid-column:2;position:relative;min-width:0;min-height:0;
  border-radius:var(--r-m);overflow:hidden}
.gal-foto img{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;display:block;
  transition:transform .35s cubic-bezier(.2,.7,.3,1)}
.gal-main:hover .gal-foto img{transform:scale(1.03)}

/* Flechas en sus columnas laterales: reservan sitio aunque estén ocultas,
   por eso la foto no se mete debajo. Aparecen al pasar el mouse; en táctil,
   siempre. */
.gal-nav{grid-row:2;align-self:center;
  width:38px;height:38px;border-radius:50%;padding:0;cursor:pointer;
  background:var(--surface);border:1px solid var(--line);box-shadow:var(--shadow-2);
  color:var(--ink);display:grid;place-items:center;
  opacity:0;transition:opacity .15s,transform .15s,background .15s}
.gal-nav svg{width:18px;height:18px}
.gal-nav.prev{grid-column:1}
.gal-nav.next{grid-column:3}
.gal-main:hover .gal-nav{opacity:1}
.gal-nav:hover{background:var(--surface-2);transform:scale(1.06)}
@media (hover:none){
  .gal-nav{opacity:.95;width:44px;height:44px}
  /* Sin cursor que pase por encima, poner portada / eliminar / reordenar
     no tenía forma de activarse -- quedaban ahí pero inalcanzables. */
  .ph .ph-tools{opacity:1}
}

/* Valor de la barra tocada, del gráfico del tablero. Alto reservado incluso
   vacío para que nada salte cuando aparece el primer texto. */
.chart-tip{min-height:18px;margin:6px 2px 0;font-size:12px;color:var(--ink-2);
  font-family:var(--font-mono)}

/* Contador (fila de arriba, izquierda) y «Ampliar» (fila de abajo, derecha),
   en la misma pastilla para que se lean como un solo sistema. */
.gal-cont,.gal-hint{display:inline-flex;align-items:center;gap:5px;
  font-family:var(--font-cond);font-size:11px;letter-spacing:.06em;text-transform:uppercase;
  color:var(--ink-2);background:var(--surface);border:1px solid var(--line);
  border-radius:999px;padding:5px 10px;box-shadow:var(--shadow-1);pointer-events:none}
.gal-cont{grid-row:1;grid-column:1 / -1;justify-self:start;font-weight:600}
.gal-hint{grid-row:3;grid-column:1 / -1;justify-self:end;opacity:.92;transition:opacity .15s}
.gal-hint svg{width:12px;height:12px}
.gal-main:hover .gal-hint{opacity:1}

/* ── La tira de miniaturas ──────────────────────────────────────────
   Debajo, del ancho del escenario, con desplazamiento a saltos. La activa
   lleva el aro del acento, y las demás se atenúan un poco para que se sepa
   cuál se está viendo sin tener que compararlas. */
.gal-tiras{display:flex;flex-direction:row;gap:10px;overflow-x:auto;padding:3px 3px 6px;
  justify-content:safe center;scroll-snap-type:x mandatory;scrollbar-width:thin}
.gal-tira{width:76px;height:76px;flex:none;border-radius:14px;overflow:hidden;padding:6px;
  scroll-snap-align:start;cursor:pointer;
  background:var(--surface-2);border:2px solid var(--line);opacity:.72;
  transition:border-color .15s ease,opacity .15s ease,transform .15s ease,box-shadow .15s ease}
.gal-tira img{width:100%;height:100%;object-fit:contain;display:block}
.gal-tira:hover{opacity:1;border-color:var(--line-2);transform:translateY(-1px)}
.gal-tira.on{opacity:1;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}

/* La lente circular de otra época: ya no se usa. */
.gal-lupa{display:none}

/* ── Visor a pantalla completa ──────────────────────────────────────
   Tres filas: cabecera (equipo, contador, cerrar) · la foto · miniaturas.
   La foto va sobre un panel claro SIEMPRE: las fotos llegan mezcladas (PNG
   transparente y JPG con fondo blanco) y sobre el fondo oscuro un JPG se
   veía como un cartón pegado. Con el panel, las dos se ven iguales. */
.visor{position:fixed;inset:0;z-index:90;background:rgba(8,9,13,.94);
  display:none;grid-template-rows:auto 1fr auto;gap:14px;padding:16px 22px 18px}
.visor.on{display:grid}
.visor-cab{display:flex;align-items:center;justify-content:space-between;gap:12px;color:#fff}
.visor-tit{font-size:15px;font-weight:650;letter-spacing:-.01em}
.visor-pos{margin-left:10px;font-family:var(--font-cond);font-size:12px;
  letter-spacing:.04em;color:rgba(255,255,255,.62)}
.visor-cuerpo{display:grid;grid-template-columns:auto 1fr auto;align-items:center;
  gap:14px;min-height:0}
/* El panel es un cuadrado del alto disponible, igual que el marco de la
   ficha: antes era un rectángulo ancho con la foto flotando en el medio. Con
   height definido (la fila 1fr de una rejilla a pantalla completa) el ancho
   sale del aspect-ratio y el panel queda centrado y del tamaño justo. La
   foto se acomoda dentro por posición absoluta, con su margen, por la misma
   razón que en la ficha: "100 %" tiene que ser siempre "este panel". */
.visor-panel{--pad:clamp(14px,2.5vw,28px);
  justify-self:center;height:100%;aspect-ratio:1/1;width:auto;max-width:100%;
  min-height:0;position:relative;background:#fff;border-radius:16px;
  box-shadow:0 24px 60px rgba(0,0,0,.45)}
.visor-panel img{position:absolute;inset:var(--pad);
  width:calc(100% - 2 * var(--pad));height:calc(100% - 2 * var(--pad));
  object-fit:contain;display:block}
.visor-x,.visor-nav{flex:none;background:rgba(255,255,255,.10);
  border:1px solid rgba(255,255,255,.22);color:#fff;border-radius:999px;
  width:44px;height:44px;display:grid;place-items:center;cursor:pointer;
  font-size:22px;line-height:1;transition:background .15s}
.visor-x:hover,.visor-nav:hover{background:rgba(255,255,255,.24)}
.visor-tiras{display:flex;gap:8px;justify-content:center;overflow-x:auto;padding:4px 2px}
.visor-tira{width:58px;height:58px;flex:none;border-radius:10px;overflow:hidden;
  background:#fff;border:2px solid transparent;padding:4px;cursor:pointer;
  opacity:.55;transition:opacity .15s,border-color .15s}
.visor-tira img{width:100%;height:100%;object-fit:contain;display:block}
.visor-tira:hover{opacity:.85}
.visor-tira.on{opacity:1;border-color:#fff}
@media (max-width:640px){
  .visor{padding:12px 10px 14px}
  .visor-cuerpo{grid-template-columns:1fr;gap:8px}
  .visor-nav{display:none}          /* en táctil se navega con las miniaturas */
  .visor-panel{border-radius:12px}
}

/* La ficha con galería necesita otra proporción: .hero-dev fue pensado para
   una silueta de 150 px, y una galería con miniaturas y lupa ahí adentro
   queda ilegible. Con esta variante la imagen manda y los datos acompañan. */
.hero-dev.con-galeria{grid-template-columns:minmax(300px,460px) 1fr;align-items:start;gap:24px}
.hero-dev.con-galeria .stage{height:auto;background:none;border:0;padding:0;display:block}
.hero-dev.con-galeria .stage svg{height:auto;max-height:340px}
@media (max-width:820px){.hero-dev.con-galeria{grid-template-columns:1fr}}

/* Dentro del panel lateral (560 px) no cabe una rejilla de dos columnas: la
   imagen pide 300 y al resto le quedan 180, con lo que los bloques se montan
   unos sobre otros. Aquí manda una sola columna: imagen arriba, datos abajo. */
.drawer .hero-dev,
.drawer .hero-dev.con-galeria{grid-template-columns:1fr;gap:16px}
.drawer .gal-main{max-height:440px}
.drawer .hero-dev.con-galeria .stage svg{max-height:300px}

/* Aquí hubo un acercamiento al pasar el mouse (la imagen escalada 2,4×
   siguiendo al cursor). Se quitó: en el cajón el recuadro es pequeño y lo
   ampliado era un recorte sin contexto. Ampliar es el clic → visor. */

/* ── Correcciones de la cuadricula con fotos reales ──────────────────
   El CSS de la maqueta limitaba el tamano de los SVG pero no el de las
   imagenes: una foto real desbordaba la tarjeta y empujaba el texto.

   Esta regla llevaba tiempo escrita y NO estaba haciendo nada: producto-svg.js
   ponia `max-width:100%;max-height:100%` en el atributo style de esa misma
   imagen, y un estilo en linea le gana siempre a la hoja de estilos. Se quito
   de alli. Si alguien vuelve a poner medidas en linea, esto se muere otra vez
   y en silencio — el sintoma no es una foto fea, es una tarjeta sin precio.

   Los 172px no son un numero al azar: es el alto de las siluetas dibujadas
   (.dev-stage svg), para que una tarjeta con foto y una con ilustracion se
   vean del mismo tamano en la misma fila.

   El min-height es el cinturon. .dev-stage es un item flex y, por omision,
   un item flex no se encoge por debajo de su contenido: una imagen mas alta
   que los 196px del escenario lo estiraba y empujaba el cuerpo de la tarjeta
   fuera del overflow:hidden. Con esto, lo peor que puede pasar es que una
   imagen se recorte; el nombre y el precio no se van a ir nunca mas. */
.dev-stage{min-height:0}
.dev-stage img{max-width:88%;max-height:172px;object-fit:contain;display:block}

/* Nombres largos (un televisor trae media ficha tecnica en el modelo) se
   recortan a dos lineas. Antes se salian de la tarjeta y se montaban sobre
   el precio. El nombre completo queda en el title. */
.dev-name{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
  overflow:hidden;min-height:2.6em}

/* Todas las tarjetas con la misma altura para que la cuadricula quede
   pareja aunque un nombre ocupe una linea y otro dos. */
.grid-dev>.dev{display:flex;flex-direction:column}
.dev-body{justify-content:space-between}

/* ── Fichas de categoría del catálogo ────────────────────────────────
   La categoría es la primera decisión del asesor, así que va arriba y en
   grande, no escondida en un desplegable. Es lo que hace que un catálogo
   con celulares, televisores y patinetas se pueda recorrer. */
/* Las categorías NO se apilan en varias líneas en ningún ancho: cuando eran
   ocho, el bloque crecía hasta tres filas y empujaba el catálogo fuera de la
   pantalla, y peor, la altura cambiaba al filtrar. Ahora es UNA sola fila que
   se desliza — con el dedo en el teléfono, con las flechas de los extremos en
   escritorio, donde no hay gesto de arrastre.

   El contenedor es quien posiciona las flechas; el riel es quien se desliza. */
.cat-tabs-wrap{position:relative;margin-bottom:10px}

.cat-tabs{display:flex;gap:8px;flex-wrap:nowrap;overflow-x:auto;
  scroll-behavior:smooth;scrollbar-width:none;-ms-overflow-style:none;
  /* Cada ficha se acomoda entera al soltar: media ficha cortada al borde se
     lee como un error de dibujo, no como «hay más». */
  scroll-snap-type:x proximity;overscroll-behavior-x:contain}
.cat-tabs::-webkit-scrollbar{display:none}
.cat-tabs>*{scroll-snap-align:start;flex:none}

/* Las flechas solo aparecen si hay hacia dónde ir — lo decide el JS con
   data-mas="izq|der|ambos". Un botón que no lleva a ninguna parte confunde
   más de lo que ayuda. */
.cat-flecha{position:absolute;top:50%;transform:translateY(-50%);z-index:2;
  width:30px;height:30px;display:none;place-items:center;
  border:1px solid var(--line);border-radius:50%;background:var(--surface);
  color:var(--ink-2);cursor:pointer;box-shadow:var(--shadow-1)}
.cat-flecha:hover{color:var(--ink);border-color:var(--line-2)}
.cat-flecha svg{width:15px;height:15px}
.cat-flecha.izq{left:-4px}
.cat-flecha.der{right:-4px}
.cat-tabs-wrap[data-mas="izq"]   .cat-flecha.izq,
.cat-tabs-wrap[data-mas="ambos"] .cat-flecha.izq,
.cat-tabs-wrap[data-mas="der"]   .cat-flecha.der,
.cat-tabs-wrap[data-mas="ambos"] .cat-flecha.der{display:grid}

/* En pantalla táctil se arrastra con el dedo: las flechas sobran y además
   taparían la primera y la última ficha. */
@media (max-width:900px){.cat-flecha{display:none!important}}
.cat-tab{display:inline-flex;align-items:center;gap:7px;padding:9px 15px;
  border:1px solid var(--line);border-radius:999px;background:var(--surface);
  font-family:var(--font-cond);font-size:12.5px;letter-spacing:.04em;
  color:var(--ink-2);cursor:pointer;transition:all .15s ease;white-space:nowrap}
.cat-tab:hover{border-color:var(--line-2);color:var(--ink)}
.cat-tab.on{background:var(--accent);border-color:var(--accent);color:#fff;font-weight:600}
.cat-ico{font-size:15px;line-height:1}

/* Contador dentro de cada ficha de categoría: saber cuántos hay antes de
   entrar evita el clic que lleva a un estante vacío. */
.cat-n{display:inline-grid;place-items:center;min-width:20px;height:20px;padding:0 6px;
  border-radius:999px;background:var(--surface-2);color:var(--ink-3);
  font-size:10.5px;font-weight:700;line-height:1}
.cat-tab.on .cat-n{background:rgba(255,255,255,.22);color:#fff}

/* ════════════════════════════════════════════════════════════════════
   Navegación por facetas del catálogo
   --------------------------------------------------------------------
   Columna de filtros con contador por opción, como en cualquier tienda
   grande. El contador cuenta sobre lo que quedaría con los OTROS filtros
   aplicados: así "Samsung (0)" sale en gris y nadie pierde el clic.
   ════════════════════════════════════════════════════════════════════ */

/* Techo de ancho, solo para el catálogo.
   Sin esto la rejilla usa todo el monitor: en 2560 px daba OCHO columnas de
   261 px y una línea de tarjetas que hay que recorrer con la cabeza. El
   límite deja cuatro columnas anchas y centra el bloque. No se pone en .view
   (que es de todas las vistas) porque el POS y el inventario sí quieren todo
   el ancho: una tabla de movimientos gana con cada píxel, una rejilla de
   productos no. */
#catShell{max-width:1500px;margin-inline:auto}

.cat-layout{display:grid;grid-template-columns:236px 1fr;gap:20px;align-items:start}
@media (max-width:900px){.cat-layout{grid-template-columns:1fr}}

/* En un teléfono, la columna fija de 128 px para el nombre de la
   característica deja al valor en una franja de dos dedos, y un
   «Almacenamiento» parte en tres líneas. Se apilan: nombre arriba, valor
   debajo. El asesor de mostrador consulta desde el celular más de lo que
   uno quisiera. */
@media (max-width:520px){
  .spec-row{grid-template-columns:1fr;gap:2px;padding:8px 12px}
  .spec-row dt{font-size:11.5px}
  .ver{min-width:0;flex:1 1 44%}
}

/* Dónde termina la barra superior del portal (.navbar, sticky, 52px) y cuánto
   aire va debajo. Los dos valores se usan cruzados, por eso son variables:
   la barra del catálogo se PEGA en --nav-h para que su fondo tape la franja,
   pero su contenido arranca --cat-aire más abajo — que es exactamente donde
   se pega el panel lateral. Si se desalinean, la barra flota a distinta
   altura que los filtros y se nota enseguida. */
:root{--nav-h:52px;--cat-aire:10px}

.facetas{background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-l);padding:4px 0;
  position:sticky;top:calc(var(--nav-h) + var(--cat-aire));
  max-height:calc(100vh - var(--nav-h) - var(--cat-aire) - 22px);overflow-y:auto}

/* Lo que se desplaza es SOLO la rejilla de productos. La barra de búsqueda,
   el orden, el contador y los distintivos de filtro aplicado se quedan
   arriba: son el estado de lo que se está mirando, y perderlos de vista al
   tercer scroll obliga a subir para saber por qué faltan equipos.

   Se pega en --nav-h y no más abajo por el fondo: pegarlo con aire dejaba una
   franja descubierta entre el encabezado y la barra, y por ahí se veían pasar
   pedazos de tarjeta. El margen negativo cancela el relleno para que no
   agregue un salto al principio de la lista. */
.cat-top{position:sticky;top:var(--nav-h);z-index:30;background:var(--plane);
  padding:var(--cat-aire) 0 2px;margin-top:calc(var(--cat-aire) * -1)}

/* El velo y el botón «Filtros» solo existen en pantalla angosta: en
   escritorio el panel está siempre a la vista y no hay nada que abrir. Van
   ANTES del @media a propósito — al revés, con la misma especificidad, el
   display:none de después ganaría siempre y el botón no aparecería nunca. */
.cat-scrim{display:none}
.cat-fbtn{display:none;align-items:center;gap:7px}
.cat-fbtn .cat-n{background:var(--accent);color:#fff}
.cat-cerrar{display:none}

/* ── Filtros en pantalla angosta ──────────────────────────────────────
   El aside se apilaba ARRIBA de la rejilla, así que con siete grupos de
   facetas había que recorrer 4.000 px de casillas para ver el primer equipo.
   Pasa a ser un cajón que entra desde la IZQUIERDA.

   Se detiene arriba de la barra táctil, igual que el resto de los paneles:
   el menú tiene que quedar siempre alcanzable. */
@media (max-width:900px){
  .facetas{position:fixed;left:0;top:0;
    bottom:calc(var(--tabbar-h) + env(safe-area-inset-bottom));
    width:min(320px,86vw);max-height:none;z-index:61;
    border-radius:0;border-left:0;border-top:0;border-bottom:0;
    padding-top:calc(env(safe-area-inset-top) + 4px);
    transform:translateX(-101%);
    transition:transform .28s cubic-bezier(.2,.75,.25,1);
    box-shadow:var(--shadow-3);overscroll-behavior:contain}
  .facetas.on{transform:none}

  /* Su propio velo: el #scrim del shell es de la ficha del producto, y
     compartirlo haría que cerrar una cerrara la otra. */
  .cat-scrim{position:fixed;inset:0;
    bottom:calc(var(--tabbar-h) + env(safe-area-inset-bottom));
    background:rgba(8,9,14,.42);backdrop-filter:blur(2px);z-index:60;
    opacity:0;pointer-events:none;transition:opacity .25s}
  .cat-scrim.on{opacity:1;pointer-events:auto}

  .cat-scrim{display:block}
  .cat-fbtn{display:inline-flex}
  .cat-cerrar{display:inline-flex}
  /* Con «Limpiar» presente, el ✕ va al final; sin él, «Limpiar» no ocupa el
     hueco y el ✕ igual queda pegado a la derecha. */
  .facetas-h .cat-cerrar{margin-left:0}
  .facetas-h b{margin-right:auto}

  /* La cabecera con «Filtros» y «Limpiar» queda a mano mientras se recorre
     una lista larga de casillas. */
  .facetas-h{position:sticky;top:0;background:var(--surface);z-index:1}
}

.facetas-h{display:flex;align-items:center;gap:8px;padding:12px 14px 10px;
  border-bottom:1px solid var(--line)}
.facetas-h b{font-family:var(--font-cond);font-size:12px;letter-spacing:.12em;
  text-transform:uppercase;color:var(--ink-3)}
.facetas-h .btn{margin-left:auto}

.faceta{border-bottom:1px solid var(--line)}
.faceta:last-child{border-bottom:0}
.faceta-h{width:100%;display:flex;align-items:center;gap:8px;padding:11px 14px;
  border:0;background:none;cursor:pointer;font-family:var(--font-cond);
  font-size:12.5px;font-weight:600;letter-spacing:.03em;color:var(--ink);text-align:left}
.faceta-h svg{width:15px;height:15px;margin-left:auto;color:var(--ink-3);
  transition:transform .2s ease}
.faceta.cerrada .faceta-h svg{transform:rotate(-90deg)}
.faceta.cerrada .faceta-b{display:none}
.faceta-b{padding:0 14px 12px;display:flex;flex-direction:column;gap:2px;
  max-height:230px;overflow-y:auto}
/* .faceta-orden ("Ordenar por"/"Cómo listar" en pantalla angosta) no pasa
   por .faceta-b, así que no heredaba el padding lateral de 14px de todo lo
   demás — el bloque quedaba pegado al borde izquierdo del cajón. */
.faceta-orden{padding:12px 14px}
.faceta-orden .field+.field{margin-top:8px}
.faceta-precio-libre{display:flex;align-items:center;gap:8px;margin-bottom:10px}
.faceta-precio-libre input{width:0;flex:1;height:34px;padding:0 8px;border-radius:8px;
  border:1px solid var(--border);background:var(--surface);color:var(--text);font-size:13px}
.faceta-precio-libre input:focus{outline:2px solid var(--primary-500);outline-offset:1px;border-color:var(--primary-500)}
.faceta-precio-libre span{color:var(--text-tertiary);flex:none}
/* Pie fijo del cajón de filtros (solo pantalla angosta): sticky dentro del
   propio scroll de .facetas, no position:fixed -- así no necesita saber la
   altura del cajón ni pelear con la tabbar por su cuenta. */
.faceta-pie{position:sticky;bottom:0;background:var(--surface);
  padding:12px 14px calc(12px + env(safe-area-inset-bottom));
  border-top:1px solid var(--line)}
.faceta-pie .btn{width:100%;justify-content:center}
/* KPI principal (hoy solo en "Ventas de hoy", pos.js:ventasDeHoy) — el
   número que manda va aparte y grande; los de apoyo se quedan en .tiles. */
.kpi-hero{display:flex;flex-direction:column;gap:2px;margin-bottom:14px}
.kpi-hero b{font-size:var(--fs-display);line-height:var(--lh-display);
  font-weight:var(--fw-display);color:var(--ink);font-variant-numeric:tabular-nums}

/* Tarjeta de usuario arriba de la hoja "Más" (shell.js:abrirMasHoja) */
.mas-usuario{display:flex;align-items:center;gap:12px;padding:4px 4px 16px;
  margin-bottom:8px;border-bottom:1px solid var(--line)}
.mas-usuario .avatar{width:44px;height:44px;font-size:15px}
.mas-usuario b{display:block;font-size:14.5px}
.mas-usuario small{display:block;color:var(--ink-3);font-size:12px;margin-top:2px}

.faceta-op{display:flex;align-items:center;gap:8px;padding:5px 4px;border-radius:7px;
  cursor:pointer;font-size:13px;color:var(--ink-2)}
.faceta-op:hover{background:var(--surface-2)}
.faceta-op input{accent-color:var(--accent);width:15px;height:15px;flex:none}
.faceta-op span{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.faceta-op b{font-family:var(--font-cond);font-size:10.5px;color:var(--ink-3);font-weight:600;
  flex:none;background:var(--surface-2);border-radius:999px;padding:1px 7px}
.faceta-op.vacia{opacity:.42;cursor:default}

.cat-bar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:8px}
.cat-bar .fcount{margin-left:auto}
.cat-bar .search{max-width:300px}

/* ── Cuadrícula o lista ───────────────────────────────────────────────
   Dos modos, y lo que significa cada uno depende del tamaño de pantalla,
   porque el problema no es el mismo:

     escritorio → cuadrícula de varias columnas, o lista de una fila por
                  equipo con la foto al costado (para barrer precios)
     teléfono   → dos por fila (el doble de catálogo por pantallazo) o una
                  por fila (para mirar el equipo con calma)

   El estado es uno solo —apretado o amplio— y por eso es un solo control. */

/* ESCRITORIO · lista: una fila por equipo, la foto al costado. */
@media (min-width:901px){
  .cat-layout[data-modo="l"] .grid-dev{grid-template-columns:1fr;gap:10px}
  .cat-layout[data-modo="l"] .dev{flex-direction:row;align-items:stretch}
  .cat-layout[data-modo="l"] .dev-stage{width:150px;height:auto;min-height:118px;flex:none}
  .cat-layout[data-modo="l"] .dev-stage svg{height:96px}
  .cat-layout[data-modo="l"] .dev-body{flex-direction:row;align-items:center;
    gap:18px;flex-wrap:wrap;padding:12px 16px}
  /* El precio deja de empujarse al fondo de una columna y se va al final de
     la fila, que es donde el ojo lo busca cuando se recorre una lista. */
  .cat-layout[data-modo="l"] .dev-price{margin:0 0 0 auto;padding-top:0;
    border-top:0;flex:none;align-items:center;gap:16px}
  .cat-layout[data-modo="l"] .dev-specs{flex:1;min-width:0}

  /* Sobre una foto de 150px, «ENTRADA» a tamaño normal se salía del recorte
     de la tarjeta. Misma solución que en el teléfono: achicarla. */
  .cat-layout[data-modo="l"] .dev-badges{top:7px;left:7px;right:7px;gap:4px}
  .cat-layout[data-modo="l"] .dev-badges .pill{padding:2px 6px;font-size:9.5px;
    letter-spacing:.02em;min-width:0;overflow:hidden;text-overflow:ellipsis;
    white-space:nowrap;max-width:100%}

  /* El recuadro de promoción repite el precio que ya está al final de la
     fila, y en horizontal los dos quedaban uno encima del otro. En lista
     manda el precio; el tachado y el porcentaje siguen ahí. */
  .cat-layout[data-modo="l"] .promo{display:none}
}

/* TELÉFONO · cuadrícula = DOS por fila. Es el modo por defecto: con una sola
   tarjeta por pantalla había que recorrer el catálogo de a un equipo. */
@media (max-width:900px){
  .cat-layout[data-modo="g"] .grid-dev{
    grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}
  .cat-layout[data-modo="l"] .grid-dev{grid-template-columns:1fr}

  /* A media pantalla de ancho, la tarjeta tiene que adelgazar o no entra
     nada legible: foto más baja, menos aire, tipografía ajustada. Lo que se
     saca es lo que se puede consultar abriendo la ficha; lo que decide una
     venta —marca, modelo, precio, si hay stock— se queda. */
  .cat-layout[data-modo="g"] .dev-stage{height:132px}
  .cat-layout[data-modo="g"] .dev-stage svg{height:112px}
  .cat-layout[data-modo="g"] .dev-body{padding:10px 11px 11px;gap:6px}
  .cat-layout[data-modo="g"] .dev-name{font-size:13px}
  .cat-layout[data-modo="g"] .dev-brand{font-size:10px;letter-spacing:.1em}
  .cat-layout[data-modo="g"] .dev-specs{display:none}
  .cat-layout[data-modo="g"] .swatches{display:none}
  .cat-layout[data-modo="g"] .cmp-tick span,
  .cat-layout[data-modo="g"] .stock-line{font-size:10.5px}
  .cat-layout[data-modo="g"] .dev-price{padding-top:8px;gap:4px}
  .cat-layout[data-modo="g"] .p-hoy,
  .cat-layout[data-modo="g"] .dev-price b{font-size:15px}

  /* A media pantalla, «ENTRADA» y «−20 %» juntos no caben y la primera se
     salía del borde de la tarjeta. Se achican y se dejan quebrar el mínimo
     necesario en vez de desbordar. */
  .cat-layout[data-modo="g"] .dev-badges{top:7px;left:7px;right:7px;gap:4px}
  .cat-layout[data-modo="g"] .dev-badges .pill{padding:2px 6px;font-size:9.5px;
    letter-spacing:.02em;min-width:0;overflow:hidden;text-overflow:ellipsis;
    white-space:nowrap;max-width:100%}
  .cat-layout[data-modo="g"] .promo-flag{top:7px;right:7px;font-size:10px;padding:2px 6px}
}

.dens{display:flex;gap:2px;padding:3px;border:1px solid var(--line);
  border-radius:10px;background:var(--surface);flex:none}
.dens button{width:30px;height:26px;display:grid;place-items:center;border:0;
  border-radius:7px;background:none;color:var(--ink-3);cursor:pointer;
  transition:background .15s,color .15s}
.dens button:hover{background:var(--surface-2);color:var(--ink)}
.dens button.on{background:var(--ink);color:var(--surface)}
.dens svg{width:15px;height:15px;display:block}

/* Orden y agrupación: en la barra solo en escritorio. En el teléfono viven
   dentro del cajón de filtros — decidir el orden es decidir qué lista se ve,
   igual que filtrar, y tenerlos en la barra forzaba una segunda fila de 48px
   que empujaba los productos fuera del primer pantallazo. */
@media (max-width:900px){
  .cat-solo-pc{display:none}

  /* Todo en UNA fila. Antes eran dos de 48px cada una y el primer equipo
     empezaba pasada la cuarta parte de la pantalla.
     El buscador trae min-width:220px de escritorio; a 390px eso solo ya
     empuja al resto a la línea de abajo. Y el botón de filtros se queda con
     su icono y el número de filtros puestos: la palabra cuesta 50px que le
     hacen más falta al campo de búsqueda. */
  .cat-bar .search{min-width:120px;max-width:none}
  .cat-fbtn-txt{display:none}
  .cat-fbtn{padding-left:11px;padding-right:11px}
  /* El contador se lee bien como línea aparte; compitiendo por ancho en la
     fila de arriba es lo que dejaba "Buscar equip…" a medio escribir. */
  .cat-bar .fcount{flex-basis:100%;margin-left:0;text-align:left}
}

/* Filtros aplicados, para quitarlos de a uno sin ir a buscarlos en la columna */
.chips-activos{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:10px}
.chip-x{display:inline-flex;align-items:center;gap:6px;padding:5px 10px;
  border:1px solid var(--accent);border-radius:999px;background:var(--accent-soft);
  color:var(--accent);font-size:12px;cursor:pointer}
.chip-x i{font-style:normal;opacity:.7;font-size:11px}
.chip-x:hover i{opacity:1}

/* Se retira el justify-content que agregué antes: .dev-price ya se empuja al
   fondo con margin-top:auto, y tener las dos reglas repartiendo el espacio
   dejaba el bloque de marca y modelo en un sitio impredecible. */
.dev-body{justify-content:flex-start}

/* La marca y el modelo son lo primero que se lee: no pueden depender de
   cómo reparta el espacio el contenedor. */
.dev-brand{display:block;min-height:1em}
.dev-name{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
  overflow:hidden;margin:2px 0 0}
.cat-layout.sin-facetas{grid-template-columns:1fr}

/* Precio en la tarjeta: sin cuota. Cuando hay promoción, el precio de lista
   tachado encima del de hoy — la comparación es lo que vende el descuento. */
.p-antes{color:var(--ink-3);text-decoration:line-through;font-size:12.5px;display:block}
.p-hoy{font-family:var(--font-cond);font-size:20px;font-weight:700;
  font-variant-numeric:tabular-nums;color:var(--ink);line-height:1.15}

/* ── Versiones ─────────────────────────────────────────────────────────────
   Un producto con versiones no tiene un precio sino un rango. El "desde" va
   pegado encima del número y en minúscula: es una advertencia, no un titular.
   Sin él, el asesor promete el precio de la versión chica por la grande. */
.p-desde{display:block;font-family:var(--font-cond);font-size:10.5px;
  letter-spacing:.08em;text-transform:uppercase;color:var(--ink-3);line-height:1}
.p-rango{display:block;font-size:11px;color:var(--ink-3);line-height:1.3;margin-top:2px}

/* Las versiones se distinguen de las características: unas son opciones que
   el cliente elige, las otras son lo que el equipo ES. Con el mismo estilo se
   leían como una sola lista de datos sueltos. */
.spec-chip.spec-ver{background:var(--accent-soft);color:var(--accent);font-weight:600}
/* La versión, pegada al modelo en la vista por versión. */
.dev-name .dev-ver{display:inline-block;background:var(--accent-soft);color:var(--accent);
  border-radius:6px;padding:0 6px;font-size:11.5px;font-weight:600;vertical-align:middle;
  margin-left:2px}
.spec-chip.spec-ver.sin{background:var(--surface-2);color:var(--ink-3);
  text-decoration:line-through}

.ver-sel{display:flex;flex-wrap:wrap;gap:8px;padding:10px 12px}
.ver{display:flex;flex-direction:column;gap:2px;align-items:flex-start;text-align:left;
  border:1px solid var(--line);background:var(--surface);border-radius:var(--r-m);
  padding:8px 11px;cursor:pointer;min-width:118px;transition:border-color .15s, background .15s}
.ver:hover{background:var(--surface-2)}
.ver.on{border-color:var(--accent);background:var(--accent-soft)}
.ver b{font-size:12.5px;color:var(--ink);font-weight:650;line-height:1.2}
.ver .num{font-family:var(--font-cond);font-size:14px;font-weight:700;
  font-variant-numeric:tabular-nums;color:var(--ink)}
/* La versión agotada NO se esconde: el cliente pregunta por ella igual y el
   asesor tiene que poder decir "esa no hay" en vez de no encontrarla. */
.ver.agotada{opacity:.6}
.ver.agotada .num{color:var(--ink-3)}

/* Mismo problema que en la cuadrícula, en el otro sitio: .hero-dev .stage se
   dimensionó para una silueta y la foto real lo desbordaba, montándose sobre
   los precios. Se limita igual que las tarjetas. */
.hero-dev:not(.con-galeria) .stage img{max-width:100%;max-height:186px;object-fit:contain;display:block;margin:0 auto}
.hero-dev:not(.con-galeria) .stage{overflow:hidden}
/* ↑ Solo sin galería. Esta regla estaba sin el :not y le tapaba a la galería
   la foto grande a 186 px: por eso la ficha mostraba una estampilla aunque
   la galería tuviera todo el ancho. La galería trae sus propias medidas. */

/* ── Colores (Ajustes) ─────────────────────────────────────────────────────
   El círculo lleva borde propio: sin él, un color casi blanco desaparece
   sobre el fondo de la tabla y parece que la fila no tuviera tono. */
.swatch{
  display:inline-block; width:20px; height:20px; border-radius:50%;
  border:1px solid rgba(0,0,0,.22); box-shadow:inset 0 1px 2px rgba(0,0,0,.18);
  vertical-align:middle;
}
.swatch-lg{ width:38px; height:38px; flex:0 0 38px; }

.color-pick{ display:flex; align-items:center; gap:10px; margin-top:6px; }
.color-pick input[type=color]{
  width:46px; height:38px; padding:2px; flex:0 0 46px; cursor:pointer;
  border:1px solid var(--bd); border-radius:8px; background:var(--card);
}
.color-pick .fsel{ flex:1 1 auto; min-width:0; letter-spacing:.04em; }

/* ── Recepción: seriales leídos ────────────────────────────────────────────
   Cada lectura es una ficha con su × propia. Se ven todos a la vez a
   propósito: quien recibe necesita confirmar de un vistazo que la pistola
   leyó lo que creía, no un contador que solo dice «47». */
.chips{ display:flex; flex-wrap:wrap; gap:6px; }
/* Renombrada de .chip a .chip-serial: colisionaba con el .chip del sistema
   de diseño (fase 3) -- misma selector, y por ir más abajo en el archivo
   este le ganaba, dejando el chip de filtros/categorías sin fondo ni
   borde visibles. De paso, --soft y --bd nunca estuvieron definidas (bug
   previo, no de esta fase): se cambian a los tokens reales que ya existen
   para el mismo propósito. */
.chip-serial{
  display:inline-flex; align-items:center; gap:6px;
  padding:4px 8px; border-radius:7px;
  background:var(--surface-2); border:1px solid var(--border);
  font-size:.82rem;
}
.chip-serial button{
  border:0; background:none; cursor:pointer; padding:0 2px;
  color:var(--ink-3); font-size:15px; line-height:1;
}
.chip-serial button:hover{ color:var(--danger-text); }
.muted{ color:var(--ink-3); }

/* ── Buscador de proveedor ─────────────────────────────────────────────────
   Va sobre el resto del formulario (position:absolute) para no empujar los
   campos de abajo cada vez que aparecen sugerencias. */
.field{ position:relative; }
.sugerencias{
  display:none; position:absolute; top:100%; left:0; right:0; z-index:40;
  margin-top:4px; max-height:260px; overflow-y:auto;
  background:var(--card); border:1px solid var(--bd); border-radius:10px;
  box-shadow:0 10px 28px rgba(0,0,0,.16);
}
.sugerencias.abierta{ display:block; }
.sugerencias button{
  display:block; width:100%; text-align:left; border:0; cursor:pointer;
  padding:9px 12px; background:none; border-bottom:1px solid var(--bd);
}
.sugerencias button:last-child{ border-bottom:0; }
.sugerencias button:hover{ background:var(--soft); }
.sugerencias button b{ display:block; font-size:.9rem; }
.sugerencias button span{ display:block; font-size:.78rem; color:var(--ink-3); margin-top:2px; }
.sug-vacio{ padding:10px 12px; font-size:.82rem; color:var(--ink-3); }

/* El campo con el buscador abierto tiene que ganarle a los campos de abajo.
   Como .field es position:relative, cada uno crea su propio contexto y los
   posteriores en el DOM se pintaban encima del desplegable. Al enfocar, este
   sube por encima de todos. */
.field:focus-within{ z-index:60; }
.form-grid{ overflow:visible; }

/* El desplegable del proveedor deja de flotar.
   Flotando dependía del apilamiento y del recorte de todos sus contenedores
   —el panel, la rejilla, el propio campo—, y bastaba que uno cortara para
   que quedara debajo. En el flujo normal empuja los campos hacia abajo
   mientras está abierto y no hay forma de que quede tapado. */
.sugerencias{
  position:static; margin-top:6px; max-height:230px;
  box-shadow:none; z-index:auto;
}

/* ── Colores al crear un producto ──────────────────────────────────────────
   Casillas con el tono real al lado: elegir «Verde oliva» de una lista de
   texto obliga a recordar cuál era; con el círculo se ve. */
.color-check{ display:flex; flex-wrap:wrap; gap:8px; margin-top:6px; }
.color-op{
  display:inline-flex; align-items:center; gap:6px; cursor:pointer;
  padding:5px 10px 5px 7px; border:1px solid var(--bd); border-radius:20px;
  font-size:.83rem; user-select:none;
}
.color-op:hover{ background:var(--soft); }
.color-op input{ margin:0; }
.color-op:has(input:checked){ border-color:var(--accent); background:var(--soft); }

/* Con veinte colores la lista completa era más larga que el formulario.
   Se recorta a tres filas con scroll propio; el filtro hace el resto. */
.color-check{ max-height:118px; overflow-y:auto; align-content:flex-start;
  padding:6px; border:1px solid var(--bd); border-radius:9px; }

/* El círculo se veía ovalado: dentro de un flex es un elemento más y se
   encogía a lo ancho cuando el nombre del color era largo. Con flex:0 0 deja
   de encogerse y vuelve a ser redondo. */
.color-op .swatch{
  width:15px; height:15px; flex:0 0 15px; border-radius:50%;
  border:1px solid rgba(0,0,0,.3);
  box-shadow:inset 0 1px 2px rgba(0,0,0,.25), 0 0 0 1px rgba(255,255,255,.55);
}
.color-op > span:last-child{ white-space:nowrap; }


/* ══════════════════════════════════════════════════════════════════════════
   POS DE MOSTRADOR — vista "Vender"
   Los productos al centro y la venta como libro a la derecha. Tomado de la
   maqueta «tienda-libromayor», adaptado a los tokens del módulo.
   ══════════════════════════════════════════════════════════════════════════ */
/* Alto fijo para que scrolleen los paneles y NO la página: 52 de la barra
   superior + 44 del padding de .view + 6 de holgura del shell = 102 (medido,
   no calculado). Sin esto, buscar un producto empuja
   el total y el botón Cobrar fuera de la pantalla.

   Sin min-height a propósito: en un portátil de 768 px un piso fijo empuja la
   página a scrollear, que es justo lo que se quiere evitar. Con los paneles
   scrolleando por dentro, una pantalla corta solo los achica. */
.pos{display:grid;grid-template-rows:auto minmax(0,1fr);gap:12px;
     height:calc(100vh - 102px)}
@media (max-width:1100px){.pos{height:auto}}

/* ── El escáner y la grilla de productos ──────────────────── */
/* El escáner es donde empieza casi toda venta: va en su propia tarjeta y con
   borde de acento, para que sea el objetivo evidente al entrar. */
.pos-hero{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:14px;align-items:center;
          padding:12px 14px;background:var(--surface);border:1px solid var(--line);
          border-radius:var(--r-l);box-shadow:var(--shadow-1)}
@media (max-width:760px){.pos-hero{grid-template-columns:1fr}}
/* El .search global está topado en max-width:340px (línea ~255). Acá el campo
   es el objetivo principal de la pantalla, así que se suelta el tope. */
.pos-hero .search{position:relative;display:flex;align-items:center;max-width:none;flex:1}
.pos-hero .search .field{width:100%}
.pos-hero .search svg{position:absolute;left:15px;top:50%;transform:translateY(-50%);width:20px;height:20px;color:var(--accent);pointer-events:none}
/* El input lleva .field, que es position:relative y sube a z-index 60 al
   enfocarse (para el desplegable de proveedor). Las dos cosas tapaban el icono:
   posicionado, gana por ir después en el DOM; y como .search es flex, el
   z-index aplica aunque el ítem sea static. Se anulan ambas. */
.pos-hero .search .field{position:static;z-index:auto;padding-left:46px;font-size:16px;height:50px;border-width:2px;border-color:var(--accent);border-radius:var(--r-l)}
.pos-hero .search .field:focus{outline:none;box-shadow:0 0 0 3px var(--accent-soft)}
.pos-hint{display:flex;gap:14px;flex-wrap:wrap;margin-top:6px;font-size:12px;color:var(--ink-3)}
.kbd{display:inline-block;font:600 11px var(--font-mono);padding:1px 6px;border:1px solid var(--line-2);border-radius:5px;background:var(--surface-2);color:var(--ink-2)}

.pos-body{display:grid;grid-template-columns:minmax(0,1fr) 500px;gap:12px;min-height:0}
@media (max-width:1100px){.pos-body{grid-template-columns:1fr;min-height:0}}
/* La estantería: columna propia que scrollea sola. */
.pos-body>div:first-child{display:flex;flex-direction:column;gap:10px;min-height:0;min-width:0}

/* Una sola fila que se desplaza en horizontal. Si las categorías envuelven en
   varias filas, la grilla de productos salta cada vez que se filtra y el dedo
   pierde el objetivo. */
.pos-cats{display:flex;gap:6px;margin-bottom:0;flex:none;overflow-x:auto;padding-bottom:2px;scrollbar-width:thin}
.pos-cats .cat{flex:none}
.pos-cats .cat{display:inline-flex;align-items:center;gap:6px;padding:6px 11px;border:1px solid var(--line);border-radius:999px;background:var(--surface);font-size:13px;color:var(--ink-2);cursor:pointer}
.pos-cats .cat svg{width:15px;height:15px}
.pos-cats .cat .c{font:600 11px var(--font-mono);color:var(--ink-3)}
.pos-cats .cat.on{background:var(--accent);border-color:var(--accent);color:var(--accent-on)}
.pos-cats .cat.on .c{color:inherit}

/* Tarjeta compacta y horizontal: miniatura de 52 px a la izquierda, nombre y
   precio a la derecha. La ilustración es genérica por categoría — todos los
   celulares se dibujan igual — así que a 110 px de alto solo tapaba productos.
   A 1440 pasan de 8 visibles a más de 30, que es lo que hace rápido buscar. */
/* Tarjeta compacta y horizontal: miniatura de 52 px a la izquierda, nombre y
   precio a la derecha. La ilustración es genérica por categoría — todos los
   celulares se dibujan igual — así que a 110 px de alto solo tapaba productos.
   A 1440 pasan de 8 visibles a más de 30, que es lo que hace rápido buscar. */
/* Tarjeta compacta y horizontal: miniatura de 52 px a la izquierda, nombre y
   precio a la derecha. La ilustración es genérica por categoría — todos los
   celulares se dibujan igual — así que a 110 px de alto solo tapaba productos.
   A 1440 pasan de 8 visibles a más de 30, que es lo que hace rápido buscar. */
.prods{flex:1;overflow:auto;display:grid;grid-template-columns:repeat(auto-fill,minmax(172px,1fr));
       gap:8px;align-content:start;padding:1px}
.prod{position:relative;display:flex;flex-direction:column;gap:5px;padding:8px 9px 10px;text-align:left;
      border:1px solid var(--line);border-radius:var(--r-m);background:var(--surface);cursor:pointer;
      box-shadow:var(--shadow-1);transition:border-color .1s,box-shadow .12s}
/* El hover marca sin mover: una tarjeta que se desplaza bajo el dedo en una
   pantalla táctil produce toques errados. */
.prod:hover{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent) inset}
.prod:active{background:var(--accent-soft)}
.prod.out{opacity:.9;cursor:pointer}
.prod.first{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent) inset}
/* 1:1 en vez de 16:10 (spec 6.9) — foto/silueta ocupa un cuadrado, más
   consistente entre equipos altos y anchos. Agotado: escala de grises,
   la propia pill "Agotado" (en prod-linea1) ya dice por qué no se puede
   comprar, no hace falta oscurecer más encima. */
.prod .foto{width:100%;aspect-ratio:1/1;border-radius:6px;background:var(--surface-2);display:grid;place-items:center;overflow:hidden}
.prod .foto img,.prod .foto svg{width:100%;height:100%;object-fit:contain;display:block}
.prod .foto-agotada{filter:grayscale(1) opacity(.5)}
/* Antes .st (stock) y .serial iban ABSOLUTOS sobre la foto, con un fondo
   oscuro a propósito para leerse sobre cualquier imagen. Ahora es una fila
   normal debajo de la foto y usa el pill semántico de siempre (el mismo
   que Ventas/Catálogo) — ya no necesita su propio contraste. */
.prod-linea1{display:flex;align-items:center;justify-content:space-between;gap:6px;margin-top:6px}
.imei-tag{display:inline-flex;align-items:center;gap:3px;flex:none;
  font-size:10.5px;font-weight:600;color:var(--text-tertiary)}
.prod .promo{position:absolute;top:13px;right:14px;font:700 10.5px var(--font-ui);
  padding:2px 6px;border-radius:4px;background:var(--crit);color:#fff}
.prod .nm{margin-top:3px;font-weight:600;font-size:13.5px;line-height:1.25;color:var(--ink);
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.prod .nm small{display:block;font-weight:400;font-size:11.5px;color:var(--ink-3);margin-top:2px}
.prod .pr{display:flex;align-items:baseline;gap:6px;flex-wrap:wrap;font:700 16px var(--font-ui);color:var(--ink)}
.prod .pr s{font-weight:400;font-size:12px;color:var(--ink-3)}
.prod .pr .cuota{font:500 11px var(--font-ui);color:var(--accent-2);background:var(--accent-soft);padding:1px 6px;border-radius:4px}
.prod .colors{display:flex;align-items:center;gap:4px}
.dot-color{display:inline-block;width:12px;height:12px;border-radius:50%;border:1px solid rgba(0,0,0,.15)}
.colors-mas{font-size:10px;font-weight:700;color:var(--text-tertiary)}

/* ── El carrito (.ledger), como libro mayor ───────────────── */
/* Ya no necesita sticky: la columna tiene alto propio y scrollea por dentro,
   así el encabezado de la tabla y el botón Cobrar nunca se van de pantalla. */
.ledger{display:flex;flex-direction:column;min-height:0;overflow:hidden;
        border:1px solid var(--line);border-radius:var(--r-l);background:var(--surface);box-shadow:var(--shadow-2)}
@media (max-width:1100px){.ledger{max-height:none}}
/* El encabezado, el cliente, los totales y las acciones son mobiliario fijo:
   lo que ocupen se lo quitan a la lista de artículos, que es lo único que
   crece con la venta. Por eso van comprimidos — cada píxel de acá es una
   fila más de venta visible. */
.ledger-h{display:flex;align-items:center;gap:6px;padding:8px 10px 8px 12px;border-bottom:1px solid var(--line);flex:none}
.ledger-h h2{margin:0;font-size:14px;font-weight:650;flex:1}
/* Pausar y Vaciar son acciones raras: van sin borde y atenuadas, para no
   competir con la lista ni con Cobrar. */
.ledger-h .btn-sm{height:28px;padding:0 9px;font-size:12px;border-color:transparent;background:transparent;color:var(--ink-2)}
.ledger-h .btn-sm:hover{background:var(--surface-2);color:var(--ink)}
/* Cliente en UNA línea: "Consumidor final · Sin datos del cliente". Eran dos
   renglones apilados que costaban 48 px; ahora 38. */
.ledger-cli{display:flex;align-items:center;gap:8px;padding:5px 10px 5px 12px;border-bottom:1px solid var(--line);background:var(--surface-2);flex:none;min-height:38px}
.ledger-cli .avatar{width:24px;height:24px;border-radius:50%;flex:none;font-size:11px}
.ledger-cli .avatar svg{width:14px;height:14px}
.ledger-cli>div{display:flex;align-items:baseline;gap:6px;min-width:0;flex:1;white-space:nowrap;overflow:hidden}
.ledger-cli b{display:inline;font-size:13px;line-height:1.25;font-weight:600}
.ledger-cli small{display:inline;color:var(--ink-3);font-size:11.5px;line-height:1.25;overflow:hidden;text-overflow:ellipsis}
.ledger-cli small::before{content:"· ";color:var(--ink-3)}
.ledger-cli .btn-sm{height:26px;padding:0 9px;font-size:12px}
.ledger-cli small::before{content:"· ";color:var(--ink-3)}
.ledger-cli .btn-sm{height:26px;padding:0 9px;font-size:12px}
/* min-height:0 NO es cosmético. El ledger tiene alto fijo y overflow:hidden;
   cualquier piso acá empuja los totales y Cobrar fuera del recorte — en 1280×720
   el botón desaparecía 51 px por debajo. La lista es lo elástico de este panel;
   el total y Cobrar son intocables. */
.ledger .tbl-wrap{flex:1;overflow:auto;min-height:0}
.ledger table{width:100%;border-collapse:collapse;font-size:13px;table-layout:fixed}
.ledger th{text-align:left;font:600 10.5px var(--font-ui);letter-spacing:.05em;text-transform:uppercase;color:var(--ink-3);padding:7px 12px;border-bottom:1px solid var(--line);position:sticky;top:0;background:var(--surface);z-index:1}
/* Fila con aire: 7 px arriba y abajo más dos renglones dan ~52 px. Es la
   parte del panel que el cajero lee mientras el cliente mira; apretarla para
   que quepan más líneas la vuelve ilegible y no la vuelve más rápida. */
.ledger td{padding:7px 12px;border-bottom:1px solid var(--line);vertical-align:middle}
.ledger td.c{padding:7px 4px}
.ledger td:last-child{padding:7px 4px 7px 0}
.ledger tbody tr:hover td{background:var(--surface-2)}
/* El nombre es lo que se confirma con el cliente: 14 px, a pleno, sin cortes a
   media palabra. Debajo, lo que identifica la línea (IMEI, o precio unitario). */
.ledger td .desc{overflow-wrap:anywhere;font-weight:600;font-size:14px;line-height:1.25;color:var(--ink)}
.ledger td .desc small{display:block;font-weight:400;color:var(--ink-3);font-size:11.5px;line-height:1.25;margin-top:2px;font-variant-numeric:tabular-nums}
.ledger th.n,.ledger td.n{text-align:right;font-variant-numeric:tabular-nums}
.ledger td.n{font-weight:700;font-size:14.5px;color:var(--ink);white-space:nowrap}
.ledger th.c,.ledger td.c{text-align:center}
/* Objetivos de 32 px: el mínimo para tocar con el dedo o con prisa. La cifra
   es lo que se confirma en voz alta: más grande y más negra que los signos. */
.ledger .qty{display:inline-flex;align-items:center;border:1px solid var(--line-2);border-radius:8px;overflow:hidden;background:var(--surface)}
.ledger .qty button{width:32px;height:32px;border:0;background:var(--surface-2);cursor:pointer;font-size:18px;font-weight:600;color:var(--ink-2);line-height:1}
.ledger .qty button:hover{background:var(--surface-3);color:var(--ink)}
.ledger .qty span{min-width:30px;text-align:center;font-weight:700;font-size:15px;color:var(--ink);font-variant-numeric:tabular-nums}
.ledger .rm{width:30px;height:30px;border:0;border-radius:6px;background:none;color:var(--ink-3);cursor:pointer;display:grid;place-items:center;padding:0}
.ledger .rm:hover{background:var(--crit-soft);color:var(--danger-text)}
.ledger .rm svg{width:16px;height:16px}
.ledger .empty{padding:32px 16px;text-align:center;color:var(--ink-3);font-size:13px;line-height:1.5}
.ledger .empty b{display:block;color:var(--ink-2);margin-bottom:4px}
/* Zona de cierre: fondo propio y borde grueso para separarla del detalle. */
/* Zona de cierre. Subtotal, descuento e IVA son información de apoyo: chicos y
   atenuados. El total es lo único grande -- pero 26 px, no 30: ya no compite
   con la lista, la remata. */
/* Zona de cierre. Subtotal, descuento e IVA son información de apoyo: chicos y
   atenuados. El total es lo único grande -- pero 26 px, no 30: ya no compite
   con la lista, la remata. */
.ledger-tot{padding:8px 12px 7px;border-top:2px solid var(--line-2);background:var(--surface-2);
            display:grid;gap:2px;font-size:12px;flex:none}
.ledger-tot .ln{display:flex;justify-content:space-between;align-items:center;color:var(--ink-3);line-height:1.4}
.ledger-tot .ln .num{font-weight:600;color:var(--ink-2);font-variant-numeric:tabular-nums}
.ledger-tot .ln.big{margin-top:3px;padding-top:6px;border-top:1px solid var(--line);
                    color:var(--ink);font-family:var(--font-cond);font-weight:700;font-size:14px;letter-spacing:.02em;text-transform:uppercase}
.ledger-tot .ln.big .num{font-size:26px;font-weight:800;letter-spacing:-.03em;color:var(--accent);line-height:1.05}
.ledger-tot .lnk{border:0;background:none;color:var(--accent);cursor:pointer;font-size:12px;padding:0 4px;text-decoration:underline}
/* Un solo botón grande: Cobrar. Pre-cuenta, Cliente nuevo y Apartar se usan
   una de cada veinte ventas; como tres botones de 30 px con borde parecían
   tres Cobrar chicos. Ahora son acciones de texto, sin borde, de 28 px. */
.ledger-act{display:grid;grid-template-columns:1fr 1fr 1fr;gap:6px;padding:6px 10px 10px;flex:none}
.ledger-act .btn-sm{height:28px;padding:0 6px;font-size:12px;font-weight:500;justify-content:center}
/* Pre-cuenta y Cliente nuevo quedan con el look secundario normal de .btn-g
   (borde + fondo tenue). Limpiar es destructivo: se deja discreto en reposo
   y solo se tiñe de peligro al pasar el mouse, para no competir con Cobrar. */
.ledger-act #posLimpiar{border-color:transparent;background:transparent;color:var(--ink-3)}
.ledger-act #posLimpiar:hover{background:var(--crit-soft);color:var(--danger-text)}
.ledger-act .cobrar{grid-column:1/-1;margin-top:4px;display:flex;justify-content:space-between;align-items:center;
                    height:50px;padding:0 16px;font-family:var(--font-cond);font-size:16px;font-weight:700;
                    border-radius:var(--r-l)}
.ledger-act .cobrar .num{font:800 19px var(--font-cond);letter-spacing:-.01em}

/* ── Pantallas cortas (portátiles de 768 px y menos) ────────────────────────
   Acá el alto es el recurso escaso, no el ancho. Lo que se recorta es lo que
   no cambia durante la venta: los atajos de teclado (se aprenden una vez y
   después son ruido) y el aire del mobiliario. Lo que gana ese espacio es la
   lista de artículos, que es lo único que crece con la venta. */
@media (max-height:820px){
  .pos-hint{display:none}
  .ledger-h{padding:6px 12px}
  .ledger-cli{padding:5px 12px}
  .ledger-tot{padding:6px 12px}
  .ledger-tot .ln.big .num{font-size:22px}
  .ledger-act{padding:6px 12px 10px}
  .ledger-act .cobrar{height:46px;font-size:15px}
}

/* Por debajo de ~640 px de alto útil el mobiliario del ledger (300 px fijos)
   no deja lista: medido, quedaban 12 px, o sea nada. Ahí se deja de fingir un
   layout de dos paneles fijos y la página scrollea como cualquier otra: se
   pierde tener Cobrar siempre a la vista, pero se ve la venta completa, que
   en una pantalla así es lo menos malo. */
@media (max-height:640px){
  .pos{height:auto}
  .ledger{overflow:visible}
  .ledger .tbl-wrap{overflow:visible}
  .prods{overflow:visible}
}

/* ── Campos de los paneles del POS ─────────────────────────────────────────
   Se estilan por SELECTOR DE ELEMENTO (input.field, no .field) a propósito:
   `.field` ya significa otra cosa en el sistema de diseño — un CONTENEDOR de
   campo (display:flex column, ver ~línea 784). El POS reusó el mismo nombre
   para el input en sí. Una regla `.field{height:38px…}` arreglaría los modales
   y rompería los contenedores; ésta solo alcanza a input, select y textarea.

   Los 16 usos están todos en pos.js, así que el alcance es este módulo.
   Valores tomados de los inputs que ya existen en la app (.search input,
   .fsel): 38 px de alto, radio 11, borde --line, fondo --surface.
   display:block y flex-direction:row anulan el flex-column del contenedor:
   Chrome usa flex-direction para el layout interno del <select> aunque el
   display no sea flex, y con column dejaba el texto pegado arriba. */
.fg{display:flex;flex-direction:column;gap:5px;min-width:0}
.fg.span2{grid-column:1/-1}
.lbl{font-family:var(--font-cond);text-transform:uppercase;letter-spacing:.13em;
     font-size:11px;color:var(--ink-3);font-weight:600}

input.field,select.field,textarea.field{
  display:block;flex-direction:row;width:100%;height:var(--ctl-h,38px);padding:0 12px;border-radius:11px;
  border:1px solid var(--line);background:var(--surface);color:var(--ink);
  font-size:var(--ctl-fs,13.5px);font-family:inherit}
input.field::placeholder,textarea.field::placeholder{color:var(--ink-3)}
input.field:focus,select.field:focus,textarea.field:focus{
  outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}
input.field:disabled,select.field:disabled{opacity:.55;cursor:not-allowed}

/* El select nativo trae su propia flecha, su propio alto y se encoge al ancho
   del texto: sin esto, "Tipo" medía 40 px al lado de un input de columna entera. */
select.field,select.fsel{appearance:none;-webkit-appearance:none;padding-right:32px;cursor:pointer;
  background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' stroke='%2376828E' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m4 6 4 4 4-4'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 10px center}

textarea.field{height:auto;min-height:76px;padding:9px 11px;
  background:var(--surface-2);resize:vertical;line-height:1.45}

/* Mismo problema que las píldoras de stock: marrón sobre ámbar translúcido no
   se lee en tema oscuro. El color va en el texto, no apagado contra el fondo. */

/* ── Cobro y medios de pago ────────────────────────────────── */
.pay-grid{display:grid;grid-template-columns:1.2fr 1fr;gap:18px}
@media (max-width:760px){.pay-grid{grid-template-columns:1fr}}
.pay-total{padding:12px 14px;border-radius:var(--r-m);background:var(--surface-2);border:1px solid var(--line)}
.pay-total .v{font:700 30px var(--font-cond);color:var(--ink);line-height:1.1}
.pay-total .rest{display:flex;justify-content:space-between;margin-top:6px;font-size:13px;color:var(--ink-2)}
.pay-total .rest b{color:var(--danger-text)}
.pay-total .rest.ok b{color:var(--success-text)}
.medios{display:grid;grid-template-columns:repeat(3,1fr);gap:6px}
.medio{display:flex;flex-direction:column;align-items:flex-start;gap:2px;padding:9px 10px;border:1px solid var(--line);border-radius:8px;background:var(--surface);cursor:pointer;font-size:13px;font-weight:600;color:var(--ink-2);text-align:left}
.medio small{font-weight:400;font-size:11px;color:var(--ink-3)}
.medio.on{border-color:var(--accent);background:var(--accent-soft);color:var(--accent)}
.quick{display:flex;gap:6px;flex-wrap:wrap}
.quick button{padding:5px 10px;border:1px solid var(--line);border-radius:6px;background:var(--surface);cursor:pointer;font-size:12.5px;font-weight:600;color:var(--ink-2)}
.quick button:hover{border-color:var(--accent);color:var(--accent)}
.pay-lines{display:grid;gap:4px;max-height:180px;overflow:auto}
.pay-ln{display:flex;align-items:center;gap:8px;padding:7px 10px;border:1px solid var(--line);border-radius:6px;font-size:13px}
.pay-ln span{flex:1}
.pay-ln b{font-variant-numeric:tabular-nums}
.cambio{padding:10px 12px;border-radius:8px;background:var(--good-soft);color:var(--success-text);font-weight:700;display:flex;justify-content:space-between;font-size:14px}
.cambio.falta{background:var(--warn-soft);color:#F5B942}
.notice{display:flex;gap:10px;align-items:flex-start;padding:10px 12px;border-radius:8px;font-size:12.5px;line-height:1.4;background:var(--surface-2);border:1px solid var(--line);color:var(--ink-2)}
/* Marrón sobre ámbar translúcido no se lee en tema oscuro (mismo problema que
   las píldoras de stock): el color va en el texto, no apagado contra el fondo. */
.notice.warn{background:var(--warn-soft);border-color:transparent;color:#F5B942}
.notice.warn b{color:#FFD37A}
.notice.info{background:var(--accent-soft);border-color:transparent;color:var(--accent-2)}

/* ── Elegir unidad / modelo (IMEI, plan) ──────────────────── */
.imei-list{display:grid;gap:6px;max-height:44vh;overflow:auto}
.imei{display:flex;align-items:center;gap:10px;width:100%;padding:9px 12px;border:1px solid var(--line);border-radius:8px;background:var(--surface);cursor:pointer;text-align:left;font-size:13px;color:var(--ink)}
.imei:hover{border-color:var(--accent)}
.imei .grow{flex:1}
.imei .mono{font-family:var(--font-mono);font-size:12.5px}
.plan-opts{display:grid;grid-template-columns:repeat(3,1fr);gap:6px}
.plan{padding:10px;border:1px solid var(--line);border-radius:8px;background:var(--surface);cursor:pointer;text-align:left;font-size:13px}
.plan:hover,.plan.on{border-color:var(--accent);background:var(--accent-soft)}

/* ── Tiquete impreso ───────────────────────────────────────── */
.ticket{font:12px/1.45 var(--font-mono);background:#fff;color:#111;padding:16px 18px;border:1px dashed #bbb;border-radius:6px;max-width:340px;margin:0 auto}
.ticket h4{margin:0 0 2px;text-align:center;font-size:14px}
.ticket .c{text-align:center;color:#444}
.ticket hr{border:0;border-top:1px dashed #bbb;margin:8px 0}
.ticket .l{display:flex;justify-content:space-between;gap:8px}
.ticket .l.b{font-weight:700;font-size:13px}

/* ── Aviso breve (toast) ───────────────────────────────────── */
.toast{position:fixed;left:50%;bottom:22px;transform:translate(-50%,20px);opacity:0;pointer-events:none;
       display:flex;align-items:center;gap:12px;max-width:calc(100vw - 32px);
       padding:10px 16px;border-radius:999px;background:var(--ink);color:var(--surface);font-size:13px;font-weight:600;
       box-shadow:var(--shadow-3);transition:opacity .18s,transform .18s;z-index:9999}
.toast.on{opacity:1;transform:translate(-50%,0);pointer-events:auto}
.toast.crit{background:var(--crit);color:#fff}
/* Una sola línea: un toast que envuelve a tres renglones deja de leerse
   como una pastilla y tapa lo que hay debajo. Si el mensaje es tan largo
   que no entra, se corta con puntos suspensivos — mejor eso que una nube
   de texto. */
.toast span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
/* ── Utilidad de accesibilidad ────────────────────────────── */
.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}

/* ── Skeletons (PJ.skeleton.*, ui-carga.js) ───────────────────
   Un solo bloque, igual que la conversión tabla→tarjeta: cualquier .skel
   que aparezca en cualquier pantalla ya sabe animarse. */
.skel{background:var(--skeleton-base);border-radius:var(--radius-sm);
  position:relative;overflow:hidden}
.skel::after{content:"";position:absolute;inset:0;
  background:linear-gradient(90deg,transparent 0,var(--skeleton-highlight) 50%,transparent 100%);
  background-size:200vw 100%;animation:skel-onda 1.4s ease-in-out infinite}
@keyframes skel-onda{from{background-position:-100vw 0}to{background-position:100vw 0}}
@media (prefers-reduced-motion:reduce){
  .skel::after{display:none}
  /* El *{animation-duration:.01ms !important} de arriba gana por !important
     aunque esta regla sea más específica -- sin repetirlo aquí, el pulso
     de abajo queda en .01ms (invisible) en vez de un vaivén de 1.8s. */
  .skel{animation:skel-pulso 1.8s ease-in-out infinite;animation-duration:1.8s !important}
}
@keyframes skel-pulso{50%{opacity:.6}}

/* ── Spinner de bloqueo (PJ.conBloqueo, ui-carga.js) ──────────
   Por encima de TODO, paneles incluidos (los más altos llegan a z-index
   72) — el toast (9999) puede seguir asomando por encima, a propósito. */
.pj-bloqueo{position:fixed;inset:0;z-index:9998;background:var(--scrim);
  opacity:0;pointer-events:none;transition:opacity .15s;
  display:flex;align-items:center;justify-content:center}
@supports (backdrop-filter:blur(1px)){.pj-bloqueo{backdrop-filter:blur(2px)}}
.pj-bloqueo.is-visible{opacity:1;pointer-events:auto}
.pj-bloqueo__card{background:var(--surface);border-radius:var(--radius-lg);padding:24px;
  width:clamp(200px,80vw,280px);box-shadow:var(--shadow-3);
  display:flex;flex-direction:column;align-items:center;gap:12px;text-align:center}
.pj-bloqueo__msg{margin:0;font-size:var(--fs-body);font-weight:500;color:var(--text);
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.pj-spinner--lg{width:36px;height:36px;border-width:3.5px;
  border-color:var(--primary-100);border-top-color:var(--primary-600);animation-duration:.8s}
.pj-check{width:36px;height:36px;display:none;fill:none;stroke:var(--success-text);
  stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round;
  stroke-dasharray:20;stroke-dashoffset:20}
.pj-bloqueo.is-exito .pj-spinner--lg{display:none}
.pj-bloqueo.is-exito .pj-check{display:block;animation:pj-check-dibujar .4s ease forwards}
@keyframes pj-check-dibujar{to{stroke-dashoffset:0}}
@media (prefers-reduced-motion:reduce){.pj-spinner--lg{animation-duration:1.6s !important}}

/* ── Banner de sin conexión ────────────────────────────────── */
.pj-offline{position:fixed;top:var(--nav-h,52px);left:0;right:0;z-index:150;
  background:var(--warning-bg);color:var(--warning-text);
  padding:8px 16px;text-align:center;font-size:13px;font-weight:600}

/* ── Barra de progreso superior (GET) ─────────────────────── */
.pj-progreso{position:fixed;top:var(--nav-h,52px);left:0;height:2px;width:0;
  background:var(--primary-600);z-index:200;opacity:0;transition:opacity .2s;pointer-events:none}
.pj-progreso.on{opacity:1;animation:pj-progreso-avance 3s cubic-bezier(.1,.6,.2,1) forwards}
@keyframes pj-progreso-avance{from{width:0}to{width:90%}}
@media (prefers-reduced-motion:reduce){.pj-progreso.on{animation:none;width:60%}}

.toast-accion{flex:none;border:0;background:none;padding:0;
  color:var(--primary-500);font-size:13px;font-weight:700;text-decoration:underline;cursor:pointer}
@media (max-width:900px){
  .toast{bottom:calc(var(--tabbar-h) + env(safe-area-inset-bottom) + 12px)}
  /* En Vender, la sell-bar flotante (52px + 10px de separación) vive
     encima del tabbar — el toast tiene que despejarla también, si no queda
     tapado por ella. No se condiciona a [hidden]: hoy .sell-bar{display:flex}
     no respeta ese atributo y el botón queda visible igual (bug aparte, no
     de esta fase) — más vale un toast un poco más arriba de lo necesario
     que uno tapado. */
  body:has(.sell-bar) .toast{
    bottom:calc(var(--tabbar-h) + env(safe-area-inset-bottom) + 74px)}
}

/* ── Selector de emoji (formulario de categoría) ─────────── */
.emoji-pick{display:flex;flex-wrap:wrap;gap:4px;margin-top:10px}
.emoji-pick button{width:34px;height:34px;font-size:19px;line-height:1;border:1px solid var(--line);
  border-radius:var(--r-s);background:var(--surface);cursor:pointer}
.emoji-pick button:hover{background:var(--surface-2)}
.emoji-pick button.on{border-color:var(--accent);background:var(--accent-soft)}

/* ============================================================
   MÓVIL — navegación táctil
   Va al final a propósito: .view tiene reglas de padding repartidas en
   varios @media de este archivo (900/760), y la de acá necesita ganarles
   a todas sin depender de en qué orden quedaron escritas las otras.
   ============================================================ */
:root{ --tabbar-h: 58px; }

.tabbar{position:fixed;left:0;right:0;bottom:0;z-index:56;
  height:calc(var(--tabbar-h) + env(safe-area-inset-bottom));
  padding-bottom:env(safe-area-inset-bottom);
  background:var(--glass);backdrop-filter:blur(14px);border-top:1px solid var(--line)}
.tab-btn{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:2px;border:0;background:none;cursor:pointer;color:var(--ink-3);min-width:0}
.tab-btn svg{width:21px;height:21px;stroke-width:1.7}
.tab-btn span{font-size:10.5px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}
/* Píldora detrás del ícono en el ítem activo — no todo el botón, para no
   competir con la etiqueta de abajo. */
.tab-ico{position:relative;display:grid;place-items:center;width:56px;height:32px;
  border-radius:var(--radius-full);transition:background .15s}
.tab-btn.on{color:var(--primary-600)}
.tab-btn.on .tab-ico{background:var(--primary-50)}
.tab-badge{position:absolute;top:-2px;right:6px;min-width:16px;height:16px;padding:0 4px;
  border-radius:999px;background:var(--danger-fill);color:#fff;font-size:9.5px;font-weight:700;
  line-height:16px;text-align:center}

@media (max-width:900px){
  /* Todo lo que scrollea bajo .app necesita ese espacio de vuelta: la barra
     es position:fixed y si no, tapa la última fila de cada vista. */
  .view{padding-bottom:calc(22px + var(--tabbar-h) + env(safe-area-inset-bottom))}

  /* El menú de abajo se ve SIEMPRE, en cualquier módulo: ningún velo, hoja
     o modal puede taparlo ni volverlo intocable. Todo lo que se abre encima
     del contenido se detiene justo arriba de la barra, nunca sobre ella. */
  .scrim,.drawer{bottom:calc(var(--tabbar-h) + env(safe-area-inset-bottom))}
}

/* ── Hoja «Más»: todos los módulos, agrupados igual que el riel ────────── */
.sheet{position:fixed;left:0;right:0;
  bottom:calc(var(--tabbar-h) + env(safe-area-inset-bottom));z-index:61;
  max-height:92dvh;background:var(--surface);border-radius:var(--r-l);
  box-shadow:var(--shadow-3);
  /* No alcanza con "100% + margen": la hoja ya descansa con el borde
     inferior tabbar-h arriba del piso real, así que ocultarla tiene que
     viajar ADEMÁS esa distancia -- si no, con pocos módulos (hoja corta)
     una franja se queda visible por encima del menú aunque esté "cerrada". */
  transform:translateY(calc(100% + var(--tabbar-h) + env(safe-area-inset-bottom) + 20px));
  transition:transform .22s ease;
  display:flex;flex-direction:column}
.sheet.on{transform:none}
.sheet-h{display:flex;align-items:center;gap:10px;padding:14px 8px 10px 18px;
  border-bottom:1px solid var(--line);flex:none}
.sheet-h b{font-size:15px}
.sheet-body{overflow-y:auto;padding:6px 10px calc(14px + env(safe-area-inset-bottom));flex:1}

/* Dentro de la hoja el texto siempre se ve -- no hay cursor que lo revele
   como en el riel de escritorio -- y cada fila mide lo mínimo táctil. */
#moreBody .rail-grp{opacity:1}
#moreBody .rail-btn{height:var(--ctl-h,48px)}
#moreBody .rail-btn span{opacity:1;font-size:15px}
#moreBody .rail-btn svg{width:19px;height:19px}
#moreBody .rail-grp::before{display:none}
#moreBody .rail-grp{margin-top:18px;padding:0 8px}
#moreBody .rail-group:first-of-type .rail-grp{margin-top:6px}

/* ── Modal centrado → hoja de pantalla completa ──────────────────────────
   El lateral (.drawer sin .centrado) ya es ancho completo por debajo de
   560px; solo le falta el respiro de arriba/abajo, que se agrega junto con
   el resto de los paneles en el bloque de @media(max-width:900px) de más
   arriba. El centrado sí cambia de forma: pasa de tarjeta flotante a
   pantalla completa, porque a este ancho una tarjeta de 94vw con 12px de
   aire a cada lado deja los formularios de 9+ campos casi tan apretados
   como el propio borde. Ni uno ni otro llegan al borde inferior real: se
   detienen arriba del menú (top y bottom fijos, sin height -- así el alto
   sale solo de la resta entre los dos, en vez de pelear con un height fijo
   que ganaría la pulseada y tapase el menú otra vez). */
@media (max-width:720px){
  .drawer.centrado{top:0;left:0;right:0;
    bottom:calc(var(--tabbar-h) + env(safe-area-inset-bottom));
    width:100%;max-width:100%;max-height:none;border-radius:0;border:0;
    transform:translateY(6%);opacity:0}
  .drawer.centrado.on{transform:translateY(0)}
  .drawer-h{padding-top:calc(14px + env(safe-area-inset-top))}

  /* .drawer.hoja: mismas reglas de .centrado de arriba, pero ancladas abajo
     con las esquinas de arriba redondeadas y la manija visible — una hoja,
     no una pantalla completa. Va DESPUÉS de .centrado a propósito: gana por
     orden de aparición con la misma especificidad. */
  .drawer.hoja{top:auto;
    bottom:calc(var(--tabbar-h) + env(safe-area-inset-bottom));
    max-height:var(--hoja-alto,92dvh);border-radius:var(--radius-lg) var(--radius-lg) 0 0;
    transform:translateY(100%)}
  .drawer.hoja.on{transform:translateY(0)}
  .drawer.hoja .drawer-h{padding-top:2px}
  .drawer.hoja .drawer-manija{display:flex;justify-content:center;padding:10px 0 6px;
    cursor:grab;touch-action:none}
  .drawer.hoja .drawer-manija span{width:36px;height:4px;border-radius:999px;
    background:var(--border-strong)}

  /* Formularios de un campo por fila: dos columnas de 210px con el input
     genérico ya en 48px de alto se sienten apretadas, y span2 deja de
     significar algo cuando ya es una sola columna. */
  .form-grid{grid-template-columns:1fr}
  .fg.span2{grid-column:auto}
}

/* ── Listas marcables: casilla de dedo, no de cursor ─────────────────────
   pointer:coarse alcanza también a una tablet ancha con pantalla táctil,
   no solo al teléfono -- por eso va aparte del ancho. */
@media (pointer:coarse), (max-width:720px){
  input[type=checkbox],input[type=radio]{width:19px;height:19px}
  .faceta-op{min-height:44px;padding:5px 8px}
  .check-row{min-height:44px;align-items:center}
  .color-op{padding:9px 14px 9px 10px}
  .cmp-tick{min-height:36px}
  .sw{width:22px;height:22px}
  /* icon-btn (campana, cerrar de hoja/ficha) es de dedo aquí -- .sm se deja
     como está: es para íconos secundarios dentro de filas ya angostas. */
  .icon-btn:not(.sm){width:44px;height:44px}
}

/* ── Tabla → tarjeta ──────────────────────────────────────────────────────
   table.dt es LA tabla del módulo: la arma PJ.tabla() (core.js) para casi
   todas las vistas, así que este único bloque alcanza a más de 40 tablas
   sin tocarlas una por una. Cada <td> ya trae data-label (el mismo texto
   del encabezado); acá solo se lee para pintarlo como etiqueta de la fila.
   Las únicas tablas que NO pasan por acá son las de impresión (ticket,
   acta) -- ahí una grilla fija es justamente lo que hace falta -- y las
   de comparación (.cmp, .plan), donde ver las columnas una al lado de la
   otra es el punto: esas siguen con scroll horizontal. */
@media (max-width:720px){
  .tblwrap{overflow-x:visible}
  table.dt,table.dt thead,table.dt tbody,table.dt tfoot,table.dt tr,table.dt th,table.dt td{display:block}
  table.dt thead{display:none}
  table.dt tr{position:relative;border:1px solid var(--line);border-radius:var(--r-m);
    margin-bottom:10px;padding:2px 12px;background:var(--surface)}
  /* display:flow-root (no flex): una celda puede traer texto suelto +
     <b>/<br>/<small> como hijos sueltos -- con flex, cada uno se vuelve un
     ítem aparte y justify-content:space-between los desparrama ("CC" lejos
     del número). Con flow-root + float en la etiqueta, todo el resto fluye
     como un solo bloque de texto pegado a la derecha. */
  table.dt td{display:flow-root;padding:8px 0;border-bottom:1px solid var(--line);
    text-align:right}
  table.dt td:last-child{border-bottom:0}
  table.dt td[data-label]::before{content:attr(data-label);float:left;margin-right:12px;
    font-family:var(--font-cond);font-size:11px;letter-spacing:.08em;text-transform:uppercase;
    color:var(--ink-3)}
  /* La primera celda es el título de la fila: sin etiqueta, grande, a la
     izquierda -- salvo que esa primera celda sea una casilla de selección
     (el giro de PayJoy), en cuyo caso el título es la que sigue. */
  table.dt tr>td:first-child:not(.chk){text-align:left;border-bottom:0;
    padding-top:12px;font-weight:700;font-size:14.5px;color:var(--ink)}
  table.dt tr>td:first-child:not(.chk)::before{content:none}
  table.dt tr:has(>td.chk)>td:nth-child(2){text-align:left;border-bottom:0;
    padding-top:12px;font-weight:700;font-size:14.5px;color:var(--ink)}
  table.dt tr:has(>td.chk)>td:nth-child(2)::before{content:none}
  table.dt td.chk{position:absolute;top:12px;right:10px;padding:0;border:0;width:auto}
  table.dt tfoot tr{background:var(--surface-2);font-weight:600}
}

/* ── Vender en el teléfono ────────────────────────────────────────────────
   En escritorio .ledger es la columna derecha, siempre a la vista -- estos
   elementos nuevos (botón de cerrar, barra fija, scrim) no hacen nada ahí.
   Por debajo de 900px la venta pasa a ser una hoja: se abre desde la barra
   fija de abajo y se cierra con la ✕, el scrim o Escape. El carrito nunca
   deja de existir en el DOM -- solo cambia si está destapado o no -- así
   que renderCart() no necesita saber en qué modo está para actualizarlo. */
.ledger-cerrar{display:none}
.sell-bar{display:none}
@media (max-width:900px){
  .ledger-cerrar{display:grid;place-items:center;width:30px;height:30px;flex:none;
    border:0;background:var(--surface-2);border-radius:8px;color:var(--ink-2);cursor:pointer}
  .ledger-cerrar svg{width:18px;height:18px}
  .ledger-h h2{flex:1}

  .ledger{position:fixed;left:0;right:0;
    bottom:calc(var(--tabbar-h) + env(safe-area-inset-bottom));z-index:62;max-height:82dvh;
    border-radius:var(--r-l);border:0;
    /* Mismo motivo que en .sheet: con el carrito vacío la hoja es corta y
       "100% + margen" no alcanza a despejar la franja del menú. */
    transform:translateY(calc(100% + var(--tabbar-h) + env(safe-area-inset-bottom) + 20px));
    transition:transform .22s ease}
  .ledger.on{transform:none}

  /* :not([hidden]) a propósito: .sell-bar{display:flex} sin esto le gana
     al [hidden] nativo (una regla de autor siempre gana a la del user-agent,
     con igual o distinta especificidad) y el botón quedaba visible con el
     carrito vacío aunque pos.js ya ponía bar.hidden=true correctamente. */
  .sell-bar:not([hidden]){display:flex;align-items:center;justify-content:space-between;gap:10px;
    position:fixed;left:12px;right:12px;bottom:calc(var(--tabbar-h) + env(safe-area-inset-bottom) + 10px);
    z-index:57;height:52px;padding:0 18px;border:0;border-radius:var(--r-l);
    background:var(--accent);color:var(--accent-on);font-weight:700;font-size:15px;
    box-shadow:var(--shadow-3);cursor:pointer}
  .sell-bar span:first-child{opacity:.85;font-weight:600;font-size:13px}
  /* .view ya deja aire para el tabbar; acá se suma el alto de la sell-bar
     (52px + 10px de separación) para que no tape la última fila de la grilla. */
  #view-pos{padding-bottom:calc(22px + var(--tabbar-h) + env(safe-area-inset-bottom) + 62px)}

  /* Filtros y estantería, más cómodos de tocar. */
  .pos-cats .cat{height:var(--ctl-h,38px);font-size:14px}
  .prods{grid-template-columns:repeat(auto-fill,minmax(150px,1fr))}
  .pos-hint{display:none}
}

