/* ═══════════════════════════════════════════════════════════════════════
   public/pos.css — capa POS sobre el design system del OS (ADR-11, E12)
   ───────────────────────────────────────────────────────────────────────
   Carga SIEMPRE después de /os-assets/mrj-palette.css y
   /os-assets/lamaruja-ds.css. NUNCA redeclara un token existente de esos
   dos ficheros — solo añade tokens con prefijo `--pos-` y clases con
   prefijo `.pos-` (regla de UX_SYSTEM.md §2.3: un simple grep detecta
   cualquier intento de reescritura). Techo de motion en flujo de cobro:
   200ms (E12). Objetivo táctil mínimo 48px, teclado numérico 72px (E12).
   ═══════════════════════════════════════════════════════════════════════ */

/* ── TONO CLARO FIJO (enmienda E27, decisión de Joni 2026-09-09) ──────────
   LaMaruja POS se ve SIEMPRE en claro, en todos los terminales: el iPad del
   mostrador, el PC de tienda, los móviles del comandero y la pantalla del
   obrador. No sigue el tema del sistema operativo del dispositivo.
   Motivo operativo: un terminal compartido no puede cambiar de aspecto según
   quién configuró ese iPad; y el personal aprende UNA pantalla, no dos.
   Cómo se consigue, sin redeclarar ningún token del design system del OS
   (regla de la cabecera de este fichero): `lamaruja-ds.css` es claro por
   defecto y SOLO se oscurece con el atributo `[data-theme="dark"]` explícito
   (no usa `@media (prefers-color-scheme: dark)`) — verificado en
   lamaruja-ds.css:190. Así que basta con (a) declarar el esquema de color de
   la página y (b) no poner nunca ese atributo: `pos-core.js` fija
   `data-theme="light"` al arrancar como blindaje, por si algún día el OS
   añadiera una media query o alguien copiara un shell con el atributo puesto.
   Si algún día hace falta un modo oscuro (p.ej. una pantalla de obrador en
   una zona sin luz), sería una decisión de producto y se activaría por
   dispositivo desde admin, nunca automáticamente por el sistema. */
html { color-scheme: light; }
/* La tipografía de la herramienta se aplica a la capa POS entera. No
   redeclara ningún token del OS: solo dice qué familia usa este cuerpo. */
body { font-family: var(--pos-font); font-variant-numeric: tabular-nums; -webkit-font-smoothing: antialiased; }

/* ── `hidden` gana SIEMPRE al display de un componente ───────────────────
   VERIFICADO EN NAVEGADOR (11/09): el pie "En turno" de la pantalla de
   acceso se quedaba visible con su rótulo y sin contenido, porque el código
   lo ocultaba con la propiedad `hidden` del elemento pero la regla del
   componente le imponía `display: flex`, que gana por especificidad. El
   fallo es de familia: cualquier componente de este fichero que declare un
   display y se oculte por `hidden` tiene el mismo problema, y hasta ahora se
   iba parcheando caso por caso (`.pos-toast[hidden]`, `.pos-admin__seccion[hidden]`).
   Esta regla lo cierra de raíz para toda la capa POS. No se puede quitar el
   `!important`: compite contra reglas de componente más específicas. */
[hidden] { display: none !important; }

/* ═══════════════════════════════════════════════════════════════════════
   SISTEMA VISUAL V3 — «que parezca de 2026» (Joni, 15/09/2026)
   ───────────────────────────────────────────────────────────────────────
   Joni trajo quince capturas de seis TPV modernos (RestroBit, Croizan,
   bitepoint, D.CC, panze, Fummo) con un encargo claro: «copia el estilo de
   todos para crear un diseño propio, con nuestros colores, y dale el toque
   moderno e interactivo que tienen estos».

   Lo que TODOS comparten, y que es lo que se adopta aquí:

   1. **La app vive dentro de una tarjeta**, no a sangre: lienzo gris muy
      claro alrededor y esquinas grandes. Da sensación de aplicación, no de
      página web.
   2. **Superficies escalonadas** en tres niveles (lienzo → tarjeta →
      elemento hundido) separadas por sombra difusa y bordes de 1px muy
      claros, nunca por líneas marcadas.
   3. **Radios generosos**: 12px en controles, 16-20px en tarjetas, 24-28px
      en el lienzo. Las píldoras (chips, estados) van a radio completo.
   4. **Un solo color de acento** haciendo casi todo el trabajo: el activo de
      la navegación, el precio, el botón principal. Aquí es el naranja de
      LaMaruja (#F59800), que da la casualidad de ser casi el mismo naranja
      que usan tres de las seis referencias.
   5. **Estado = punto de color + palabra**, en píldora de fondo suave.
      Nunca solo color (no se lee) ni solo texto (no se ve de lejos).
   6. **Cifras grandes y con peso**, etiquetas pequeñas en gris medio. El
      número es el protagonista de cada tarjeta.
   7. **Acción principal = barra ancha con el importe dentro** al pie del
      panel («Cobrar · 78,00 €»), nunca un botón pequeño perdido.

   TIPOGRAFÍA: Plus Jakarta Sans, variable, autoalojada (27 KB para todos
   los pesos). Sustituye a Paytone One + Lato DENTRO DEL TPV — el encargo
   fue explícito: «vamos a elegir una tipografía mucho más moderna de la que
   tenemos actualmente». Paytone One sigue siendo la tipografía de la MARCA
   (logo, web, etiquetas, documentos); esto es la tipografía de la
   HERRAMIENTA, que es otra cosa y tiene otro trabajo: números legibles de
   un vistazo y etiquetas cortas a tamaño pequeño.

   Se declara aquí, con prefijo `--pos-`, sin tocar `--font-heading` ni
   `--font-body` del design system del OS (regla de la cabecera de este
   fichero: la capa POS nunca redeclara un token del OS).
   ═══════════════════════════════════════════════════════════════════════ */
@font-face {
  font-family: 'Plus Jakarta Sans';
  src: url('/fonts/plus-jakarta-sans-variable.woff2') format('woff2-variations');
  font-weight: 200 800;
  font-style: normal;
  font-display: swap;
}
/* Inter para los TÍTULOS (Joni, 15/09/2026: «incorpora la tipografía Inter
   Bold para los títulos»). Dos tipografías con un trabajo claro cada una:
   Inter manda en los rótulos —es la que mejor aguanta en bold a tamaño
   grande, y la que usan casi todas las referencias que trajo—, y Plus
   Jakarta Sans se queda con el cuerpo, las etiquetas y los números, donde
   su forma más abierta se lee mejor de un vistazo a tamaño pequeño. */
@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-variable.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

:root {
  /* Tipografía de la herramienta (ver el bloque de arriba) */
  --pos-font: 'Plus Jakarta Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  /* Los títulos van en Inter Bold (ver el @font-face de arriba). */
  --pos-font-display: 'Inter', 'Plus Jakarta Sans', system-ui, sans-serif;
  --pos-peso-titulo: 700;

  /* Superficies escalonadas (punto 2) */
  --pos-v3-lienzo: #F4F1EC;          /* el gris cálido de alrededor de la app */
  --pos-v3-carta: #FFFFFF;           /* la tarjeta que contiene cada zona */
  --pos-v3-hundido: #FAF7F2;         /* lo que va DENTRO de una tarjeta */
  --pos-v3-linea: rgba(76, 44, 28, .10);
  --pos-v3-linea-fuerte: rgba(76, 44, 28, .18);

  /* Radios (punto 3) */
  --pos-v3-r-control: 12px;
  --pos-v3-r-carta: 18px;
  --pos-v3-r-lienzo: 26px;

  /* Sombras difusas, nunca duras — todas en el marrón de marca, jamás negro */
  --pos-v3-sombra-1: 0 1px 2px rgba(19, 7, 2, .04), 0 2px 8px rgba(19, 7, 2, .04);
  --pos-v3-sombra-2: 0 2px 6px rgba(19, 7, 2, .05), 0 12px 24px -8px rgba(19, 7, 2, .10);
  --pos-v3-sombra-3: 0 8px 16px rgba(19, 7, 2, .06), 0 28px 56px -16px rgba(19, 7, 2, .18);

  /* Texto */
  --pos-v3-texto: #24160F;
  --pos-v3-suave: #8A7565;           /* etiquetas, unidades, texto secundario */

  /* Estados (punto 5) — fondo suave + texto legible, uno por estado */
  --pos-v3-ok-bg: #E7F5EC;      --pos-v3-ok-tx: #1B7A43;
  --pos-v3-espera-bg: #FEF3DA;  --pos-v3-espera-tx: #9A6B05;
  --pos-v3-alerta-bg: #FDECE4;  --pos-v3-alerta-tx: #B4401A;
  --pos-v3-info-bg: #EAF1FB;    --pos-v3-info-tx: #2B5FA8;

  /* Tokens táctiles — mínimos WCAG 2.5.8 AA + margen de guante/mano húmeda */
  --pos-tap-min: 48px;
  --pos-tap-comfort: 64px;
  --pos-tap-gap: 8px;

  /* Densidades */
  --pos-density-tpv: 1;
  --pos-density-comandero: 1.1;

  /* Teclado numérico de pago/PIN */
  --pos-keypad-key: 72px;
  /* CORREGIDO por el corrector final de Tandas 0/1 (hallazgo #4, fidelidad
     MEDIA): la maqueta v4 (.keypad) usa gap:10px, no 6px. */
  --pos-keypad-gap: 10px;

  /* Chips de modificador */
  --pos-chip-h: 44px;

  /* Tarjetas de mesa por estado — derivadas del hue/croma OKLCH del OS
     (Fase C todavía no las usa en A4, se declaran ya para no redeclarar
     tokens más adelante en otro fichero). */
  --pos-mesa-libre: var(--pos-card);
  --pos-mesa-ocupada: oklch(75% calc(var(--accent-c) * 0.6) var(--accent-h));
  --pos-mesa-cuenta: var(--warn-text);
  --pos-mesa-atencion: var(--bad-text);

  /* Techo de motion del flujo de cobro/PIN (principio 7 UX_SYSTEM.md) */
  --pos-t-cobro: 200ms;

  /* ── Añadidos al portar la maqueta v4 (contrato de diseño, 2026-09-10) ──
     Tokens que la maqueta usa y que NI mrj-palette.css NI lamaruja-ds.css
     declaran con ese nombre — con prefijo `--pos-` según la regla de la
     cabecera. El resto de colores/tipos/sombras/radios de la maqueta SÍ
     existen ya en el DS y se reutilizan tal cual (ver UX_SYSTEM_IMPLEMENTACION.md
     §1 para la tabla completa de equivalencias). */
  --pos-t: 180ms var(--ease);              /* transición por defecto de la maqueta (--t) */
  --pos-ok-bg: #E4EBD3;                    /* fondo de estado OK (maqueta --ok-bg) */
  --pos-warn-bg: #FBE7C0;                  /* fondo de estado AVISO (maqueta --warn-bg) */
  --pos-bad-bg: #F6D9D4;                   /* fondo de estado ERROR (maqueta --bad-bg) */
  --pos-info-bg: #FBE3B8;                  /* fondo de estado INFO (maqueta --info-bg) */

  /* ── Sistema visual v2 «Horno» (docs/SISTEMA_VISUAL_V2.md, 2026-09-11) ──
     Sombra SIEMPRE cálida (tinta --mrj-ink con alfa, principio 8: "un
     un negro puro sin tinte en el CSS es un error de marca") y radios de forma por
     nivel de componente (§2.3). El rayado de toldo (principio 6) va en su
     propio token, aislado a propósito, para poder apagarlo con un solo
     cambio si Joni prefiere la barra de avance lisa. */
  /* CORREGIDO (maqueta v5 §9.2, 2026-09-11): la maqueta aprobada fija estos
     dos valores literales — no eran exactamente estos (.04/.10 y 8px/24px
     en vez de 10px/28px). Gana la maqueta. */
  --pos-sombra-1: 0 1px 2px rgba(19, 7, 2, .05);   /* reposo */
  --pos-sombra-2: 0 10px 28px rgba(19, 7, 2, .12); /* elevado */
  --pos-r-control: 12px;  /* botones, chips de control, filtros */
  --pos-r-card: 18px;     /* tarjetas */
  --pos-r-panel: 24px;    /* paneles y hojas a pantalla completa */
  --pos-rayado: repeating-linear-gradient(135deg, var(--accent) 0 6px, transparent 6px 12px);

  /* ── Sistema visual v2 (docs/SISTEMA_VISUAL_V2.md §2.2) — los DOS únicos
     neutros nuevos que hacían falta, derivados de la marca (mocha/sand con
     alfa), nunca un gris de sistema. Todo lo demás del documento reutiliza
     tokens que ya existen (--shadow-1/2/3, --r-sm/md/lg/xl, --cat-*). */
  --pos-surface-3: rgba(234, 205, 168, .55); /* superficie hundida marcada: pista de progreso, contador inactivo */
  --pos-border-2: rgba(140, 101, 79, .42);   /* borde de énfasis sin recurrir al color de marca entero */

  /* ── Maqueta v5 APROBADA (docs/maqueta-v5-aprobada.html, §9.1, 2026-09-11)
     — LAS DOS SUPERFICIES que arreglan «se ve gris/blanco» (el hallazgo
     principal de esta pasada: `.pos-shell` iba en `--bg` (crema) y las
     tarjetas en `--surface` (un tono casi blanco) — la relación INVERTIDA
     respecto a la maqueta). `--pos-ground` es el fondo de la aplicación
     (arena); `--pos-card` es toda tarjeta/panel/hoja (crema); `--pos-line`
     es su borde. Dentro de una tarjeta crema, lo que va HUNDIDO (rejilla de
     artículos, pie del ticket, teclas del teclado numérico, segmented
     control, chip neutro) vuelve a `--pos-ground`: se invierte la relación
     otra vez y sigue sin aparecer un gris. Sustituyen a
     los dos tokens de superficie plana que traía Fase A en todo este
     fichero — no debe quedar ninguna aparición de esos dos tras esta pasada. */
  --pos-ground: #F3DEC0;
  --pos-card: var(--mrj-cream);
  --pos-line: var(--mrj-sand);

  /* ── Tintes de categoría (§6.2 SISTEMA_VISUAL_V2.md; hallazgo del
     verificador adversarial, 2026-09-11): `--cat-op/--cat-marca/--cat-b2b/
     --cat-control/--cat-finanzas` se USAN en `.pos-prod--tinte-*` y en
     `.pos-ctrl-grp--*` pero no estaban declaradas en ningún fichero del
     árbol servido — todos los tintes caían al mismo fallback sand. Se
     declaran aquí como 5 mezclas de los 4 colores de marca ya existentes
     (nunca un color nuevo): naranja puro, sand, mocha, marrón y un marrón
     oscurecido con mocha para el quinto (el más oscuro, pensado para
     Café/Bebidas — funciona bien sobre fondos claros de líquido). */
  --cat-op: var(--accent);
  --cat-marca: var(--mrj-sand);
  --cat-b2b: var(--mrj-mocha);
  --cat-control: var(--brown);
  --cat-finanzas: color-mix(in srgb, var(--brown) 65%, var(--mrj-mocha) 35%);
}

@media (prefers-reduced-motion: reduce) {
  :root { --pos-t-cobro: 0ms; --pos-t: 0ms; }
}

/* Con el flujo de cobro va TODO el motion de la capa POS (E12: "techo de
   motion en flujo de cobro 200ms" + "prefers-reduced-motion"): en vez de
   listar transition:none regla a regla, se apaga aquí para cualquier cosa
   dentro del contenedor raíz del POS. Los componentes de la maqueta que
   siguen más abajo usan siempre var(--pos-t) o var(--pos-t-cobro), nunca un
   valor de motion suelto, así que este único bloque los cubre a todos. */
@media (prefers-reduced-motion: reduce) {
  .pos-shell, .pos-shell * { transition: none !important; }
}

/* ── Reset mínimo propio (el body ya trae fondo/tipografía del reset del
   artefacto; aquí solo lo que necesita la capa POS) ─────────────────── */
.pos-shell {
  min-height: 100vh;
  /* CORREGIDO (maqueta v5 §9.1, hallazgo principal de esta pasada): el
     fondo de la aplicación es la arena `--pos-ground`, no `--bg` (crema) —
     `--bg` es el tono de TARJETA, no el de fondo; se estaba usando al
     revés de lo que manda la maqueta aprobada. */
  background: var(--pos-ground);
  color: var(--text);
  font-family: var(--font-body);
  display: flex;
  flex-direction: column;
}
.pos-shell * { box-sizing: border-box; }
/* CORREGIDO en la pasada de acabado (E31, 2026-09-10) — EL HALLAZGO
   PRINCIPAL de esta pasada. Este reset se quedaba a medias — solo heredaba
   la tipografía. `lamaruja-ds.css` sí trae el reset completo de botón
   (`.shell button{border:none;background:none;color:inherit;
   cursor:pointer}`, línea ~1028), pero está encerrado bajo `.shell`, la
   clase raíz DEL OS — el POS usa su propia raíz `.pos-shell` y ese selector
   nunca llega a coincidir. Resultado, verificado abriendo el POS real en el
   navegador (inspeccionando estilos computados): CUALQUIER botón sin fondo
   propio (`.pos-modes button`, `.pos-cat`, `.pos-obr-tabs button`,
   `.pos-ctrl .nav button`…) caía al estilo nativo del navegador — fondo
   gris `rgb(239,239,239)` y borde `2px outset` en relieve — que es
   exactamente el "gris" que Joni dijo que no le gustaba (E30): no eran
   botones pintados a propósito en gris, eran botones SIN pintar. */
:where(.pos-shell) button { font-family: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
/* CORREGIDO en SISTEMA_VISUAL_V2 (2026-09-11) — Joni ve OTRA VEZ botones
   grises, ahora en el menú ≡ de la cabecera, y la regla de arriba no podía
   arreglarlo por sí sola: además, esta misma edición del bloque se había
   corrompido en algún punto anterior (comentario sin cerrar que se comía la
   regla real — verificado leyendo el fichero servido: el selector nunca
   llegaba a aplicarse). Causa de fondo: `.pos-menu-pop` se monta en
   `#tpv-menu-slot`, FUERA de `.pos-shell`, así que el reset de arriba no le
   llega. Se añade el MISMO reset colgado de `body` con especificidad cero
   (`:where()`), para que cualquier pantalla nueva quede cubierta aunque
   monte algo fuera de `.pos-shell` — no sustituye al de arriba (se deja por
   claridad histórica y porque no hace daño tenerlo dos veces), solo lo
   amplía. Cualquier clase de componente (`.pos-btn`, `.pos-chip`,
   `.pos-prod`, `.pos-menu-pop button`…) que declare su propio fondo/borde
   sigue ganando la cascada sin tocarla — este reset solo entra donde nadie
   más pinta nada. Regla de marca (skill lamaruja §2.1): nunca gris. */
:where(body) button, :where(body) [role="button"] { font-family: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
/* CORREGIDO por el corrector final (hallazgo MEDIA, verificado en vivo con
   getComputedStyle en 3 puntos reales — #campo-codigo de /emparejar/, el
   input de PIN de Control y el <select> de Rol de Control — los tres en
   'Arial'): a diferencia de los botones (regla de arriba), los campos de
   formulario NUNCA heredan font-family del body por defecto en ningún
   navegador (UA stylesheet) — nadie lo había corregido aquí. Mismo criterio
   de especificidad cero (`:where()`) para que cualquier pantalla nueva
   quede cubierta aunque el campo no lleve `.pos-campo`. */
:where(body) input, :where(body) select, :where(body) textarea { font-family: inherit; }

/* CORREGIDO por el corrector final (hallazgo ALTA, verificado en vivo): la
   maqueta aprobada (docs/maqueta-v4.html) fija Paytone One en TODO h1/h2/h3
   de forma global (`h1,h2,h3{font-family:var(--fh);font-weight:400}`);
   pos.css no tenía el equivalente y varios títulos reales (Control →
   'Personal y fichajes', 'Ahora mismo', 'Rol operativo y PIN', 'Tu PIN' en
   /login/ antes de que esa pantalla tuviera su propia regla) se veían en
   Lato de cuerpo en vez del titular de marca. Las clases más específicas
   que ya fijan su propio font-family (.pos-ctx h2, .pos-kpi .v, .pos-acc-pin
   h2…) siguen ganando la cascada exactamente igual que hoy — esta regla
   solo entra donde ningún h1/h2/h3 real tenía ya una declaración propia. */
/* Con Paytone One bastaba el 400 (era su único peso). Plus Jakarta Sans es
   variable, así que los titulares piden su peso explícito o salen en
   regular y la jerarquía desaparece. */
.pos-shell h1, .pos-shell h2, .pos-shell h3 { font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo); letter-spacing: -.02em; }

/* ── Barra superior común (empleado/dispositivo/red/sync) ───────────── */
.pos-topbar {
  display: flex;
  align-items: center;
  gap: var(--sp-4, 16px);
  padding: var(--sp-3, 12px) var(--sp-4, 16px);
  background: var(--pos-card);
  border-bottom: 1px solid var(--border);
  min-height: var(--pos-tap-min);
}
.pos-topbar__marca {
  font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo);
  color: var(--brown);
  font-size: var(--fs-lg, 1.1rem);
}
.pos-topbar__spacer { flex: 1; }
.pos-topbar__dato { font-size: var(--fs-sm, .875rem); color: var(--muted); }

/* ── Banner offline (§4 UX_SYSTEM.md) ────────────────────────────────── */
.pos-banner-offline {
  width: 100%;
  min-height: 40px;
  display: none;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2, 8px);
  font-size: var(--fs-sm, .875rem);
  padding: var(--sp-2, 8px) var(--sp-4, 16px);
}
.pos-banner-offline[data-estado="activo"] {
  display: flex;
  background: color-mix(in srgb, var(--warn-text) 14%, var(--pos-card));
  color: var(--warn-text);
}
.pos-banner-offline[data-estado="critico"] {
  display: flex;
  background: color-mix(in srgb, var(--bad-text) 16%, var(--pos-card));
  color: var(--bad-text);
}
.pos-banner-offline__reintentar {
  min-width: var(--pos-tap-min);
  min-height: var(--pos-tap-min);
  border: 1px solid currentColor;
  background: transparent;
  color: inherit;
  border-radius: var(--r-sm, 6px);
  cursor: pointer;
}

/* ── Botón táctil genérico (§4.5 SISTEMA_VISUAL_V2.md, contrato v2) ────
   48px de alto mínimo, radio de control (12px), Paytone One. Sustituye al
   acabado de fidelidad-a-la-maqueta-v4 que tenía este bloque: la v2 es el
   contrato vigente y pide explícitamente `--primario` con texto `--brown`
   (no `--ink`) y ninguna variante en reposo con sombra — la sombra se
   reserva para la interacción (principio "la sombra cálida se reserva para
   lo que de verdad flota", §2.3). */
.pos-btn {
  min-width: var(--pos-tap-min);
  min-height: var(--pos-tap-min);
  padding: var(--sp-2, 8px) var(--sp-3, 12px);
  border-radius: var(--pos-r-control);
  border: 1.5px solid var(--mrj-sand);
  background: var(--pos-card);
  color: var(--brown);
  font-family: var(--pos-font-display);
  font-weight: 400;
  font-size: var(--fs-md, 16px);
  cursor: pointer;
  transition: transform var(--pos-t), background var(--pos-t), box-shadow var(--pos-t), filter var(--pos-t), border-color var(--pos-t);
}
/* Feedback al pulsar: encogido sutil + tinte de fondo (nunca solo tamaño,
   imperceptible en un iPad). */
.pos-btn:active { transform: scale(.98); background: var(--pos-ground); }
.pos-btn--primario:active { background: var(--accent); filter: brightness(.94); }
.pos-btn--marron:active { filter: brightness(1.15); }
.pos-btn--fantasma:active { background: var(--pos-ground); }
.pos-btn--peligro:active { filter: brightness(.96); }
/* Foco visible: anillo naranja de 3px separado 2px — CORREGIDO (regresión
   detectada al verificar contra la maqueta aprobada, que manda en esto:
   `docs/maqueta-v4.html:22` fija literalmente
   `button:focus-visible,[tabindex]:focus-visible{outline:3px solid
   var(--orange);outline-offset:2px}`. 2px ya se probó y se corrigió una vez
   (E31): en un TPV táctil usado también con teclado o lector de código de
   barras el foco tiene que notarse más, no menos — no es una preferencia
   de esta pasada, es fidelidad a la maqueta que manda en estructura. */
.pos-btn:focus-visible,
.pos-shell :where(a,button,input,select,textarea,[tabindex]):focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}
/* Estados :hover — SOLO con puntero fino real (ratón/trackpad, escritorio
   de admin): un móvil o un iPad no tienen hover de verdad y un `:hover`
   sin esta guarda se queda "pegado" tras el primer toque, que es peor que
   no tener hover. Nunca sustituye al feedback de `:active` de arriba, que
   es el que de verdad importa en un terminal táctil. Interacción de la v2:
   se eleva 1px y saca sombra cálida al pasar. */
@media (hover: hover) and (pointer: fine) {
  .pos-btn:hover:not(:disabled) { background: var(--pos-ground); transform: translateY(-1px); box-shadow: var(--pos-sombra-2); }
  .pos-btn--primario:hover:not(:disabled) { filter: brightness(1.06); }
  .pos-btn--marron:hover:not(:disabled) { filter: brightness(1.12); }
  .pos-btn--fantasma:hover:not(:disabled) { background: var(--pos-ground); }
  .pos-btn--peligro:hover:not(:disabled) { filter: brightness(1.04); }
  .pos-chip:not(.on):hover { border-color: var(--mocha, var(--brown)); }
  .pos-prod:not(.out):hover { box-shadow: var(--pos-sombra-2); transform: translateY(-2px); }
  .pos-mesa:hover { box-shadow: var(--pos-sombra-2); }
  .pos-modes button:not(.on):hover { opacity: 1; background: rgba(252, 236, 215, .1); }
}
/* CORREGIDO (regresión detectada al verificar contra la maqueta aprobada:
   `docs/maqueta-v4.html:82` fija literalmente `.btn.primary{background:
   var(--orange);color:var(--ink);border-color:transparent;box-shadow:0 2px
   0 rgba(76,44,28,.25)}` — texto `--ink` (casi negro), no `--brown`, y CON
   el relieve de sombra. Ya se corrigió una vez con esta misma cita — no es
   un matiz de esta pasada, es fidelidad literal a lo que manda en
   estructura/acabado base). El relieve se "hunde" de verdad al pulsar
   (§4.1 SISTEMA_VISUAL_V2.md: el botón principal es un botón físico). */
.pos-btn--primario { background: var(--accent); color: var(--mrj-ink); border-color: transparent; box-shadow: 0 2px 0 rgba(76, 44, 28, .25); }
.pos-btn--primario:active { box-shadow: 0 0 0 rgba(76, 44, 28, .25); }
.pos-btn--fantasma { background: transparent; border-color: var(--mrj-sand); color: var(--brown); }
.pos-btn--ancho { width: 100%; max-width: 320px; margin-top: var(--sp-3, 12px); }
.pos-btn:disabled { opacity: .45; cursor: not-allowed; }
.pos-btn--marron { background: var(--brown); color: var(--mrj-cream); border-color: transparent; }
/* `--peligro`: fondo `--pos-bad-bg` (mismo pastel cálido que `.pos-badge.bad`,
   nunca un rojo puro ni un borde suelto), texto en el rojo fuerte de marca. */
.pos-btn--peligro { background: var(--pos-bad-bg); color: var(--bad-text); border-color: transparent; }
.pos-btn--grande { min-height: 64px; font-size: var(--fs-lg, 1.1rem); width: 100%; }

/* ── Producto (rejilla del TPV) ──────────────────────────────────────── */
.pos-grid-productos {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: var(--pos-tap-gap);
  padding: var(--sp-4, 16px);
  overflow-y: auto;
}
.pos-btn-producto {
  min-height: var(--pos-tap-comfort);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  padding: var(--sp-3, 12px);
  border-radius: var(--r-md, 10px);
  border: 1px solid var(--border);
  background: var(--pos-ground);
  color: var(--text);
  text-align: left;
  cursor: pointer;
  /* CORREGIDO (verificado en navegador durante la tanda de movimiento del
     14/09, con los tokens reales de lamaruja-ds.css): `--t-fast` YA es
     `90ms var(--ease)` (duración + curva juntas) — con el `ease` suelto de
     detrás había DOS funciones de curva en el mismo `<single-transition>`,
     valor inválido de principio a fin, así que el navegador lo descartaba
     entero y caía a `all 0s ease`: la tarjeta de producto llevaba toda la
     tanda anterior sin transición real, un salto seco en vez del
     hundimiento suave que pedía el diseño. Mismo defecto en
     `.pos-keypad__tecla` (abajo) — corregido ahí también. */
  transition: transform var(--t-fast, 90ms);
}
.pos-btn-producto:active { transform: scale(.97); }
.pos-btn-producto__nombre { font-weight: 700; }
.pos-btn-producto__precio { color: var(--muted); font-variant-numeric: tabular-nums; }
.pos-btn-producto[aria-disabled="true"] {
  opacity: .45;
  cursor: not-allowed;
  background-image: repeating-linear-gradient(45deg, var(--border) 0 2px, transparent 2px 10px);
}
.pos-btn-producto__badge {
  align-self: flex-start;
  font-size: var(--fs-xs, .75rem);
  padding: 2px 8px;
  border-radius: var(--r-sm, 6px);
  background: var(--bad-text);
  color: var(--pos-card);
}

/* ── Chip de variante/modificador (valores = maqueta v4 .chip; esta clase
   se declaró en Fase A pero ninguna pantalla la usaba aún, así que se
   actualiza aquí sin riesgo de romper nada ya construido) ─────────────── */
.pos-chip {
  min-height: var(--pos-chip-h);
  padding: 0 var(--sp-3, 12px);
  border-radius: 999px;
  border: 1.5px solid var(--border);
  background: var(--pos-card);
  color: var(--text);
  font-weight: 700;
  font-size: var(--fs-sm, .875rem);
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2, 8px);
  cursor: pointer;
  transition: background var(--pos-t), border-color var(--pos-t), transform var(--pos-t);
}
.pos-chip:active { transform: scale(.97); }
.pos-chip.is-activo, .pos-chip.on { background: var(--brown); color: var(--mrj-cream); border-color: var(--brown); }

/* ── Teclado numérico (PIN/pago) ─────────────────────────────────────── */
/* NOTA — verificación en navegador del 14/09 (encargo punto 1): se probó
   primero a hacer ESTE componente fluido (`width:100%` + columnas `1fr`)
   para que se encogiera solo dentro de la hoja de cobro. Se revirtió: en
   cuanto una tecla vive dentro de un contenedor con `align-items:center`
   en vez de `stretch` (p.ej. `.pos-acc-pin` del login, `.lmos-dialog-box`
   de los diálogos de PIN) el ancho `100%` no tiene ningún ancho definido
   contra el que resolverse — el teclado entero colapsaba a ~35px por
   tecla en el login, una regresión peor que el bug original, encontrada
   ANTES de entregar (comprobado con `getBoundingClientRect()` en las 4
   pantallas que usan `tecladoNumerico()`). El teclado vuelve a su tamaño
   FIJO de siempre (72px, el mínimo del propio sistema); el arreglo de
   raíz del encargo vive en `.pos-paysheet`/`.methods`/`.pos-paysheet__cobro`
   (más abajo): ahora le dan a esta caja ~320-360px de columna — de sobra
   para sus 236px — así que el teclado nunca necesita encogerse ahí. */
.pos-keypad {
  display: grid;
  grid-template-columns: repeat(3, var(--pos-keypad-key));
  gap: var(--pos-keypad-gap);
  justify-content: center;
}
.pos-keypad__tecla {
  width: var(--pos-keypad-key);
  height: var(--pos-keypad-key);
  /* CORREGIDO por el corrector final de Tandas 0/1 (hallazgo #4, fidelidad
     MEDIA): la maqueta v4 (.keypad button) usa border-radius:var(--r-lg) y
     font-family:var(--pos-font-display) (Paytone One) — aquí tenía --r-md y
     heredaba la tipografía de cuerpo (Lato). */
  border-radius: var(--r-lg);
  border: 1px solid var(--border);
  /* Tecla = elemento HUNDIDO dentro de la tarjeta que la contiene (§9.1) */
  background: var(--pos-ground);
  color: var(--text);
  font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo);
  font-size: var(--fs-xl, 1.5rem);
  cursor: pointer;
  /* CORREGIDO (mismo defecto que `.pos-btn-producto`, ver su comentario):
     `--t-fast` ya trae su curva — el `ease` de detrás duplicaba la función
     de tiempo e invalidaba la propiedad entera. La tecla de PIN/cobro se
     hundía sin transición real. */
  transition: transform var(--t-fast, 90ms), background var(--t-fast, 90ms), color var(--t-fast, 90ms);
}
/* Hallazgo (c) del verificador adversarial: solo cambiaba de tamaño al
   pulsar — se añade el mismo cambio de fondo a --brown/texto --cream que ya
   usa el teclado de la pantalla de bloqueo (`.pos-lock .pos-keypad button`),
   para que el teclado de COBRO tenga el mismo feedback de doble propiedad
   (regla dura de movimiento, §5 SISTEMA_VISUAL_V2.md). */
.pos-keypad__tecla:active { transform: scale(.95); background: var(--brown); color: var(--mrj-cream); }
.pos-keypad__tecla:disabled { opacity: .4; cursor: not-allowed; }
/* CORREGIDO por el corrector final de Tandas 0/1 (hallazgo #4, fidelidad
   MEDIA): en la maqueta v4 las 12 teclas son visualmente idénticas — ni
   siquiera "Exacto" cambia de color, solo de tamaño de letra. La tecla OK
   tenía aquí un tratamiento naranja/marrón propio que no existe en la
   maqueta; se retira para que herede el mismo aspecto que el resto. */

/* ── PIN visual (puntos, nunca muestra el dígito) ────────────────────── */
.pos-pin-puntos { display: flex; gap: var(--sp-2, 8px); justify-content: center; }
/* CORREGIDO por el corrector final de Tandas 0/1 (hallazgo #6, fidelidad
   BAJA): la maqueta v4 (.pinbox i) usa 16×16px y borde var(--mocha), no
   18×18px y var(--muted). */
.pos-pin-punto {
  width: 16px; height: 16px; border-radius: 50%;
  border: 2px solid var(--mrj-mocha); background: transparent;
}
.pos-pin-punto.is-relleno { background: var(--brown); border-color: var(--brown); }
.pos-pin-punto.is-error { border-color: var(--bad-text); background: var(--bad-text); }

/* ── Toast ────────────────────────────────────────────────────────────── */
/* CORREGIDO por el corrector final de Tandas 0/1 (hallazgo #2, fidelidad
   ALTA): la maqueta v4 (.toast) es una píldora OSCURA fija —
   background:var(--ink); color:var(--cream) — con solo el icono ✓/✕
   coloreado (`.toast .ok`/`.ko`), nunca el texto completo. Antes esto era
   una superficie clara (el tono de tarjeta) que coloreaba el TEXTO entero según
   `--exito`/`--error`: en un fondo cream, un aviso de 2-3s casi no se veía.
   `#95A271`/`#D88170` no son valores inventados: son exactamente
   --ok-text/--bad-text del TEMA OSCURO de lamaruja-ds.css (líneas 218-219)
   — se hardcodean aquí, como ya hacía `.pos-dot` (línea ~516) con el mismo
   #95A271, porque el toast vive siempre sobre fondo oscuro
   (var(--mrj-ink)) sea cual sea el tema de la página (E27: POS fijo en
   claro), así que necesita SUS propios tonos de contraste, no
   var(--ok-text)/var(--bad-text) del tema claro activo. */
.pos-toast {
  position: fixed;
  left: 50%;
  bottom: var(--sp-6, 32px);
  transform: translateX(-50%);
  min-height: 44px;
  padding: var(--sp-2, 8px) var(--sp-4, 16px);
  border-radius: var(--r-lg);
  background: var(--mrj-ink);
  color: var(--mrj-cream);
  /* CORREGIDO en la pasada de acabado (E31): la maqueta v4 (`.toast`) usa
     shadow-3 — el toast flota por encima de TODO (z-index:100, nivel
     "diálogo"), no solo por encima de una tarjeta (shadow-2). */
  box-shadow: var(--shadow-3);
  display: flex;
  align-items: center;
  gap: var(--sp-2, 8px);
  transition: opacity var(--pos-t-cobro) ease, transform var(--pos-t-cobro) ease;
  z-index: 100;
}
.pos-toast[hidden] { display: none; }
.pos-toast .ok { color: #95A271; font-size: 18px; }
.pos-toast .ko { color: #D88170; font-size: 18px; }

/* ── Layout TPV: 3 columnas (categorías / rejilla / ticket) ──────────── */
.pos-tpv {
  flex: 1;
  display: grid;
  grid-template-columns: 200px 1fr 340px;
  min-height: 0;
}
@media (max-width: 900px) {
  .pos-tpv { grid-template-columns: 160px 1fr 280px; }
}
.pos-tpv__categorias {
  border-right: 1px solid var(--border);
  overflow-y: auto;
  padding: var(--sp-2, 8px);
  display: flex;
  flex-direction: column;
  gap: var(--pos-tap-gap);
}
.pos-tpv__categoria {
  min-height: var(--pos-tap-min);
  text-align: left;
  padding: 0 var(--sp-3, 12px);
  border-radius: var(--r-sm, 6px);
  border: 1px solid transparent;
  background: transparent;
  color: var(--text);
  cursor: pointer;
}
.pos-tpv__categoria.is-activa { background: var(--accent-100, var(--pos-card)); border-color: var(--accent); font-weight: 700; }
.pos-tpv__central { display: flex; flex-direction: column; min-height: 0; }
.pos-tpv__ticket {
  border-left: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  padding: var(--sp-4, 16px);
  gap: var(--sp-3, 12px);
  min-height: 0;
}
.pos-tpv__ticket-lineas { flex: 1; overflow-y: auto; list-style: none; margin: 0; padding: 0; }
.pos-tpv__ticket-linea {
  display: flex; justify-content: space-between; gap: var(--sp-2, 8px);
  min-height: var(--pos-tap-min); align-items: center;
  border-bottom: 1px solid var(--border);
}
.pos-tpv__vacio {
  flex: 1; display: flex; align-items: center; justify-content: center;
  text-align: center; color: var(--muted); padding: var(--sp-4, 16px);
}

/* ── Diálogo de PIN (envuelve .lmos-dialog-centrado del OS) ──────────── */
.pos-dialog-pin { display: flex; flex-direction: column; gap: var(--sp-4, 16px); align-items: center; }
.pos-dialog-pin__error {
  color: var(--bad-text);
  font-size: var(--fs-sm, .875rem);
  min-height: 1.2em;
}
@keyframes pos-shake { 0%,100%{transform:translateX(0)} 25%{transform:translateX(-6px)} 75%{transform:translateX(6px)} }
.pos-dialog-pin.is-error .pos-pin-puntos { animation: pos-shake 150ms ease; }

/* ── Login / emparejar (pantalla centrada) ───────────────────────────── */
.pos-centro {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-4, 16px);
  padding: var(--sp-6, 32px);
  text-align: center;
}
.pos-centro__marca { font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo); color: var(--brown); font-size: var(--fs-2xl, 1.75rem); }
.pos-centro__dispositivo { color: var(--muted); font-size: var(--fs-sm, .875rem); }
.pos-campo {
  min-height: var(--pos-tap-min);
  width: 100%;
  max-width: 320px;
  padding: 0 var(--sp-3, 12px);
  border-radius: var(--r-md, 10px);
  border: 1px solid var(--border);
  background: var(--pos-card);
  color: var(--text);
  font-size: var(--fs-lg, 1.1rem);
  text-align: center;
}
/* CORREGIDO por el corrector final (hallazgo ALTA, verificado en vivo: el
   <select> de "Rol" en Control → Personal, único desplegable de todo el
   POS, se veía con fondo blanco, borde gris del sistema y flecha nativa —
   exactamente el gris de sistema prohibido por la marca). `.pos-select` va
   SIEMPRE junto a `.pos-campo` (que ya pone fondo/borde/radio de marca) y
   solo añade lo específico de un <select>: quitar la flecha nativa
   (`appearance:none` en las dos variantes, Safari/iPadOS incluido — ADR E19,
   el navegador real del TPV — la pinta igual sin esto aunque cambie el
   fondo) y sustituirla por una propia en --mocha, alineación a la izquierda
   (un <select> se lee, no se centra como el PIN) y una pizca de hueco a la
   derecha para que la flecha no pise el texto. */
.pos-select {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  text-align: left;
  padding-right: 30px;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'><path d='M1 1l5 5 5-5' fill='none' stroke='%238C654F' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 10px center;
  /* Sin `background-size` explícito, un SVG sin width/height propios (solo
     viewBox) no tiene tamaño intrínseco y algunos navegadores lo estiran al
     área de fondo entera — visto en vivo: la flecha salía enorme, casi del
     alto del propio <select>. */
  background-size: 12px 8px;
  cursor: pointer;
}
.pos-error {
  color: var(--bad-text);
  min-height: 1.4em;
  font-size: var(--fs-sm, .875rem);
}
.pos-aviso {
  color: var(--muted);
  font-size: var(--fs-sm, .875rem);
}

/* ── Acceso y fichaje (maqueta v5 "Acceso y fichaje" + SISTEMA_VISUAL_V2.md
   §7.1, login/login.js + pos-ui.js `chipsEmpleadosAcceso`/`pildorasTurno`) —
   HALLAZGO al verificar en el navegador (Puppeteer, 2026-09-11): `/login/`
   ya monta `.pos-acceso`/`.pos-acc-body`/`.pos-acc-gente`/`.pos-persona`/
   `.pos-acc-turno*`/`.pos-acc-pin` desde hace una tanda, pero pos.css nunca
   les dio forma — la pantalla real se veía como texto plano sin tarjetas
   (el propio código las señala: "quien complete pos.css en paralelo").
   Es la pantalla que ve el equipo cada mañana (§7.3 SISTEMA_VISUAL_V2.md):
   dos mitades, izquierda "¿Quién eres?" en tarjetas grandes, derecha "Tu
   PIN" — fondo `--pos-ground`, las dos mitades y cada tarjeta de persona
   son `--pos-card`. Avatar con inicial, nunca foto (§9.3). ─────────────── */
.pos-acceso { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.pos-acc-body {
  flex: 1; display: grid; grid-template-columns: 1fr 380px; gap: 16px;
  padding: 16px; min-height: 0; overflow: auto;
}
@media (max-width: 760px) {
  .pos-acc-body { grid-template-columns: 1fr; }
}
.pos-acc-gente { display: flex; flex-direction: column; gap: 9px; min-height: 0; }
.pos-personas { display: flex; flex-direction: column; gap: 9px; overflow: auto; }
.pos-persona {
  display: flex; align-items: center; gap: 13px; text-align: left;
  min-height: var(--pos-tap-comfort);
  background: var(--pos-card); border: 1px solid var(--pos-line); border-radius: var(--pos-r-card);
  padding: 12px 15px;
  transition: background var(--pos-t), border-color var(--pos-t), transform var(--pos-t);
}
.pos-persona:active { transform: scale(.98); }
.pos-persona.on { background: var(--accent); border-color: transparent; }
.pos-persona .av {
  width: 42px; height: 42px; border-radius: 50%; flex-shrink: 0;
  background: var(--pos-line); color: var(--brown);
  font-family: var(--pos-font-display); font-weight: 400; font-size: 16px;
  display: grid; place-items: center;
}
.pos-persona.on .av { background: var(--mrj-cream); }
.pos-persona .nm { font-size: 16.5px; font-weight: 700; }
.pos-persona .badge { margin-left: auto; }
/* Pie "En turno": quién ha fichado ya, en píldoras compactas — reutiliza
   `.pos-chip` (base) + este modificador para el tamaño/avatar reducidos. */
.pos-acc-turno { margin-top: auto; padding-top: var(--sp-2, 8px); display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.pos-acc-turno-lista { display: flex; gap: 6px; flex-wrap: wrap; }
.pos-acc-turno-pill { min-height: auto; padding: 3px 11px 3px 3px; gap: 6px; font-size: 11.5px; }
.pos-acc-turno-pill .av {
  width: 20px; height: 20px; border-radius: 50%; flex-shrink: 0;
  background: var(--pos-line); color: var(--brown);
  font-family: var(--pos-font-display); font-weight: 400; font-size: 9px;
  display: inline-grid; place-items: center;
}
/* Mitad derecha: PIN + teclado — misma tarjeta que la izquierda. */
.pos-acc-pin {
  background: var(--pos-card); border: 1px solid var(--pos-line); border-radius: var(--pos-r-card);
  padding: 22px; display: flex; flex-direction: column; align-items: center; gap: var(--sp-2, 8px);
}
.pos-acc-pin h2 { margin: 0 0 8px; font-family: var(--pos-font-display); font-weight: 400; font-size: 17px; }

/* ── Admin: escritorio, hereda .shell del OS; layout propio de sidebar ── */
.pos-admin { flex: 1; display: grid; grid-template-columns: 220px 1fr; min-height: 0; }
.pos-admin__nav {
  border-right: 1px solid var(--border);
  padding: var(--sp-3, 12px);
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.pos-admin__nav-item {
  min-height: var(--pos-tap-min);
  text-align: left;
  padding: 0 var(--sp-3, 12px);
  border-radius: var(--r-sm, 6px);
  border: none;
  background: transparent;
  color: var(--text);
  cursor: pointer;
}
.pos-admin__nav-item.is-activa { background: var(--pos-ground); font-weight: 700; }
.pos-admin__panel { padding: var(--sp-5, 24px); overflow-y: auto; }
.pos-admin__seccion[hidden] { display: none; }
.pos-admin__tabla { width: 100%; border-collapse: collapse; }
.pos-admin__tabla th, .pos-admin__tabla td {
  text-align: left; padding: var(--sp-2, 8px) var(--sp-3, 12px);
  border-bottom: 1px solid var(--border);
}

/* ── Utilidades ───────────────────────────────────────────────────────── */
.pos-visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap;
}
.pos-oculto { display: none !important; }

/* ═══════════════════════════════════════════════════════════════════════
   SISTEMA DE COMPONENTES DE LA MAQUETA v4 — contrato de diseño aprobado
   ───────────────────────────────────────────────────────────────────────
   A partir de aquí: TODO lo que hay en el bloque <style> de
   docs/maqueta-v4.html salvo el "showroom" (marco de dispositivo, pestañas
   de arriba, columna de notas de la derecha, pie explicativo — eso vive
   solo en la maqueta, nunca en la app real). Es el catálogo de clases que
   debe usar quien construya una pantalla nueva del POS (Fases B en
   adelante). Mapa completo componente-de-la-maqueta → clase en
   docs/UX_SYSTEM_IMPLEMENTACION.md.

   Reglas de esta sección:
   · Todo nombre de clase lleva prefijo `pos-`. Los pocos casos en los que
     la maqueta reutiliza un nombre para dos cosas distintas según el
     contenedor (p.ej. "box", "nav", "two") se resuelven exactamente igual
     que en la maqueta: por selector descendiente, nunca en global.
   · Donde ya existía un componente de Fase A para el mismo propósito
     (`.pos-btn`, `.pos-toast`, `.pos-keypad`, `.pos-pin-puntos`) NO se
     duplica: se reutiliza el de Fase A (ver mapa) y aquí solo se añade lo
     que le faltaba. `.pos-chip` se actualizó arriba en su sitio porque
     ninguna pantalla la usaba aún.
   · `.table` de la maqueta (la tarjeta de mesa del plano de sala) se llama
     aquí `.pos-mesa` para no confundirla con una tabla de datos — el POS sí
     tiene tablas de datos de verdad (`.pos-admin__tabla`).
   · El "menú de comida" del cliente QR (`.menu` en la maqueta, dentro de
     `.cli`) se llama aquí `.pos-carta` para no confundirlo con el menú
     desplegable `.pos-menu-pop` (≡ de la cabecera). Son dos cosas distintas
     que en la maqueta comparten sílaba por casualidad del inglés.
   · Solo `--pos-` como prefijo de tokens nuevos; todo lo demás (color,
     tipo, sombra, radio, tap) reutiliza los tokens ya existentes de
     mrj-palette.css / lamaruja-ds.css (ver la tabla de equivalencias del
     doc). Ningún valor de la maqueta se ha inventado aquí de nuevo.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── Utilidad tipográfica ─────────────────────────────────────────────── */
.pos-num { font-variant-numeric: tabular-nums; }
.pos-lbl {
  display: block;
  font-size: var(--fs-xs, .75rem);
  font-weight: 900;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: var(--sp-2, 6px);
}

/* ── Tarjetas (§4.2 SISTEMA_VISUAL_V2.md) ──────────────────────────────
   `.pos-card` NUEVO: base reutilizable por cualquier bloque de contenido —
   superficie clara, radio de tarjeta, borde sand, relleno 20px. `.pos-kpi`
   NUEVO reutiliza los mismos `.l`/`.v`/`.s` que ya usan `.pos-admin__kpi` y
   `.pos-ob-kpi` (label/valor/subtítulo) para no inventar otra convención de
   nombres dentro del mismo catálogo. Rasgo 1 ("la cifra manda"): la cifra
   SIEMPRE en Paytone One, enorme, con la etiqueta pequeña encima. */
.pos-card {
  background: var(--pos-card);
  border: 1px solid var(--pos-line);
  border-radius: var(--pos-r-card);
  padding: 20px;
}
.pos-kpi {
  background: var(--pos-card);
  border: 1px solid var(--pos-line);
  border-radius: var(--pos-r-card);
  padding: 20px;
  display: grid;
  gap: 6px;
  align-content: start;
}
.pos-kpi .l { font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--mrj-mocha); }
/* CORREGIDO (maqueta v5 §9.2): la cifra de KPI son 33px, no 44px — gana la
   maqueta sobre el valor que traía SISTEMA_VISUAL_V2. */
.pos-kpi .v { font-family: var(--pos-font-display); font-weight: 400; font-size: 33px; color: var(--brown); line-height: 1; font-variant-numeric: tabular-nums; }
.pos-kpi .s { display: flex; align-items: center; gap: 8px; font-size: var(--fs-sm, 13px); color: var(--mrj-mocha); }
/* `.pos-kpi__delta` NUEVO (§8.1 punto 2, Baker's Dash): el delta va PEGADO
   a la cifra, a 11px — verde si mejora, rojo si empeora (maqueta `.kpi .d`). */
.pos-kpi__delta { font-size: 11px; font-weight: 700; color: var(--ok-text); margin-left: 7px; }
.pos-kpi__delta.n { color: var(--bad-text); }
/* `.pos-kpi--destacada`: invierte a fondo sand — UNA sola por vista (patrón
   de Cosy POS, §1). No es una regla que se pueda comprobar por CSS, es una
   convención de uso de quien monte la pantalla. */
.pos-kpi--destacada { background: var(--mrj-sand); border-color: transparent; box-shadow: var(--pos-sombra-1); }
.pos-kpi--destacada .l,
.pos-kpi--destacada .v,
.pos-kpi--destacada .s { color: var(--brown); }

/* `.pos-lista-mini` NUEVO: fila de 56px con miniatura, nombre, subtítulo y
   cifra a la derecha; cabecera con «Ver todo». */
.pos-lista-mini { display: grid; gap: 2px; }
.pos-lista-mini .cab { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 6px; }
.pos-lista-mini .cab h2 { font-size: var(--fs-md, 15px); }
.pos-lista-mini .cab .vertodo { font-size: var(--fs-sm, 13px); font-weight: 700; color: var(--brown); }
.pos-lista-mini .fila { display: grid; grid-template-columns: 44px 1fr auto; gap: var(--sp-3, 12px); align-items: center; min-height: 56px; border-bottom: 1px solid var(--mrj-sand); }
.pos-lista-mini .fila:last-child { border-bottom: 0; }
.pos-lista-mini .fila .mini { width: 44px; height: 44px; border-radius: var(--pos-r-control); background: var(--mrj-sand); }
.pos-lista-mini .fila .nom { font-weight: 700; line-height: 1.2; }
.pos-lista-mini .fila .sub { font-size: 12px; color: var(--mrj-mocha); }
.pos-lista-mini .fila .cifra { font-family: var(--pos-font-display); font-weight: 400; color: var(--brown); font-size: var(--fs-lg, 17px); font-variant-numeric: tabular-nums; }

/* ── Avance y datos (§4.3 SISTEMA_VISUAL_V2.md) ────────────────────────
   `.pos-barra`: pista sand + relleno rayado (principio 6, "rayado de
   toldo"), en `--pos-rayado` para poder apagarlo desde un solo token. Sigue
   la misma convención `contenedor` + `i` interno que ya usa `.pos-ob-row
   .prog i` en este catálogo. */
.pos-barra { height: 10px; border-radius: 999px; background: var(--mrj-sand); overflow: hidden; }
.pos-barra i {
  display: block;
  height: 100%;
  border-radius: 999px;
  background-color: color-mix(in srgb, var(--accent) 35%, transparent);
  background-image: var(--pos-rayado);
}
/* `.pos-grafico-barras` NUEVO: barras verticales sin librería — un `<div
   class="col">` por punto, con un `i` de altura en % dentro. La del día
   actual lleva `.hoy` (plena saturación); el resto queda al 45% (regla 1
   de la barra: "la del día actual a plena saturación y el resto al 45%"). */
.pos-grafico-barras {
  display: flex;
  align-items: flex-end;
  gap: 6px;
  height: 120px;
  padding-bottom: 8px;
  border-bottom: 2px solid var(--mrj-sand);
}
.pos-grafico-barras .col {
  flex: 1;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  gap: 6px;
}
.pos-grafico-barras .col i { display: block; width: 100%; min-height: 3px; border-radius: 4px 4px 0 0; background: var(--accent); opacity: .45; }
.pos-grafico-barras .col.hoy i { opacity: 1; }
.pos-grafico-barras .col .etq { font-weight: 700; font-size: 11px; color: var(--mrj-mocha); }
/* RECONCILIACIÓN (verificado leyendo `graficoBarras()` servido en
   pos-ui.js): el marcado real envuelve las columnas en un `.cols` propio
   (para que el título y la nota de pie queden fuera de la fila de flex) y
   pinta la barra con `<span class="barra n1..n5">`, no un `<i>` — se
   resetean aquí las propiedades que chocarían con las reglas de arriba
   (aplicadas al contenedor entero) y se redeclara todo sobre `.cols`. */
.pos-grafico-barras { display: grid; gap: var(--sp-2, 8px); height: auto; padding-bottom: 0; border-bottom: 0; align-items: stretch; }
.pos-grafico-barras .cols { display: flex; align-items: flex-end; gap: 6px; height: 120px; padding-bottom: 8px; border-bottom: 2px solid var(--mrj-sand); }
.pos-grafico-barras .cols .col { flex: 1; height: 100%; display: flex; flex-direction: column; justify-content: flex-end; align-items: center; gap: 6px; }
.pos-grafico-barras .cols .col .barra { display: block; width: 100%; min-height: 3px; border-radius: 4px 4px 0 0; background: var(--accent); opacity: .45; }
.pos-grafico-barras .cols .col.hoy .barra { opacity: 1; }
.pos-grafico-barras .cols .col .et { font-weight: 700; font-size: 11px; color: var(--mrj-mocha); }
.pos-grafico-barras .cols .col .barra.n1 { height: 20%; }
.pos-grafico-barras .cols .col .barra.n2 { height: 40%; }
.pos-grafico-barras .cols .col .barra.n3 { height: 60%; }
.pos-grafico-barras .cols .col .barra.n4 { height: 80%; }
.pos-grafico-barras .cols .col .barra.n5 { height: 100%; }

/* RECONCILIACIÓN `.pos-lista-mini` (verificado leyendo `listaMini()`
   servido): el título es un `<h2>` suelto con el badge/enlace dentro (sin
   el envoltorio `.cab`), la miniatura es `.thumb` (no `.mini`), el nombre
   vive en `.datos .n` y la cifra en `.p` (no `.nom`/`.cifra`). */
.pos-lista-mini h2 { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-2, 8px); margin-bottom: 6px; }
.pos-lista-mini h2 .lk { font-size: 13px; font-weight: 700; color: var(--brown); }
.pos-lista-mini .fila .thumb { width: 44px; height: 44px; border-radius: var(--pos-r-control); background: var(--mrj-sand); display: grid; place-items: center; font-family: var(--pos-font-display); font-weight: 400; color: var(--brown); flex-shrink: 0; }
.pos-lista-mini .fila .datos { display: grid; gap: 2px; min-width: 0; }
.pos-lista-mini .fila .datos .n { font-weight: 700; line-height: 1.2; }
.pos-lista-mini .fila .p { font-family: var(--pos-font-display); font-weight: 400; color: var(--brown); font-size: var(--fs-lg, 17px); }

/* ═══════════════════════════════════════════════════════════════════════
   COMPONENTES NUEVOS de la maqueta v5 APROBADA (docs/maqueta-v5-aprobada
   .html, §8/§9 SISTEMA_VISUAL_V2.md, 2026-09-11) — la maqueta los dibuja
   pero pos.css todavía no los tenía. Se declaran aquí listos para que
   quien construya el marcado de Control/Encargos los use tal cual; ningún
   componente nuevo redeclara un token existente (regla de cabecera).
   ═══════════════════════════════════════════════════════════════════════ */

/* `.pos-avatar` NUEVO (§9.2/§9.3 maqueta): círculo de inicial reutilizable
   — círculo --sand con inicial en Paytone One; el activo/seleccionado pasa
   a --accent. Nunca una foto: ni del equipo ni de las clientas (decisión
   cerrada §9.3) — es SIEMPRE un monograma. */
.pos-avatar {
  width: 30px; height: 30px; border-radius: 50%; flex-shrink: 0;
  background: var(--pos-line); color: var(--brown);
  font-family: var(--pos-font-display); font-weight: 400; font-size: 13px;
  display: inline-grid; place-items: center; line-height: 1;
}
.pos-avatar--activo { background: var(--accent); }
.pos-avatar--sm { width: 22px; height: 22px; font-size: 10px; }
.pos-avatar--lg { width: 48px; height: 48px; font-size: 18px; }

/* `.pos-saludo` NUEVO (§8.1 punto 1, Baker's Dash): «Buenos días, María.
   Hoy hay 12 cosas que hornear.» — la cifra del día va DENTRO de la frase,
   en naranja y Paytone One (maqueta `.saludo`/`.saludo em`). La primera
   línea de Control ya es una instrucción, no un titular seco. */
.pos-saludo { font-family: var(--pos-font-display); font-weight: 400; font-size: 27px; margin: 2px 0 4px; }
.pos-saludo em { font-style: normal; color: #C97A00; }
.pos-saludo + p { margin: 0 0 16px; font-size: 13.5px; color: var(--mrj-mocha); }

/* `.pos-tabs` NUEVO (§8.1 punto 3): pestañas CON contador dentro de una
   tarjeta — distintas de la píldora de `.pos-modes` (navegación principal),
   para que las dos jerarquías de pestaña no compitan: aquí nunca hay
   fondo, solo un subrayado naranja de 2px en la activa. */
.pos-tabs { display: flex; gap: 22px; border-bottom: 1px solid var(--pos-line); margin-bottom: 14px; overflow-x: auto; scrollbar-width: none; }
.pos-tabs::-webkit-scrollbar { display: none; }
.pos-tabs span { padding: 0 0 11px; font-size: 13.5px; font-weight: 700; color: var(--mrj-mocha); white-space: nowrap; cursor: pointer; }
.pos-tabs span.on { color: var(--brown); box-shadow: inset 0 -2px 0 var(--accent); }

/* `.pos-fecha` NUEVO (§8.1 punto 6): chip de fecha con semáforo de
   urgencia — se lee la urgencia de toda la pantalla sin leer una sola
   fecha. Neutro (más adelante) = --pos-ground, el "chip neutro" hundido
   de §9.1; hoy/mañana usan los pasteles de estado ya existentes. */
.pos-fecha { display: inline-block; font-size: 11.5px; font-weight: 700; padding: 5px 10px; border-radius: 8px; background: var(--pos-ground); color: var(--mrj-mocha); }
.pos-fecha--hoy { background: var(--pos-bad-bg); color: var(--bad-text); }
.pos-fecha--manana { background: var(--pos-warn-bg); color: var(--warn-text); }
.pos-fecha--lejos { background: var(--pos-ground); color: var(--mrj-mocha); }

/* `.pos-encargo` NUEVO (§8.1 punto 5, componente estrella de Baker's Dash):
   tarjeta de encargo completa — cabecera con avatar + chip de origen,
   líneas con viñeta naranja de 5px, dos columnas ENTREGA/ESTADO y un pie
   con importe grande + UN solo botón (la acción siguiente cambia con el
   estado; nunca una tarjeta con seis botones). */
.pos-encargo { background: var(--pos-card); border: 1px solid var(--pos-line); border-radius: var(--pos-r-card); overflow: hidden; box-shadow: var(--pos-sombra-1); display: flex; flex-direction: column; }
.pos-encargo__h { display: flex; align-items: center; gap: 10px; padding: 13px 14px 11px; }
.pos-encargo__h b { font-size: 14px; display: block; font-weight: 700; }
.pos-encargo__origen { display: block; font-size: 9.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--mrj-mocha); }
.pos-encargo__b { padding: 0 14px 12px; font-size: 12.5px; line-height: 1.85; }
.pos-encargo__b ul { margin: 0; padding: 0; }
.pos-encargo__b li { list-style: none; position: relative; padding-left: 13px; }
.pos-encargo__b li::before { content: ""; position: absolute; left: 0; top: 9px; width: 5px; height: 5px; border-radius: 50%; background: var(--accent); }
.pos-encargo__meta { display: flex; gap: 18px; padding: 0 14px 12px; }
.pos-encargo__meta > div > span:first-child { font-size: 9.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--mrj-mocha); display: block; margin-bottom: 5px; }
.pos-encargo__f { margin-top: auto; border-top: 1px solid var(--pos-line); background: var(--pos-ground); padding: 11px 14px; display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3, 10px); }
.pos-encargo__f strong { font-family: var(--pos-font-display); font-weight: 400; font-size: 19px; font-variant-numeric: tabular-nums; color: var(--brown); }
.pos-encargo__f button { min-height: var(--pos-tap-min); border: 0; border-radius: 10px; background: var(--accent); color: var(--brown); font-family: var(--pos-font-display); font-weight: 400; font-size: 12.5px; padding: 0 15px; cursor: pointer; }

/* `.pos-tip` NUEVO (§8.1 punto 7): tarjeta de consejo al pie de la barra
   lateral de Control — SIEMPRE naranja relleno, reservada a avisos
   operativos reales, nunca a relleno decorativo. */
.pos-tip { margin-top: auto; background: var(--accent); border-radius: var(--pos-r-card); padding: 12px; font-size: 11px; line-height: 1.45; color: var(--brown); }
.pos-tip b { display: block; font-family: var(--pos-font-display); font-weight: 400; font-size: 13px; margin-bottom: 4px; }

/* ── Cabecera común (logo + estado + accesos) ─────────────────────────
   maqueta: .pos-head .logo .who .lnk ─────────────────────────────────── */
.pos-head {
  /* CORREGIDO (maqueta v5 §9.2): la cabecera son 60px, no 64px — coincide
     además con `grid-template-rows:60px 1fr` de `.pos-pantalla-tpv`/
     `.pos-obr`/`.pos-fichajes-bloqueado`, que ya esperaban 60px: con 64px
     aquí la cabecera se recortaba 4px contra su propia fila de grid. */
  height: 60px;
  background: var(--brown);
  color: var(--mrj-cream);
  display: flex;
  align-items: center;
  gap: var(--sp-4, 14px);
  padding: 0 var(--sp-4, 14px);
}
/* CORREGIDO en la pasada de acabado (E31, hallazgo verificado abriendo el
   POS real en el navegador): `min-width:150px` es MENOR que el ancho real
   del logotipo servido por el OS a 36px de alto (`assets/logo-beis.png`,
   ~176px de ancho + "POS" + separación ≈ 217px) — un `min-width` explícito
   MENOR que el contenido no protege nada, solo le da permiso a flexbox para
   encoger el logo por debajo de su tamaño natural cuando la cabecera va
   justa de sitio (7 modos + insignia + "quién"), y el logo se comprimía
   literalmente encima de la insignia siguiente. `min-width:0` (el inicial)
   deja que el mínimo lo marque el propio contenido — el logo ya no se
   encoge nunca por debajo de su tamaño real. Si algún día hay menos sitio,
   es `.pos-modes` quien cede (regla de abajo), nunca la marca. */
.pos-head .logo { display: flex; align-items: center; gap: var(--sp-2, 8px); min-width: 0; flex-shrink: 0; }
.pos-head .logo img { height: 32px; width: auto; display: block; }
/* VERIFICADO EN NAVEGADOR (11/09, iPad apaisado 1180px): la barra de modos
   necesitaba 614px y solo tenía 587 disponibles, así que el último modo
   —Control, justo el que Joni pidió— quedaba cortado contra el bloque del
   usuario y solo se alcanzaba haciendo scroll lateral a ciegas. Se recuperan
   ~55px bajando el logo de 36 a 32px y retirando el rótulo "POS" de al lado:
   la marca ya la dice el propio logotipo, y el tipo de terminal lo dice la
   píldora que va a continuación. */
.pos-head .logo span { display: none; }
/* CORREGIDO (maqueta v5 §9.2): logo Paytone One 17px fijo, no --fs-md (15px). */
.pos-head .logo span { font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo); font-size: 17px; color: var(--mrj-sand); letter-spacing: .02em; }
/* Verificación en navegador del orquestador (2026-09-10, 1180x820 = iPad):
   con los 7 modos + insignia + "quién", el último botón ("Control") quedaba
   PISADO por el bloque de la derecha: `.pos-modes` no tenía `min-width:0`,
   así que su mínimo lo marcaba su contenido y desbordaba en vez de ceder.
   Con `min-width:0` + scroll horizontal propio, la barra cede sitio y ningún
   modo queda cortado. La barra de scroll se oculta: en un TPV se arrastra
   con el dedo, no con ratón. */
.pos-head .pos-modes { min-width: 0; overflow-x: auto; scrollbar-width: none; -ms-overflow-style: none; }
.pos-head .pos-modes::-webkit-scrollbar { display: none; }
.pos-head .pos-modes button { flex-shrink: 0; }
/* CORREGIDO por el corrector final (hallazgo MEDIA, verificado en vivo: sin
   Paytone One cacheada — arranque en frío del iPad — la barra desborda y no
   había NINGUNA pista de que se puede deslizar; "hay más modos" solo se
   descubría tocando por casualidad). "Sombras de scroll" — el truco clásico
   de dos tapas (recorte del propio --brown, se mueven CON el contenido) más
   dos sombras radiales (fijas al borde del contenedor): la sombra solo
   asoma cuando queda contenido oculto a ese lado, y desaparece sola al
   llegar al principio/final — ninguna flecha que dibujar ni JS que
   mantener. */
.pos-head .pos-modes {
  background:
    linear-gradient(to right, var(--brown) 30%, rgba(76, 44, 28, 0)) 0 0 / 24px 100% no-repeat local,
    linear-gradient(to left, var(--brown) 30%, rgba(76, 44, 28, 0)) 100% 0 / 24px 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%, rgba(19, 7, 2, .45), rgba(19, 7, 2, 0)) 0 0 / 10px 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, rgba(19, 7, 2, .45), rgba(19, 7, 2, 0)) 100% 0 / 10px 100% no-repeat scroll;
}

/* ── La barra de modos SIEMPRE cabe, sea cual sea el bloque de la derecha ──
   Esto ya se arregló una vez el 11/09 recortando el logo, y volvió a romperse
   el 14/09 en cuanto se añadió el botón de Caja: la pestaña "Control" quedaba
   otra vez fuera del área visible en el iPad de 1180 px, alcanzable solo
   arrastrando y sin ninguna pista de que hubiera más. Recortar píxeles cada
   vez que se añade algo no es un arreglo, es aplazarlo.
   Ahora el que cede es el bloque de la derecha, no los modos: el nombre de la
   empleada se acorta con puntos suspensivos y el rol desaparece cuando aprieta
   el espacio. El nombre completo y el rol siguen estando en el menú, así que
   no se pierde información, solo deja de competir por la barra. */
.pos-head .who {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--sp-3, 10px);
  font-size: var(--fs-sm, 13px);
  min-width: 0;          /* puede encogerse: sin esto, flex se niega */
  flex: 0 1 auto;
}
.pos-head .who > * { flex-shrink: 0; }
.pos-head .who .nombre {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex-shrink: 1;
}
/* El rol es lo primero que sobra: se lee en el menú, y en la barra solo
   estorba. Por debajo de 1024px de ancho de ventana desaparece. */
@media (max-width: 1024px) {
  .pos-head .who .rol { display: none; }
}
/* Los modos nunca se comprimen ni se recortan: son la navegación. El 0 del
   medio es lo que importa (flex-shrink), y es lo que faltaba en el primer
   intento: con shrink 1 eran los modos los que cedían y "Control" seguía
   quedándose fuera por unos pocos píxeles. Ahora quien encoge es el nombre. */
.pos-head .pos-modes { flex: 1 0 auto; }
.pos-head .lnk {
  /* CORREGIDO por el corrector final (hallazgo MEDIA, medido en vivo:
     34×40px — sin `min-width` el ancho lo marcaba solo el glifo "≡". Es el
     botón del menú, presente en TODAS las pantallas, y da acceso a
     fichar/bloquear/OS entre un cliente y otro — sube al mínimo táctil de
     E12 igual que el resto de botones de cabecera. */
  min-width: var(--pos-tap-min);
  min-height: var(--pos-tap-min);
  justify-content: center;
  padding: 0 var(--sp-3, 12px);
  border-radius: var(--r-md);
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2, 6px);
  color: var(--mrj-cream);
  border: 1.5px solid rgba(252, 236, 215, .28);
  font-weight: 700;
  font-size: var(--fs-sm, 13px);
}
.pos-head .lnk.os { border-color: var(--accent); color: var(--accent); } /* enlace a LaMaruja OS: solo visible con rol ADMIN (E25) */

/* ── Barra de modos (Mostrador · Mesas · Recogidas · Encargos · Obrador · Fichajes · Control — E23/E28/E29)
   §4.1 SISTEMA_VISUAL_V2.md: píldoras de TEXTO — activa = fondo `--orange`,
   texto `--cream`, Paytone One 15px; inactiva = texto `--cream` al 70%, sin
   fondo. Sin iconos (principio 3: "nada de iconos en navegación"). */
/* min-width:0 + overflow-x:auto (E31): con 7 modos posibles (E28/E29) más
   la insignia y "quién", la barra puede no caber entera en un iPad — que
   ceda ella con scroll horizontal discreto, nunca a costa de encoger el
   logo (regla de arriba) ni de desbordar la página entera. */
/* CORREGIDO (maqueta v5 §9.2, hallazgo de fidelidad): la maqueta NO pone
   una bandeja oscura detrás de las píldoras (`.modes{display:flex;gap:4px;
   margin-left:6px;flex:1}`, sin fondo/relleno/radio propios) — flotan
   directamente sobre el marrón de la cabecera. Se retira el fondo
   rgba(19,7,2,.35)+padding+radio que había aquí, que no existe en el
   contrato. */
.pos-modes { display: flex; gap: 4px; margin-left: 6px; flex: 1; min-width: 0; overflow-x: auto; scrollbar-width: none; }
.pos-modes::-webkit-scrollbar { display: none; }
.pos-modes button {
  flex: 0 0 auto;
  /* CORREGIDO por el corrector de Tanda 3 (hallazgo media #3): 44px
     incumplía el mínimo táctil de 48px de E12 en la barra que más se toca
     de todo el turno; unificado con var(--pos-tap-min) como el resto de
     botones de la app. */
  min-height: var(--pos-tap-min);
  /* CORREGIDO (maqueta v5 §9.2): píldora Paytone One 13px con relleno
     8px/14px y radio 999px (era 15px/9px/sin píldora real: `border-radius`
     se quedaba en 9px, ni de lejos una píldora). Inactiva a
     `rgba(cream,.62)` en vez de `opacity:.7` (la opacidad además apagaba
     cualquier icono/badge hijo, que aquí no debe verse afectado). */
  padding: 8px 14px;
  border-radius: 999px;
  font-family: var(--pos-font-display);
  font-weight: 400;
  color: rgba(252, 236, 215, .62);
  font-size: 13px;
  display: flex;
  align-items: center;
  gap: var(--sp-2, 8px);
  transition: background var(--pos-t), color var(--pos-t);
}
/* CORREGIDO (maqueta v5 §9.2: `.mode.on{background:var(--orange);
   color:var(--brown)}`): el texto activo es --brown, no --cream — sobre
   naranja, cream tiene mucho menos contraste que marrón. */
.pos-modes button.on { background: var(--accent); color: var(--brown); }
.pos-modes button .b { background: var(--mrj-cream); color: var(--brown); border-radius: 999px; font-size: var(--fs-xs, 11px); padding: 1px 7px; font-family: var(--font-body); font-weight: 900; }
.pos-modes button.on .b { background: var(--mrj-ink); color: var(--mrj-cream); }

.pos-dot { width: 9px; height: 9px; border-radius: 50%; background: #95A271; box-shadow: 0 0 0 3px rgba(149, 162, 113, .25); } /* estado "en línea" junto al reloj/sync */

/* ── Badge de estado (§4.4 SISTEMA_VISUAL_V2.md) — pastel cálido + palabra,
   nunca solo color (rasgo 5). Lato 700 11px, radio 999px, alto 22px. ──── */
.pos-badge {
  display: inline-flex;
  align-items: center;
  height: 22px;
  padding: 0 9px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  border-radius: 999px;
  /* CORREGIDO por el corrector final (hallazgo detectado verificando en
     vivo #1/#8 — el badge "Fuera" de chipsEmpleadosAcceso() y de Control →
     Personal se emite como `.pos-badge` A SECAS, sin `.ok/.warn/.bad/.info`
     — sin un neutro por defecto, quedaba texto plano transparente en vez de
     píldora, exactamente lo que la maqueta v5 evita con `.badge.f{
     background:var(--ground);color:var(--mocha)}`). El neutro es el
     ESTADO POR DEFECTO; los cuatro modificadores de abajo lo sustituyen. */
  background: var(--pos-ground);
  color: var(--mrj-mocha);
}
.pos-badge.ok { background: var(--pos-ok-bg); color: var(--ok-text); }
.pos-badge.warn { background: var(--pos-warn-bg); color: var(--warn-text); }
.pos-badge.bad { background: var(--pos-bad-bg); color: var(--bad-text); }
.pos-badge.info { background: var(--pos-info-bg); color: var(--brown); }

/* `.pos-filtro` NUEVO: píldora de filtro con contador dentro (patrón de
   Lapar POS, §1) — texto + burbuja con el número. Activa = naranja
   rellena. */
.pos-filtro {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  /* CORREGIDO (hallazgo a del verificador adversarial): 40px incumplía el
     mínimo táctil de 48px (--pos-tap-min) que cumple el resto de la app. */
  min-height: var(--pos-tap-min);
  padding: 0 14px;
  border-radius: 999px;
  border: 1.5px solid var(--pos-line);
  background: var(--pos-card);
  color: var(--brown);
  font-weight: 700;
  font-size: var(--fs-sm, 13px);
  cursor: pointer;
  transition: background var(--pos-t), border-color var(--pos-t), color var(--pos-t);
}
.pos-filtro .n {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--mrj-sand);
  color: var(--brown);
  font-size: 11px;
  font-weight: 900;
}
.pos-filtro.on { background: var(--accent); border-color: transparent; color: var(--mrj-cream); }
.pos-filtro.on .n { background: var(--mrj-cream); color: var(--brown); }
/* RECONCILIACIÓN (verificado leyendo `filtros()` servido en pos-ui.js): el
   contenedor real es `.pos-filtros` (no declarado arriba) y el contador
   real lleva la clase `.c` (no `.n`) — mismo aspecto, alias del real. */
.pos-filtros { display: flex; gap: 8px; flex-wrap: wrap; }
.pos-filtro .c { display: inline-flex; align-items: center; justify-content: center; min-width: 20px; height: 20px; padding: 0 5px; border-radius: 999px; background: var(--mrj-sand); color: var(--brown); font-size: 11px; font-weight: 900; }
.pos-filtro.on .c { background: var(--mrj-cream); color: var(--brown); }

/* ── Menú desplegable ≡ (cabecera del TPV/comandero) ─────────────────── */
.pos-menu-pop {
  position: absolute;
  /* CORREGIDO: sigue a la cabecera, que baja de 64px a 60px (maqueta v5 §9.2). */
  top: 60px;
  right: var(--sp-3, 12px);
  background: var(--pos-card);
  color: var(--text);
  border-radius: var(--pos-r-panel);
  box-shadow: var(--shadow-3);
  padding: var(--sp-2, 8px);
  display: grid;
  gap: 4px;
  z-index: 11;
  min-width: 280px;
  border: 1px solid var(--pos-line);
}
/* CORREGIDO por el corrector de Tanda 3 (hallazgo media #5): 46px incumplía
   el mínimo táctil de 48px de E12; unificado con var(--pos-tap-min).
   Fila HUNDIDA dentro de la tarjeta del popover (§9.1); al pasar/pulsar
   sube a --pos-card, invirtiendo la relación como "esto se ha activado". */
.pos-menu-pop button { background: var(--pos-ground); border: 1px solid var(--border); color: var(--text); cursor: pointer; min-height: var(--pos-tap-min); padding: 0 var(--sp-3, 12px); border-radius: var(--r-md); text-align: left; display: flex; align-items: center; gap: var(--sp-3, 10px); font-weight: 700; }
.pos-menu-pop button:hover { background: var(--pos-card); }
.pos-menu-pop small { display: block; font-weight: 400; color: var(--muted); font-size: var(--fs-xs, 12px); }
.pos-menu-pop .sep { height: 1px; background: var(--border); margin: 4px 0; }

/* ── Pantalla TPV: cabecera + cuerpo de 3 columnas (E23) ───────────────
   Sustituye a `.pos-tpv` mínimo de Fase A cuando se reconstruya la
   pantalla real siguiendo la maqueta (ese `.pos-tpv` de 3 columnas sigue
   ahí arriba, en uso por public/tpv/index.html, hasta que se rehaga). ── */
/* T7, CORREGIDO tras verlo en el navegador a 375px (el hallazgo real: no
   bastaba con acotar `#tpv-cuerpo`, había que subir un nivel más): este
   grid solo fijaba `grid-template-ROWS` — su columna (una sola, implícita)
   quedaba en `auto`, que para una columna de grid sin `grid-template-
   columns` explícito se mide por el CONTENIDO de sus items, sin techo. En
   cuanto un descendiente pide más sitio del que hay (la tira de categorías
   en fila de `.pos-tpv-body--mesa`, más abajo) esa columna implícita crecía
   sin límite (840px, medida dentro de un `<body>` de 359px) y desbordaba
   TODO lo de dentro, `#tpv-cuerpo` incluido, en vez de quedarse dentro y
   dejar que el descendiente se desplazara con SU `overflow-x:auto`.
   `minmax(0, 1fr)` es el mismo arreglo que `.pos-tpv-body--mesa` usa más
   abajo, aplicado un nivel más arriba. */
.pos-pantalla-tpv { display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: 60px 1fr; height: 100%; }
#tpv-cuerpo { min-width: 0; min-height: 0; }
/* T8 (docs/PANTALLA_MESA.md): la columna lateral de categorías (`.pos-cats`,
   172px de texto) desaparece — Qamarero las pone en bloques de color
   arriba del propio catálogo (`.pos-catbar`, más abajo), así que ya no hace
   falta una tercera columna de grid solo para ella. Quedan DOS: el
   catálogo (`.pos-prods`, con su barra de bloques/píldoras/buscador dentro)
   y el ticket. */
.pos-tpv-body {
  /* Ocupa TODO el alto de #tpv-cuerpo: sin esto el grid solo mide lo que
     miden sus hijos y el TPV real dejaba una franja vacía abajo (visto al
     abrirlo en el navegador el 2026-09-10). La maqueta llena la pantalla.
     CORREGIDO (maqueta v5 §9.1): la maqueta separa sus columnas con
     `gap`/`padding` de 14px sobre el fondo arena del marco (`.tpv{gap:14px;
     padding:14px}`) — sin este hueco, la tarjeta nueva de `.pos-prods` y el
     borde nuevo de `.pos-ticket` quedaban recortados contra sus vecinas y
     el radio/sombra de tarjeta no se veía. */
  height: 100%; display: grid; grid-template-columns: 1fr 360px; gap: 10px; padding: 10px; min-height: 0; }
/* T8 (encargo §1, "arriba de la pantalla dentro de una mesa, que salgan el
   resto"): la tira de mesas (`.pos-mesas-tira`, tpv.js) es una fila NUEVA
   que ocupa las dos columnas por encima de catálogo+ticket — SOLO en la
   cuenta de una mesa (Mostrador no tiene zonas, no lleva esta fila). */
.pos-tpv-body--mesa {
  grid-template-rows: auto 1fr;
  grid-template-areas: "mesas mesas" "prods ticket";
}
.pos-tpv-body--mesa > .pos-mesas-tira { grid-area: mesas; }
.pos-tpv-body--mesa > .pos-prods { grid-area: prods; }
.pos-tpv-body--mesa > .pos-ticket { grid-area: ticket; }
/* CORREGIDO por el corrector final (hallazgo ALTA, verificado en vivo a
   375×812: sin ningún `@media`, las columnas fijas se recortaban en
   silencio en un iPad vertical (~810-834px) o un móvil — el ticket con el
   Total y los botones de cobro quedaba fuera de la vista, inalcanzable, sin
   scroll para llegar a él).
   ARREGLADO 15/09 (verificado en vivo a 375×812 con el TPV de verdad, no en
   teoría): el intento anterior pasaba MOSTRADOR a 2 "páginas" horizontales
   con `scroll-snap` sin ninguna pista visual de que hubiera que deslizar —
   el botón Cobrar quedaba fuera de la pantalla (medido con
   getBoundingClientRect: x=307→553 con solo 375px de ancho) y nadie
   descubría el gesto por sí solo. MESA ya resolvía este mismo problema con
   FILAS en vez de "páginas": el ticket entero (cabecera+líneas+Cobrar, con
   su propio `overflow` interno — ver `.pos-ticket` más abajo, que YA pinta
   el pie pegado abajo con `grid-template-rows:auto 1fr auto`) en una caja
   de altura acotada arriba, y la rejilla de producto ocupando el resto
   debajo — los dos siempre visibles, sin gesto oculto y sin que el total o
   Cobrar puedan quedar fuera de la pantalla. MOSTRADOR y la cuenta de una
   MESA comparten exactamente el mismo DOM (`.pos-prods`+`.pos-ticket` — ver
   `construirPantallaVenta()` en tpv.js), así que el mismo patrón de filas
   sirve para los dos: la única diferencia es la tira de mesas, que solo
   existe (y solo pinta su fila) en la cuenta de una mesa. `.pos-mob` (más
   abajo) sigue siendo el layout dedicado del comandero cuando ese flujo lo
   enrute — esto es la red de seguridad para cuando la pantalla de
   Mostrador se abre tal cual en una pantalla estrecha. Rango extendido
   desde 640px: encargo "sirve igual en el comandero de móvil", y una
   tablet de ~800px de una sola mano se beneficia igual de ver
   catálogo+ticket a la vez que un móvil. */
@media (max-width: 960px) {
  .pos-tpv-body {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr);
    grid-template-areas: "ticket" "prods";
    overflow: visible;
  }
  /* La barra de categorías se queda DENTRO de `.pos-prods` (a diferencia de
     la antigua `.pos-cats`, que era una columna hermana) — su propio
     `overflow:auto` de `.pos-prods` ya la desplaza junto con la rejilla,
     sin necesidad de una fila de grid aparte. Los bloques sí pasan a fila
     horizontal con scroll propio, igual que hacía la vieja tira vertical. */
  /* El tope de altura del ticket en móvil, y por qué el pie va anclado.
     Medido en el navegador el 15/09/2026: en la cuenta de una MESA, la
     cabecera es bastante más alta que en Mostrador (fila «‹ Plano · S1»,
     fila de comensales y descuento, y la fila de socia del club), así que
     cabecera + pie ya se comen los 38vh ellos solos — sin dejar sitio ni
     para una línea de producto. Con `overflow:hidden` heredado, el TOTAL y
     los botones de comandar y cobrar quedaban recortados a una tira de 9
     píxeles: se veían, pero el dedo caía en el elemento de debajo.
     Dos cambios que lo cierran de raíz:
       · el ticket se puede desplazar por dentro (nunca recorta y punto), y
       · el pie va ANCLADO abajo, así que el importe y los botones de cobrar
         están SIEMPRE a la vista, mida lo que mida la cabecera y haya las
         líneas que haya. */
  .pos-tpv-body .pos-ticket { grid-area: ticket; max-height: 42vh; overflow-y: auto; }
  .pos-tpv-body .pos-ticket .pos-tfoot {
    position: sticky;
    bottom: 0;
    z-index: 2;
    box-shadow: 0 -10px 16px -10px rgba(19, 7, 2, .18);
  }
  .pos-tpv-body .pos-prods { grid-area: prods; min-height: 0; }
  .pos-tpv-body .pos-catbar__bloques { flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .pos-tpv-body .pos-catbar__bloques .pos-catblock { flex: 0 0 auto; min-width: 120px; }
  /* De 4 a 2 columnas: con 4 la tarjeta se quedaba en ~74px de ancho y el
     nombre del producto se cortaba a media palabra ("Napoli de chocol...",
     visto en el navegador) — `.pos-prod` recorta con `overflow:hidden` lo
     que no cabe, así que la tarjeta necesita más ancho, no un tachado. */
  .pos-tpv-body .pos-grid { grid-template-columns: repeat(2, 1fr); }

  /* T7→T8 (Comandero): la cuenta de una mesa añade una fila más arriba de
     todo para la tira de mesas — Mostrador no tiene zonas, así que no
     lleva esta fila ni el elemento que la ocupa. */
  .pos-tpv-body--mesa {
    grid-template-rows: auto auto minmax(0, 1fr);
    grid-template-areas: "mesas" "ticket" "prods";
  }
  .pos-tpv-body--mesa > .pos-mesas-tira { grid-area: mesas; }

  /* Hallazgo aparte (mismo pase de verificación 15/09, medido con
     `document.body.scrollWidth` a 375×812: daba 765px sobre 375 de
     ventana — SÍ había scroll horizontal de página, aunque invisible por
     `scrollbar-width:none`). La causa no es nada de lo de arriba: es
     `.pos-head .pos-modes { flex: 1 0 auto }` (más arriba en este fichero,
     añadido para el iPad — "los modos nunca se comprimen"). Con
     `flex-shrink:0` la barra de pestañas SIEMPRE ocupa su ancho de
     contenido completo (536px con los 7 modos) — en el iPad (1180px) le
     sobra sitio y su propio `overflow-x:auto` nunca llega a activarse
     (todo cabe). En un móvil de 375px ese mismo ancho fijo desborda la
     CABECERA entera (`.pos-head`, sin overflow propio), no solo `.pos-
     modes` — por eso `document.body.scrollWidth` crecía aunque no se viera
     ninguna barra. Primer intento (encogiendo `.pos-modes` con `flex:1 1
     0`) quitaba el scroll de página pero dejaba la barra de modos
     ilegible (un solo carácter visible, sin pista de deslizar) — peor que
     el problema original. Arreglo real: `.pos-head` pasa a ser ÉL MISMO el
     contenedor que se desliza con el dedo — mismo patrón ya usado en
     `.pos-modes`/`.pos-catbar__bloques` (`overflow-x:auto` + scrollbar
     oculta) pero un nivel más arriba, así el desborde queda contenido
     ANTES de llegar a `document.body` y las pestañas se ven enteras,
     igual que antes, solo que ahora arrastrables si no caben. */
  .pos-head { overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; -ms-overflow-style: none; }
  .pos-head::-webkit-scrollbar { display: none; }

  /* Hallazgo del "vende de verdad" (mismo pase 15/09: tocar un producto y
     cobrar de principio a fin, no solo mirar el botón): `.pos-modsheet` y
     `.pos-paysheet` son `position:absolute;inset:0` — T6 (más arriba en
     este fichero) las ancla a `.pos-ticket` a propósito para que la hoja de
     modificadores/cobro quede DENTRO de la columna del ticket en el iPad.
     Con `.pos-ticket` ya acotado a `max-height:38vh` en este `@media`
     (arriba), esa misma hoja se quedaba embutida en esos 38vh — importe
     grande, botones de método y teclado numérico solapándose unos con
     otros, visto en el navegador al cobrar de verdad. Cobrar es la tarea
     más importante de esta pantalla: en vez de resolverlo con un tamaño de
     letra menor (parchear sin arreglar), la hoja pasa a `position:fixed`
     cubriendo TODO el viewport — una pantalla completa para cobrar es
     mejor uso del espacio en un móvil de todas formas, no un mal menor.
     `z-index:15`: por debajo de `.pos-hoja` (20, los diálogos de Caja/PIN)
     para no invertir con qué se tapa qué si algún día coinciden, por
     encima de todo lo demás de esta pantalla. */
  /* Selector con un ancestro de más (`.pos-tpv-body …`) a propósito: sube
     la especificidad para ganarle SIEMPRE a `.pos-modsheet`/`.pos-paysheet`
     de más abajo en este mismo fichero (misma especificidad si no, y esa
     declaración va DESPUÉS en el CSS — ganaría ella por orden de cascada
     pese a estar dentro de este `@media`, como pasó en la primera prueba
     en el navegador: `position` seguía dando `absolute`). */
  /* `background:var(--pos-ground)` — la declaración base de estas hojas
     (más abajo en este fichero) pone `background: var(--bg)`, una variable
     que NO EXISTE en este fichero (grep hecho al arreglar esto: solo
     existen `--pos-ground`/`--pos-card`) y que por tanto siempre ha
     calculado transparente. Nunca se notó porque, ancladas dentro de
     `.pos-ticket` (que sí tiene fondo propio), el hueco transparente
     dejaba ver el crema de la tarjeta de debajo — visualmente indistintо.
     A pantalla completa (regla de arriba) ese mismo hueco deja ver TODO lo
     de detrás (catálogo, cabecera…), letras montadas unas sobre otras —
     visto en el navegador al cobrar de verdad. Fondo arena explícito,
     opaco, del contrato visual (§11/§12: nunca un gris ni un blanco puro). */
  .pos-tpv-body .pos-modsheet, .pos-tpv-body .pos-paysheet { position: fixed; inset: 0; z-index: 15; background: var(--pos-ground); }
}

/* CORREGIDO (maqueta v5 §9.2, `.cgrp`): Lato 700 10px, letter-spacing
   .09em, en --mocha — no 900/10.5px/.14em/--muted. Sigue viva aunque
   `.pos-cats` ya no existe (T8): la usa `.pos-cuenta-grupo` de admin. */
.pos-cgrp { font-size: 10px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase; color: var(--mrj-mocha); padding: var(--sp-3, 10px) var(--sp-3, 10px) 4px; }
.pos-cgrp:first-child { padding-top: 4px; }

/* ── T8 (docs/PANTALLA_MESA.md §2.4/§4.5): catálogo a la Qamarero ────────
   Bloques de color grandes (nunca una lista lateral de texto) + píldoras
   de subcategoría + buscador, todo dentro de `.pos-prods` — sustituye a la
   columna `.pos-cats`/`.pos-cat` de antes (retirada de este fichero). */
.pos-catbar { display: flex; flex-direction: column; gap: var(--sp-3, 10px); margin-bottom: var(--sp-3, 12px); }
.pos-catbar__buscar .pos-campo { width: 100%; }
.pos-catbar__bloques { display: flex; flex-wrap: wrap; gap: var(--sp-2, 8px); }
/* Bloque = botón grande teñido de lleno (nunca un gris, nunca un blanco):
   los 5 tintes de marca (`--cat-*`, ya declarados arriba) en vez de los
   7 colores libres de Qamarero — mismo efecto ("se acierta con el dedo sin
   mirar"), sin salirse de la paleta de la casa. */
.pos-catblock {
  min-height: var(--pos-tap-comfort, 64px);
  flex: 1 1 128px;
  border-radius: var(--pos-r-control, 12px);
  padding: 10px var(--sp-3, 12px);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: 3px;
  color: var(--mrj-cream);
  font-weight: 900;
  text-align: left;
  box-shadow: var(--pos-sombra-1);
  transition: transform var(--pos-t-touch, 120ms) var(--ease), box-shadow var(--pos-t);
}
.pos-catblock .nm { font-size: var(--fs-sm, 14px); line-height: 1.15; }
.pos-catblock .n { font-size: var(--fs-xs, 11px); font-weight: 700; opacity: .85; }
.pos-catblock:active { transform: scale(.97); }
/* Activo: borde marcado en --brown (mismo criterio de "lo activo lleva un
   borde de énfasis", §4.12) — el bloque YA es sólido de color, así que aquí
   no puede repetirse el truco naranja de `.pos-cat.on`. */
.pos-catblock.on { box-shadow: var(--pos-sombra-2), inset 0 0 0 3px var(--mrj-ink); }
/* Texto INK/--brown sobre los dos tintes claros (op=naranja, marca=sand) —
   mismo criterio que `.pos-btn--primario` (naranja+ink) en vez de un cream
   claro-sobre-claro; los tres tintes oscuros (b2b/control/finanzas) se
   quedan con el cream por defecto de `.pos-catblock`. */
.pos-catblock--tinte-op { background: var(--cat-op); color: var(--mrj-ink); }
.pos-catblock--tinte-marca { background: var(--cat-marca); color: var(--brown); }
.pos-catblock--tinte-b2b { background: var(--cat-b2b); }
.pos-catblock--tinte-control { background: var(--cat-control); }
.pos-catblock--tinte-finanzas { background: var(--cat-finanzas); }
/* Píldoras de subcategoría (encargo: "Todos · Café · Infusiones…") —
   reutilizan `.pos-chip`, ya con su propio aspecto/estado `.on` (mismo
   componente que las opciones de un modificador). */
.pos-catbar__pildoras { display: flex; flex-wrap: wrap; gap: var(--sp-2, 8px); }
.pos-catbar__pildoras:empty { display: none; }

/* T8: franja de color arriba de CADA tarjeta de producto (con foto o sin
   ella), encargo §2.4/§6 — marcada por tpv.js con `data-tinte` (nunca el
   atributo `style`) porque `tarjetaProducto()` (pos-ui.js) solo añade la
   clase de tinte al respaldo SIN foto. Mismos 5 tintes que los bloques. */
.pos-prod[data-tinte]::before {
  content: ''; position: absolute; inset: 0 0 auto 0; height: 4px; z-index: 2;
  border-radius: var(--r-lg) var(--r-lg) 0 0;
}
.pos-prod[data-tinte="op"]::before { background: var(--cat-op); }
.pos-prod[data-tinte="marca"]::before { background: var(--cat-marca); }
.pos-prod[data-tinte="b2b"]::before { background: var(--cat-b2b); }
.pos-prod[data-tinte="control"]::before { background: var(--cat-control); }
.pos-prod[data-tinte="finanzas"]::before { background: var(--cat-finanzas); }
.pos-esqueleto--bloque { min-height: var(--pos-tap-comfort, 64px); flex: 1 1 128px; border-radius: var(--pos-r-control, 12px); }

/* CORREGIDO (maqueta v5 §9.1): `.pos-prods` no tenía superficie propia —
   la maqueta lo pinta como tarjeta (`.prods{background:var(--card);
   border:1px solid var(--line)}`) y la rejilla de productos que contiene
   va HUNDIDA (`.prod{background:var(--ground)}`), la relación se invierte
   dentro. Sin esto, la tarjeta de productos no existía y la rejilla se
   veía flotando directamente sobre el fondo de la aplicación. */
.pos-prods { background: var(--pos-card); border: 1px solid var(--pos-line); border-radius: var(--pos-r-card); padding: var(--sp-3, 12px); overflow: auto; position: relative; min-height: 0; display: flex; flex-direction: column; }
.pos-ctx { display: flex; align-items: center; gap: var(--sp-3, 10px); margin-bottom: var(--sp-3, 12px); min-height: 44px; }
.pos-ctx h2 { font-size: var(--fs-xl, 20px); }
.pos-ctx .meta { margin-left: auto; font-size: var(--fs-sm, 12.5px); color: var(--muted); }

/* MÁS PRODUCTO POR PANTALLA (Joni, 15/09/2026, viendo la rejilla con el
   catálogo real: «haz los botones de los productos mucho más pequeños»).
   Con 4 columnas fijas y tarjetas de 96 px, un bloque de 20 tartas obligaba
   a bajar scroll con una clienta delante. `auto-fill` con un mínimo de
   148 px mete el doble de tarjetas en el mismo sitio y sigue respetando el
   objetivo táctil (148 × 64 px es de sobra para un dedo). */
.pos-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(148px, 1fr)); gap: var(--sp-2, 8px); align-content: start; }
/* SISTEMA_VISUAL_V2 §3: la sombra de Nivel 1 ya separa la forma del fondo —
   el borde grueso de 1.5px encima era ruido (regla dura de la pasada:
   "si hay sombra de Nivel 1+ el borde baja a 1px al 55% de --border"). */
.pos-prod {
  min-height: 64px;
  border-radius: var(--pos-r-control, 12px);
  /* Tarjeta de producto = elemento HUNDIDO dentro de `.pos-prods` (§9.1) */
  background: var(--pos-ground);
  border: 1px solid color-mix(in srgb, var(--border) 55%, transparent);
  padding: var(--sp-2, 9px) var(--sp-3, 10px);
  gap: 2px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  text-align: left;
  box-shadow: var(--shadow-1);
  position: relative;
  overflow: hidden;
  transition: transform var(--pos-t), box-shadow var(--pos-t);
}
.pos-prod:active { transform: scale(.97); box-shadow: var(--shadow-2); }
/* `overflow-wrap: break-word` de refuerzo: `.pos-prod` recorta con
   `overflow:hidden` (arriba) lo que no cabe — sin esto, una palabra larga
   que no cupiese en el ancho de la tarjeta (2 columnas en móvil, ver el
   `@media` de más abajo) se recortaba a media palabra en vez de partirse
   con salto de línea limpio. */
.pos-prod .n { font-weight: 900; font-size: var(--fs-sm, 13.5px); line-height: 1.2; overflow-wrap: break-word; }
/* El precio sigue siendo lo segundo que se lee, con la tipografía de
   titulares; baja de 20 a 16 px al encoger la tarjeta, pero conserva el
   contraste de peso frente al nombre. */
.pos-prod .p { font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo); font-size: 16px; color: var(--brown); font-variant-numeric: tabular-nums; }
.pos-prod .v { font-size: var(--fs-xs, 10.5px); color: var(--muted); font-weight: 700; line-height: 1.15; }
/* Hallazgo (e) del verificador adversarial: la línea de producto agotado
   llevaba un degradado rayado que no es ni el rayado de toldo aprobado
   (§9.3) ni el overlay sobre foto — se sustituye por color plano. */
.pos-prod.out { opacity: .55; background: var(--pos-ground); }
.pos-prod.out .p { display: none; }
.pos-prod .st { position: absolute; top: 8px; right: 8px; z-index: 2; }

/* ── §4.3/§4.4/§6: tarjeta de producto CON foto y su respaldo SIN foto ──
   Sin foto es el caso MAYORITARIO del día 1 (≈2/3 del catálogo sin
   `imagen_url` todavía, §6.1) y recibe el mismo radio, sombra y tamaño que
   la de foto, nunca un tratamiento de segunda. Lo que lo distingue es la
   franja de color de su categoría, no un icono genérico de "sin imagen" ni
   —desde el 15/09/2026— la letra inicial del nombre. */
.pos-prod--foto { padding: 0; justify-content: flex-end; }
.pos-prod--foto .foto { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0; }
.pos-prod--foto::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(180deg, transparent 55%, rgba(19, 7, 2, .72) 100%);
  z-index: 1;
}
.pos-prod--foto .n, .pos-prod--foto .p { position: relative; z-index: 2; color: var(--mrj-cream); padding: 0 var(--sp-3, 12px); }
.pos-prod--foto .n { padding-top: var(--sp-2, 10px); }
.pos-prod--foto .p { padding-bottom: var(--sp-2, 10px); opacity: .9; }
.pos-prod--foto .v { display: none; } /* la variante ya la dice el nombre en la foto; no hay sitio limpio para una tercera línea */

/* Fallback sin foto: fondo con degradado propio, nunca plano gris. El tinte
   de la categoría lo pinta la franja superior (`[data-tinte]::before`). */
.pos-prod--sinfoto { background: linear-gradient(135deg, var(--pos-card) 0%, var(--pos-ground) 100%); }

/* Badge de "N opciones" (productos con modificadores, §4.3) — arriba a la
   izquierda, siempre legible sobre cualquier foto gracias al blur. */
.pos-prod .opciones {
  position: absolute; top: 8px; left: 8px; z-index: 2;
  background: var(--pos-info-bg); color: var(--brown);
  font-size: 10.5px; font-weight: 900; letter-spacing: .04em; text-transform: uppercase;
  padding: 3px 8px; border-radius: var(--r-sm, 6px);
}
@supports (backdrop-filter: blur(4px)) {
  .pos-prod--foto .opciones, .pos-prod--foto .st { backdrop-filter: blur(4px); background: color-mix(in srgb, var(--pos-info-bg) 82%, transparent); }
  .pos-prod--foto .st.pos-badge.bad { background: color-mix(in srgb, var(--pos-bad-bg) 82%, transparent); }
  .pos-prod--foto .st.pos-badge.warn { background: color-mix(in srgb, var(--pos-warn-bg) 82%, transparent); }
}

/* ── Ticket ─────────────────────────────────────────────────────────── */
/* CORREGIDO (maqueta v5 §9.1: `.ticket{background:var(--card);border:1px
   solid var(--line);border-radius:var(--rc)}`) — con el hueco nuevo entre
   columnas de `.pos-tpv-body`, el ticket es una tarjeta suelta, no una
   columna a ras del borde: pasa de un solo `border-left` a tarjeta completa. */
/* T6: `position:relative` — SIN esto, `.pos-modsheet`/`.pos-paysheet`
   (`position:absolute;inset:0`, más abajo) no tenían ningún ancestro
   posicionado y caían contra el viewport entero en vez de cubrir solo el
   ticket. Las dos reglas llevaban desde antes de esta tanda sin usarse en
   ningún marcado real — nadie lo había notado todavía. */
.pos-ticket { position: relative; background: var(--pos-card); border: 1px solid var(--pos-line); border-radius: var(--pos-r-card); display: grid; grid-template-rows: auto 1fr auto; min-height: 0; overflow: hidden; }
/* §4.7: separador punteado entre la cabecera del ticket y sus líneas, como
   el corte de un tique de papel (rasgo 7) — antes era un borde sólido. */
.pos-ticket .th { padding: var(--sp-3, 12px) var(--sp-4, 16px); border-bottom: 2px dotted var(--mrj-sand); display: flex; align-items: center; justify-content: space-between; }
.pos-ticket .th h2 { font-size: var(--fs-lg, 18px); }
/* T8 (encargo §4.4): cabecera de la cuenta de MESA — dos filas (título+volver
   arriba, comensales/descuento/menú abajo) en vez de la fila única de
   Mostrador; nunca llenar la pantalla de botones sueltos. */
.pos-ticket .th--mesa { flex-direction: column; align-items: stretch; gap: var(--sp-2, 8px); }
.pos-ticket__titulo { display: flex; align-items: center; gap: var(--sp-2, 8px); }
.pos-ticket__titulo h2 { font-size: var(--fs-lg, 18px); }
.pos-ticket__acciones { display: flex; align-items: center; gap: 6px; }
.pos-comensales { display: flex; align-items: center; gap: 4px; margin-right: auto; }
/* Mínimo táctil de 48px (E12) también aquí — un stepper de cabecera no es
   excepción, mismo criterio que `.pos-btn--menor`. */
.pos-comensales button {
  width: var(--pos-tap-min); height: var(--pos-tap-min); border-radius: var(--r-md, 8px);
  border: 1px solid var(--pos-line); background: var(--pos-ground);
  font-weight: 900; font-size: 18px; line-height: 1; color: var(--brown);
  display: inline-flex; align-items: center; justify-content: center;
}
.pos-comensales .n { min-width: 22px; text-align: center; font-weight: 900; font-variant-numeric: tabular-nums; }
/* Menú «⋯» de la cuenta — mismo `.pos-menu-pop` del ≡ de cabecera, reposicionado
   relativo a `.pos-ticket` (ya `position:relative`) en vez del viewport. */
.pos-ticket-menu { position: relative; }
.pos-ticket .pos-menu-pop { top: 92px; right: 10px; min-width: 260px; }
/* `min-height: 0` no es un adorno: sin él, esta fila `1fr` del ticket usa su
   contenido como altura mínima, crece con cada línea añadida y **empuja el
   pie fuera de la caja** — que en `.pos-ticket` va con `overflow:hidden`, así
   que el TOTAL y los botones de cobrar desaparecen sin dejar ni barra de
   desplazamiento con la que ir a buscarlos.
   Encontrado el 15/09/2026 en una verificación con el navegador: en la cuenta
   de una mesa, en un móvil de 375px, «Comandar en mesa» y «Cerrar cuenta»
   quedaban reducidos a una tira de 9 píxeles sin texto legible y sin forma de
   pulsarlos con el dedo. Bastaba una línea de producto para provocarlo,
   porque la cabecera de Mesa es más alta que la de Mostrador.
   Con esto, las líneas se desplazan dentro de su zona y el pie está SIEMPRE a
   la vista, que es lo que tiene que pasar: el importe y el botón de cobrar no
   se esconden nunca. */
.pos-lines { overflow: auto; min-height: 0; padding: 6px var(--sp-2, 8px); }
.pos-line { display: grid; grid-template-columns: 34px 1fr auto; gap: var(--sp-2, 8px); align-items: center; padding: var(--sp-2, 8px); border-radius: var(--r-md); min-height: var(--pos-tap-min); }
.pos-line:hover { background: var(--pos-ground); }
/* La cantidad va en burbuja sand (§4.7), no como texto suelto. */
.pos-line .q {
  font-family: var(--pos-font-display);
  font-weight: 400;
  font-size: var(--fs-sm, 14px);
  color: var(--brown);
  background: var(--mrj-sand);
  width: 34px;
  height: 34px;
  border-radius: 999px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-variant-numeric: tabular-nums;
}
.pos-line .n { font-weight: 700; line-height: 1.15; }
.pos-line .m { font-size: var(--fs-xs, 12px); color: var(--mrj-mocha); }
.pos-line .p { font-weight: 900; display: flex; align-items: center; font-variant-numeric: tabular-nums; }
.pos-line .del { margin-left: 6px; color: var(--bad-text); font-weight: 900; min-width: 32px; min-height: 32px; }
.pos-empty { color: var(--muted); text-align: center; padding: 40px var(--sp-4, 16px); font-size: var(--fs-sm, 14px); }
/* CORREGIDO (maqueta v5 §9.1: `.tfoot{background:var(--ground)}`) — el pie
   del ticket es uno de los elementos HUNDIDOS explícitos de la maqueta y no
   tenía fondo propio (heredaba el crema de `.pos-ticket`, sin invertirse). */
.pos-tfoot { background: var(--pos-ground); border-top: 1px solid var(--border); padding: var(--sp-3, 12px) var(--sp-4, 16px) 14px; display: grid; gap: var(--sp-3, 10px); }
.pos-tot { display: flex; justify-content: space-between; align-items: baseline; }
.pos-tot .l { font-size: var(--fs-xs, 13px); color: var(--muted); font-weight: 700; text-transform: uppercase; letter-spacing: .1em; }
/* CORREGIDO (maqueta v5 §9.2): el total es 36px, no 40px — la maqueta
   aprobada manda sobre el valor que traía SISTEMA_VISUAL_V2. Sigue siendo
   el número más grande de la pantalla de venta. Tabular-nums para que no
   "baile" al cambiar de importe. */
.pos-tot .v { font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo); font-size: 36px; color: var(--brown); font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
/* §4.7: línea divisoria sutil entre las líneas del ticket y el total — el
   salto era abrupto sin ella. */
.pos-tfoot .pos-tot:first-of-type { border-top: 1px solid color-mix(in srgb, var(--border) 40%, transparent); padding-top: var(--sp-4, 16px); }
.pos-tot.sm .v { font-size: var(--fs-sm, 14px); font-family: var(--font-body); font-weight: 700; color: var(--muted); }
/* T8 (encargo §4.3): "avisa de lo que queda por cobrar" tras un cobro por
   separado — en aviso (warn), no en gris neutro: es un dato que importa. */
.pos-tot--pendiente .v { color: var(--warn-text); }
.pos-tfoot .row { display: grid; grid-template-columns: 1fr 2fr; gap: var(--sp-3, 10px); }
/* T6: fila de dos acciones secundarias a lo ancho igual ("Descuento"/"Vaciar"
   — `.row2` de la maqueta aprobada, docs/maqueta-v5-aprobada.html §"ticket"). */
.pos-tfoot .row2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-2, 8px); }

/* ── Hoja de modificadores (sobre el ticket, E22) ──────────────────────── */
.pos-modsheet { position: absolute; inset: 0; background: var(--bg); padding: var(--sp-4, 14px); display: flex; flex-direction: column; gap: var(--sp-3, 12px); z-index: 5; overflow: auto; }
.pos-modsheet h2 { font-size: var(--fs-xl, 22px); display: flex; justify-content: space-between; align-items: center; gap: var(--sp-3, 10px); }
.pos-modsheet .g { display: flex; flex-wrap: wrap; gap: var(--sp-2, 8px); }

/* ── Tarta personalizada (15/09/2026) ───────────────────────────────── */
/* Encargo de Joni: «como en la web calculadora, deben de seleccionarse
   junto». Reutiliza el chasis de `.pos-modsheet` —los mismos chips, el mismo
   botón grande abajo— y solo añade dos piezas propias: el contador de
   comensales y la línea que explica la cuenta, que es lo que la empleada le
   dice a la clienta en voz alta ("son 2,30 por persona"). */
/* Va sobre la REJILLA (mismo chasis que la hoja de sabores), no sobre la
   columna del ticket: son cuatro listas que tienen que verse a la vez, y
   en el panel estrecho el botón de añadir se quedaba bajo el pliegue. */
.pos-tartasheet { position: absolute; inset: 0; background: var(--bg); padding: var(--sp-4, 14px); display: flex; flex-direction: column; gap: var(--sp-3, 12px); z-index: 6; }
.pos-tartasheet__cab { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--sp-3, 10px); flex-wrap: wrap; }
.pos-tartasheet__cab h3 { font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo); font-size: var(--fs-xl, 22px); color: var(--brown); margin: 0; }
.pos-tartasheet__sub { color: var(--muted); font-size: var(--fs-sm, .875rem); margin: 2px 0 0; }
/* Las cuatro decisiones, en columnas. Cada una scrollea por su cuenta: así
   una lista larga de rellenos no empuja el resto fuera de la pantalla. */
.pos-tartasheet__cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: var(--sp-4, 14px); flex: 1; min-height: 0; overflow: auto; align-content: start; }
.pos-tartasheet__cols .grupo { display: flex; flex-direction: column; gap: var(--sp-2, 8px); min-width: 0; }
.pos-tartasheet__cols .g { display: flex; flex-direction: column; gap: var(--sp-2, 8px); }
.pos-tartasheet__cols .pos-chip { width: 100%; text-align: left; }
/* El pie NO scrollea: el precio y el botón de añadir siempre a la vista. */
.pos-tartasheet__pie { display: flex; justify-content: space-between; align-items: center; gap: var(--sp-4, 14px); flex-wrap: wrap; border-top: 1px solid var(--border); padding-top: var(--sp-3, 12px); }
.pos-tartasheet__cierre { display: flex; align-items: center; gap: var(--sp-3, 12px); flex-wrap: wrap; }
.pos-tarta__comensales { display: flex; align-items: center; gap: var(--sp-2, 8px); flex-wrap: wrap; }
.pos-tarta__comensales strong { font-size: var(--fs-xl, 22px); min-width: 3ch; text-align: center; font-variant-numeric: tabular-nums; }
.pos-tarta__comensales .pos-btn { min-width: 52px; }
.pos-modsheet__cuenta { color: var(--muted); font-size: var(--fs-sm, .875rem); margin: 0; }

/* ── Hoja de SABORES (15/09/2026) ───────────────────────────────────── */
/* Encargo de Joni viendo la pantalla con el catálogo real: «al abrir se
   despliegan todos los sabores, no podemos tener una lista infernal de larga».
   Se abre SOBRE la rejilla de productos (no sobre el ticket, a diferencia de
   la hoja de complementos): elegir el sabor es todavía parte de elegir el
   producto, así que el ticket se sigue viendo al lado y no se pierde de vista
   lo que ya se lleva pedido.
   Los sabores van en una rejilla, no en una lista vertical: 31 Marujitas en
   columna obligarían a deslizar mucho; en rejilla se ven casi todas de un
   vistazo, que es lo que pedía el encargo. */
/* ── LA VENTANA ÚNICA DE ELECCIÓN (`.pos-eleccion`) ──────────────────── */
/* Joni, 15/09/2026, viendo la pantalla con el catálogo real: «que no se abra
   una pestaña en grande, sino una ventana en el centro de la app para
   seleccionarlo (como hace qamarero) [...] que se elija todo dentro de la
   ventana del producto y no haya 2/3 ventanas».

   Una caja centrada sobre la app, con velo detrás para que se vea que lo de
   debajo sigue ahí. Tres zonas: cabecera y pie fijos, y en medio lo único
   que scrollea — así el botón de añadir con su precio nunca se va de la
   vista, por muchas opciones que tenga el producto. */
.pos-eleccion { position: fixed; inset: 0; z-index: 40; display: flex; align-items: center; justify-content: center; padding: var(--sp-4, 16px); }
.pos-eleccion__velo { position: absolute; inset: 0; background: rgba(19, 7, 2, .46); }
.pos-eleccion__caja {
  position: relative;
  width: min(760px, 100%);
  max-height: min(84vh, 720px);
  display: flex;
  flex-direction: column;
  background: var(--pos-card);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-3, 0 24px 48px -12px rgba(19, 7, 2, .38));
  overflow: hidden;
}
.pos-eleccion__cab { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-3, 12px); padding: var(--sp-4, 16px); border-bottom: 1px solid var(--border); }
.pos-eleccion__cab h3 { font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo); font-size: var(--fs-xl, 22px); color: var(--brown); margin: 0; }
.pos-eleccion__sub { color: var(--muted); font-size: var(--fs-sm, .875rem); margin: 2px 0 0; }
.pos-eleccion__cuerpo { flex: 1; min-height: 0; overflow: auto; padding: var(--sp-4, 16px); display: flex; flex-direction: column; gap: var(--sp-4, 16px); }
.pos-eleccion__grupo { display: flex; flex-direction: column; gap: var(--sp-2, 8px); }
.pos-eleccion__ops { display: flex; flex-wrap: wrap; gap: var(--sp-2, 8px); }
/* Lo que todavía no se puede elegir se ve, apagado, diciendo qué falta antes
   — nunca un hueco vacío que parezca que ahí no hay nada. */
.pos-eleccion__espera { color: var(--muted); font-size: var(--fs-sm, .875rem); margin: 0; font-style: italic; }
.pos-eleccion__pie { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3, 12px); padding: var(--sp-3, 12px) var(--sp-4, 16px); border-top: 1px solid var(--border); background: var(--pos-ground); flex-wrap: wrap; }
.pos-eleccion__falta { color: var(--muted); font-size: var(--fs-sm, .875rem); margin: 0; }
.pos-eleccion__pie .pos-btn--grande { margin-left: auto; }
.pos-eleccion .pos-chip.out { opacity: .5; }
@media (max-width: 640px) {
  /* En el iPad en vertical y en el comandero, la caja ocupa lo que puede:
     una ventana pequeña con veinte sabores dentro obligaría a deslizar más
     de lo que se tarda en decir el sabor. */
  .pos-eleccion { padding: 0; }
  .pos-eleccion__caja { width: 100%; max-height: 100%; height: 100%; border-radius: 0; border: 0; }
}

.pos-varsheet { position: absolute; inset: 0; background: var(--bg); padding: var(--sp-4, 14px); display: flex; flex-direction: column; gap: var(--sp-3, 12px); z-index: 6; overflow: auto; }
.pos-varsheet__cab { display: flex; justify-content: space-between; align-items: center; gap: var(--sp-3, 10px); flex-wrap: wrap; }
.pos-varsheet__cab h3 { font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo); font-size: var(--fs-xl, 22px); color: var(--brown); margin: 0; }
.pos-varsheet__ops { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: var(--sp-3, 10px); align-content: start; }
.pos-varsheet__op {
  min-height: 72px; border-radius: var(--r-lg); border: 1px solid var(--pos-line);
  background: var(--pos-card); padding: var(--sp-3, 10px) var(--sp-3, 12px);
  display: flex; flex-direction: column; justify-content: center; gap: 4px;
  text-align: left; font: inherit; cursor: pointer;
}
.pos-varsheet__op:hover { border-color: var(--accent); }
.pos-varsheet__op .n { font-weight: 700; font-size: var(--fs-md, 15px); color: var(--ink); line-height: 1.2; }
.pos-varsheet__op .p { font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo); font-size: var(--fs-lg, 18px); color: var(--brown); }
/* Un sabor agotado se ve, pero no se puede pulsar: esconderlo haría creer que
   ese sabor no existe, y lo que pasa es que hoy no queda. */
.pos-varsheet__op.out { opacity: .55; cursor: not-allowed; }
.pos-varsheet__op.out:hover { border-color: var(--pos-line); }

/* Asistente de FAMILIA (15/09/2026): igual chasis que la hoja de sabores de
   arriba, con el paso actual + las migas de lo ya elegido + un botón
   «‹ Atrás» que solo aparece a partir del segundo paso. */
.pos-varsheet__titulos { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pos-varsheet__paso { margin: 0; font-size: var(--fs-sm, 13px); color: var(--muted); }
.pos-varsheet__migas { margin: 0; font-size: var(--fs-sm, 13px); color: var(--accent); font-weight: 600; }
.pos-varsheet__cab-botones { display: flex; gap: var(--sp-2, 8px); flex-shrink: 0; }

/* ── Hoja de cobro ──────────────────────────────────────────────────── */
/* CORREGIDO — verificación en navegador del 14/09 (encargo punto 1): esta
   hoja vive DENTRO de `.pos-ticket` (`position:absolute;inset:0` sobre el
   panel del ticket, nunca la pantalla entera — así la aprobó la maqueta,
   como una capa sobre el ticket, no un rediseño de toda la pantalla), que
   en el iPad real mide ~360px de ancho. Repartida a `1fr 1fr` (métodos |
   importe+teclado), cada columna se quedaba en ~154px — muy por debajo de
   los 236px que necesitan las 12 teclas del teclado numérico a su tamaño
   de siempre (72px, el mínimo del propio sistema, §11 SISTEMA_VISUAL_V2).
   Con `overflow:auto` de por medio (hace falta para el scroll vertical) el
   sobrante no se veía como una barra: se recortaba en silencio por los dos
   lados y la columna del 1/4/7/borrar quedaba pintada FUERA de la caja —
   confirmado con `elementFromPoint()` sobre la propia tecla "1": el toque
   caía en el fondo de `.pos-paysheet`, nunca en el botón.
   Arreglo de raíz — el reparto, no el ancho de una sola tecla: en vez de
   forzar dos columnas iguales en un panel que nunca tuvo sitio para las
   dos a la vez, esta hoja pasa a UNA sola columna (métodos arriba, importe
   y teclado debajo) — con todo el ancho del panel para el teclado, que
   vuelve a caber entero a su tamaño de siempre sin ninguna barra de
   desplazamiento — ver la nota de `.pos-keypad` (pos.css, más abajo) sobre
   por qué el propio componente del teclado se quedó con su ancho FIJO de
   siempre en vez de encogerse él solo. */
/* La hoja reparte su altura en tres filas y NO scrollea: el título y los
   métodos ocupan lo suyo, y todo lo demás —importe, atajos, cambio, teclado
   y el botón de cobrar— vive en la tercera, que es la que se adapta. Antes
   la hoja entera scrolleaba y el botón se anclaba encima con `sticky`, así
   que a poca altura de pantalla tapaba las teclas ←, 0 y OK (Joni,
   15/09/2026: «el botón de cobrar se solapa con los números»). Ahora lo que
   cede es el teclado, que puede encogerse sin dejar de ser pulsable. */
.pos-paysheet { position: absolute; inset: 0; background: var(--bg); padding: var(--sp-4, 16px) 18px; display: grid; grid-template-columns: 1fr; grid-template-rows: auto auto minmax(0, 1fr); gap: var(--sp-3, 12px); z-index: 5; overflow: hidden; }
.pos-paysheet h2 { font-size: var(--fs-xl, 24px); grid-column: 1 / -1; display: flex; justify-content: space-between; align-items: center; }
/* CORREGIDO (mismo hallazgo del 14/09): en una sola columna, los 3 métodos
   apilados verticalmente (200px: 3×60px + 2 huecos) empujaban el teclado
   fuera de la vista y obligaban a deslizar para llegar a él Y al botón
   «Cobrar» — technically ya no se recortaba nada (la hoja entera hace de
   único scroll, `.pos-paysheet` más arriba), pero un teclado que hay que
   ir a buscar deslizando tampoco es "se ve entero" de verdad. En fila (3
   columnas iguales, ~60px de alto en vez de 200px) deja sitio de sobra
   para que el teclado a su tamaño de siempre (72px, sin encoger) quepa
   SIN deslizar en el iPad real (1180×820) con una cuenta de una sola
   línea — verificado en navegador. */
.pos-paysheet .methods { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-3, 10px); align-content: start; }
/* T6: renombrada de `.meth` a `.pos-paysheet__metodo` — todo `<button>` del
   POS lleva una clase `pos-` (prohibición comprobable, §12.2), y `.meth`
   sola no la tenía; se añade `.pos-btn` en el marcado para que además
   herede foco/pulsado del catálogo base. */
.pos-paysheet .pos-paysheet__metodo { min-height: 60px; border-radius: var(--r-lg); border: 2px solid var(--border); background: var(--pos-card); display: flex; align-items: center; justify-content: center; gap: var(--sp-3, 12px); padding: 0 var(--sp-4, 16px); font-weight: 900; font-size: var(--fs-md, 16px); width: 100%; margin-top: 0; }
.pos-paysheet .pos-paysheet__metodo.on { border-color: var(--accent); background: var(--pos-info-bg); }
/* Mismo tamaño que el total del ticket — es el mismo dato en otro contexto,
   no debe leerse "más pequeño" al cobrar. CORREGIDO (maqueta v5 §9.2): el
   total del ticket son 36px, no 40px — gana la maqueta. */
.pos-paysheet .amount { font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo); font-size: 36px; text-align: center; color: var(--brown); min-height: 48px; font-variant-numeric: tabular-nums; }
/* T6: columna derecha de la hoja de cobro (importe/teclado/Cobrar) — sin
   esto los elementos quedaban pegados unos a otros sin el aire que ya tiene
   el resto del catálogo de hojas.
   CORREGIDO (mismo hallazgo del 14/09 que el comentario grande de arriba,
   segunda vuelta): al pasar `.pos-paysheet` a una sola columna, esta fila
   dejó de ser una de dos columnas iguales y pasó a compartir una fila
   `1fr` con `.methods` encima — `min-height:0; overflow:auto` aquí, pensado
   para cuando esta caja SÍ tenía casi toda la altura de la hoja para ella
   sola, ahora la encerraba en el hueco sobrante (bastante más pequeño) y
   volvía a recortar el teclado EN VERTICAL, en silencio, exactamente el
   mismo síntoma que el del ancho — solo que esta vez ni `elementFromPoint`
   hacía falta para verlo: el botón «Cobrar» quedaba fuera. Sin
   `overflow:auto`/`min-height:0` propios, esta caja crece a su alto real y
   es `.pos-paysheet` (con SU `overflow:auto`, más arriba) quien hace de
   único scroll de toda la hoja si hace falta — un solo sitio por el que
   deslizar, nunca dos recortes anidados peleándose por la misma altura. */
.pos-paysheet__cobro { display: flex; flex-direction: column; gap: var(--sp-3, 10px); min-height: 0; }
/* El teclado es lo único elástico de la hoja: se queda con lo que sobra y
   encoge cuando la pantalla es baja, en vez de empujar al botón fuera. */
.pos-paysheet__cobro [data-slot="teclado"] { flex: 1 1 auto; min-height: 0; display: flex; align-items: center; justify-content: center; }
.pos-paysheet__cobro .pos-keypad { height: 100%; grid-auto-rows: minmax(var(--pos-tap-min, 44px), 1fr); }
.pos-paysheet__cobro .pos-keypad__tecla { height: auto; min-height: var(--pos-tap-min, 44px); }
/* El botón nunca se encoge: es lo que se busca con la vista de cara al
   cliente y siempre está entero, debajo del teclado y sin taparlo. */
.pos-paysheet__cobro .pos-btn--grande { flex: 0 0 auto; }
/* Punto 3 del encargo: "el cambio a devolver en grande, que es lo que la
   empleada mira mientras el cliente espera" — CORREGIDO: 18px no cumplía
   ese encargo, se sube al mismo tratamiento que el total (Paytone One
   grande) para que sea lo primero que se lee en la hoja de cobro. */
.pos-paysheet .change { text-align: center; color: var(--ok-text); font-weight: 400; font-family: var(--pos-font-display); font-size: 32px; min-height: 40px; font-variant-numeric: tabular-nums; }
/* CORREGIDO (punto 3 del encargo, 14/09): `.is-corto` la pinta el propio
   `refrescarImportesCobro()` de tpv.js cuando el importe tecleado es menor
   que el total — el mismo verde de "Cambio: X €" (que SÍ significa "esto
   ha ido bien") se quedaba puesto en el mensaje que explica que NO se
   puede cobrar así; mismo tono que `.pos-tot--pendiente .v` para un cobro
   que queda a medias, no un color nuevo. */
.pos-paysheet .change.is-corto { color: var(--warn-text); font-size: var(--fs-lg, 20px); }
/* CORREGIDO (verificación en navegador del 14/09, tercera vuelta): con el
   teclado ya entero y accesible, el botón «Cobrar» grande seguía cayendo
   por debajo del pliegue en el iPad real — medido, `bottom:871` con la
   pantalla en 820, incluso en el caso normal de una cuenta de una línea con
   importe exacto. Se llegaba deslizando, y el cobro también se puede
   rematar con la tecla OK del teclado, así que no bloqueaba nada; pero el
   botón que dice «Cobrar» es el que busca con la vista quien está de cara
   al cliente, y no verlo se lee como "esto no está listo". Se ancla al pie
   de la hoja: pase lo que pase con el alto del contenido (un cambio largo,
   un aviso, tres métodos de pago), el botón está SIEMPRE a la vista.
   Fondo propio para que el contenido no se transparente por debajo al
   deslizar, y un pelo de aire arriba para que no quede pegado al teclado. */
/* Ya no hace falta anclarlo con `sticky`: la hoja le reserva su fila y el
   teclado cede altura si hace falta (ver `.pos-paysheet` más arriba). El
   `sticky` era lo que lo ponía ENCIMA de las teclas. */
.pos-paysheet__cobro .pos-btn[data-accion="pago-confirmar"] {
  margin-top: auto;
}

/* ── Plano de mesas ─────────────────────────────────────────────────── */
.pos-plan { padding: var(--sp-4, 16px); display: grid; gap: var(--sp-4, 16px); overflow: auto; align-content: start; }
.pos-zone h3 { font-family: var(--font-body); font-weight: 900; font-size: var(--fs-md, 15px); margin: 0 0 var(--sp-3, 10px); display: flex; align-items: center; gap: var(--sp-3, 10px); }
.pos-zone .tables { display: flex; flex-wrap: wrap; gap: var(--sp-3, 12px); }
/* .table de la maqueta → .pos-mesa (evita confusión con las tablas de datos
   del admin). §4.6 SISTEMA_VISUAL_V2.md: sillas dibujadas con dos
   pseudo-elementos (arriba/abajo) a base de `box-shadow` repetido — nunca un
   icono ni una imagen, siempre sand. El estado se lee en el color Y en la
   palabra de `.s` (rasgo 5, "nunca solo color"); `.imp`/`.t` (importe en
   Paytone One y tiempo transcurrido) quedan preparados para cuando Fase C
   pinte esta pantalla, sin usarse todavía en ningún markup real. */
.pos-mesa {
  position: relative;
  /* SISTEMA_VISUAL_V2 §4.9: sube de 112×92 a 128×108 — más aire para el
     tiempo transcurrido sin apretar el nombre. */
  width: 128px;
  height: 108px;
  border-radius: var(--pos-r-card);
  border: 2px solid var(--mrj-sand);
  background: var(--pos-card);
  color: var(--brown);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: var(--sp-3, 10px) var(--sp-3, 12px);
  text-align: left;
  transition: transform var(--pos-t), box-shadow var(--pos-t);
}
/* CORREGIDO (maqueta v5 §9.2: `.m::before,.m::after{left:27%;right:27%;
   height:6px}`) — las sillas eran dos barras fijas de 18px con
   box-shadow duplicado a 20px/54px; la maqueta las centra con left/right
   al 27% (ancho variable = 46% del centro de la mesa) y 6px de alto. */
.pos-mesa::before,
.pos-mesa::after {
  content: "";
  position: absolute;
  left: 27%;
  right: 27%;
  height: 6px;
  border-radius: 3px;
  background: var(--pos-line);
}
.pos-mesa::before { top: -9px; }
.pos-mesa::after { bottom: -9px; }
.pos-mesa:active { transform: scale(.96); }
.pos-mesa .nm { font-family: var(--pos-font-display); font-weight: 400; font-size: var(--fs-xl, 20px); }
.pos-mesa .s { font-size: 11px; font-weight: 900; letter-spacing: .06em; text-transform: uppercase; }
.pos-mesa .imp { font-family: var(--pos-font-display); font-weight: 400; font-size: var(--fs-lg, 18px); font-variant-numeric: tabular-nums; }
.pos-mesa .t { font-size: 11px; color: var(--mrj-mocha); font-weight: 700; }
/* Estados (§4.6): libre = superficie clara; ocupada = sand con tinta
   marrón; pidiendo = naranja lleno; cuenta = borde naranja de 2px;
   preparando = pastel ámbar (mismo tono que `.pos-badge.warn`). */
.pos-mesa.libre { border-color: var(--mrj-sand); background: var(--pos-card); color: var(--brown); }
.pos-mesa.ocupada { border-color: var(--mrj-sand); background: var(--mrj-sand); color: var(--brown); }
.pos-mesa.pidiendo { border-color: transparent; background: var(--accent); color: var(--mrj-cream); }
.pos-mesa.pidiendo .t { color: var(--mrj-cream); opacity: .8; }
.pos-mesa.cuenta { border-color: var(--accent); background: var(--pos-card); color: var(--brown); }
.pos-mesa.preparando { border-color: transparent; background: var(--pos-warn-bg); color: var(--warn-text); }
.pos-mesa.preparando .t { color: var(--warn-text); opacity: .8; }
/* SISTEMA_VISUAL_V2 §2.3/§4.9 (nuevo): "atascada" es un negocio DISTINTO de
   "cuenta pedida" — mesa sin atender >45min, ERROR real, nunca el mismo
   tono que un trámite normal. `m.seleccionada` (mesa abierta ahora mismo en
   pantalla) reutiliza el mismo lenguaje visual del anillo de foco, como
   "estás aquí" (visto en Qamarero: contorno naranja sobre la mesa activa). */
.pos-mesa.atascada { border-color: transparent; background: var(--pos-bad-bg); color: var(--bad-text); }
.pos-mesa.atascada .t { color: var(--bad-text); }
.pos-mesa.seleccionada { outline: 3px solid var(--accent); outline-offset: 2px; }
/* Sillas siempre en --pos-line: son el mueble, no el estado del pedido — el
   único naranja de la tarjeta es el que marca el estado (rasgo 2). A plena
   opacidad, como en la maqueta aprobada (antes se atenuaban al 80%). */

.pos-legend { display: flex; gap: var(--sp-4, 14px); flex-wrap: wrap; font-size: 12.5px; color: var(--mrj-mocha); font-weight: 700; }
.pos-legend i { display: inline-block; width: 12px; height: 12px; border-radius: 3px; margin-right: 6px; vertical-align: -1px; background: var(--mrj-sand); }
.pos-legend i.libre { background: var(--pos-card); border: 2px solid var(--mrj-sand); }
.pos-legend i.ocupada { background: var(--mrj-sand); }
.pos-legend i.pidiendo { background: var(--accent); }
.pos-legend i.cuenta { background: var(--pos-card); border: 2px solid var(--accent); }
.pos-legend i.preparando { background: var(--pos-warn-bg); }
/* T7: sexta leyenda — «atascada» es el estado nuevo del §2.3 (mesa sin
   atender >45min), mismo pastel que el chip `.pos-mesa.atascada`. */
.pos-legend i.atascada { background: var(--pos-bad-bg); }

/* ── T8 (docs/PANTALLA_MESA.md §1): TIRA DE MESAS dentro de la cuenta ────
   «arriba de la pantalla dentro de una mesa, que salgan el resto» — pestañas
   de zona + chips de mesa, cada uno con su punto de color de estado, de un
   solo toque. Vive en `.pos-tpv-body--mesa` (ver el grid más arriba). */
.pos-mesas-tira { background: var(--pos-ground); border-radius: var(--pos-r-card); padding: var(--sp-2, 8px) var(--sp-3, 12px); display: flex; flex-direction: column; gap: 6px; }
.pos-mtabs { display: flex; gap: 20px; overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
.pos-mtabs::-webkit-scrollbar { display: none; }
.pos-mtab { min-height: var(--pos-tap-min); padding: 0 4px; display: inline-flex; align-items: center; font-size: 12.5px; font-weight: 700; color: var(--mrj-mocha); white-space: nowrap; background: transparent; border: 0; }
.pos-mtab.on { color: var(--brown); box-shadow: inset 0 -2px 0 var(--accent); }
.pos-mtira__chips { display: flex; gap: 8px; overflow-x: auto; -webkit-overflow-scrolling: touch; padding-bottom: 2px; }
.pos-mtira__vacio { color: var(--muted); font-size: var(--fs-sm, 13px); margin: 4px 0; }
.pos-mchip {
  flex: 0 0 auto; display: flex; align-items: center; gap: 6px;
  min-height: var(--pos-tap-min); padding: 0 var(--sp-3, 12px);
  border-radius: 999px; background: var(--pos-card); border: 1px solid var(--pos-line);
  font-weight: 700; font-size: var(--fs-sm, 13px); color: var(--brown); white-space: nowrap;
  transition: transform var(--pos-t-touch) var(--ease);
}
.pos-mchip:active { transform: scale(.95); }
.pos-mchip .pt { width: 9px; height: 9px; border-radius: 50%; background: var(--mrj-sand); flex-shrink: 0; }
.pos-mchip .imp { font-family: var(--pos-font-display); font-weight: 400; font-size: 13px; opacity: .85; }
/* Mismos tonos que `.pos-mesa.*` (nunca un segundo mapa de color que pueda
   divergir del plano) — solo en el punto, el fondo del chip se queda claro
   para que quepan varias sin competir visualmente entre ellas. */
.pos-mchip.ocupada .pt { background: var(--mrj-mocha); }
.pos-mchip.pidiendo .pt { background: var(--accent); }
.pos-mchip.cuenta .pt { background: var(--brown); }
.pos-mchip.preparando .pt { background: var(--warn-text); }
.pos-mchip.atascada .pt { background: var(--bad-text); }
/* La mesa activa (donde ESTÁ la camarera ahora mismo) lleva un contorno
   naranja — mismo criterio que `.pos-mesa.seleccionada` del plano. */
.pos-mchip[aria-current="true"] { outline: 3px solid var(--accent); outline-offset: 1px; }

/* ── Listas (encargos, recogidas) ──────────────────────────────────── */
.pos-enc { display: grid; grid-template-columns: 1fr auto; gap: var(--sp-3, 12px); align-items: center; padding: var(--sp-3, 14px) var(--sp-4, 16px); border: 1px solid var(--border); border-radius: var(--r-lg); background: var(--pos-card); }
.pos-enc .n { font-weight: 900; font-size: var(--fs-md, 16px); }
.pos-enc .d { font-size: var(--fs-sm, 13px); color: var(--muted); }
.pos-listpad { padding: var(--sp-4, 16px); display: grid; gap: var(--sp-3, 10px); overflow: auto; align-content: start; }

/* ── Encargos: pantalla real (14/09/2026, círculo Encargar→POS→Entregado→OS) ──
   Mismas dos superficies que el resto del sistema (fondo `--pos-ground`,
   tarjeta `--pos-card`, borde `--pos-line`) y la viñeta de línea de 5px en
   `--accent` que fija la maqueta v5 §11.2 ("viñeta de línea de encargo:
   punto de 5px en --orange"). Reutiliza `.pos-filtro(s)`/`.pos-badge`/
   `.pos-campo`/`.pos-empty`/`.pos-aviso` ya existentes — nada de esto
   inventa un componente nuevo, solo compone los que ya hay. */
.pos-enc-toolbar { display: flex; align-items: center; gap: var(--sp-3, 10px); flex-wrap: wrap; }
.pos-enc-buscar { flex: 1 1 260px; max-width: 360px; margin: 0; }
.pos-enc-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: var(--sp-3, 12px); }
.pos-enc-card { background: var(--pos-card); border: 1px solid var(--pos-line); border-radius: var(--r-lg, 18px); overflow: hidden; display: flex; flex-direction: column; }
.pos-enc-card.is-cerrado { opacity: .72; }
.pos-enc-card__h { padding: 13px 14px 11px; }
.pos-enc-card__h b { font-size: 14px; display: block; }
.pos-enc-card__origen { font-size: 11px; font-weight: 700; color: var(--mrj-mocha); display: block; margin-top: 2px; }
.pos-enc-card__lineas { margin: 0; padding: 0 14px 12px; list-style: none; font-size: 12.5px; line-height: 1.85; color: var(--brown); flex: 1; }
.pos-enc-card__lineas li { position: relative; padding-left: 13px; }
.pos-enc-card__lineas li::before { content: ""; position: absolute; left: 0; top: 9px; width: 5px; height: 5px; border-radius: 50%; background: var(--accent); }
.pos-enc-card__f { border-top: 1px solid var(--pos-line); background: var(--pos-ground); padding: 11px 14px; display: flex; flex-direction: column; gap: 8px; }
.pos-enc-card__f-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.pos-enc-card__total { font-family: var(--pos-font-display); font-weight: 400; font-size: 19px; font-variant-numeric: tabular-nums; }
.pos-enc-card__frase { font-size: 11.5px; color: var(--mrj-mocha); line-height: 1.4; }
.pos-enc-card__f-bottom .pos-btn { margin-top: 0; width: 100%; }

/* ── Socia del Club en el ticket (encargo 2, 14/09/2026) — chip de un toque
   dentro de `.pos-ticket .th`, mismo `.pos-chip` de siempre. */
.pos-socia-row { margin-top: 8px; }
.pos-socia-row .pos-chip { display: inline-flex; }

/* ── Pantalla de bloqueo con PIN (empleado) ───────────────────────────── */
/* CORREGIDO por el corrector de Tanda 3 (hallazgo alta #1, verificado en un
   iPad apaisado real a 1180×820, resolución de referencia de E19):
   `display:grid;place-items:center` sin overflow controlado dejaba cortado
   el pie de la pantalla (aviso "Ficha tu entrada..." + enlace de
   emparejar) cuando el contenido no cabía entero — scrollHeight solo daba
   35px extra, insuficientes. `display:flex` + `margin:auto` en `.box` (en
   vez de centrar con `align-items`/`place-items` en el padre) centra igual
   cuando cabe y, si no cabe, el desbordamiento queda alcanzable con scroll
   en vez de recortado por los dos lados — es el patrón estándar para evitar
   el "contenido inalcanzable" que produce `place-items:center` combinado
   con overflow. Junto al arreglo del hallazgo #2 (el campo de teléfono ya
   no se pinta visible por defecto) y los gaps reducidos de abajo, el caso
   normal de acceso por chip vuelve a caber sin scroll en 1180×820. */
.pos-lock { position: absolute; inset: 0; background: var(--brown); color: var(--mrj-cream); z-index: 12; display: flex; overflow-y: auto; padding: var(--sp-3, 12px) 0; }
.pos-lock .box { text-align: center; width: 360px; margin: auto; }
.pos-lock h2 { font-size: var(--fs-xl, 26px); color: var(--mrj-cream); }
.pos-lock p { color: var(--mrj-sand); margin: 4px 0; }
.pos-lock img { height: 56px; margin-bottom: 4px; }
.pos-lock .pos-keypad button { background: rgba(252, 236, 215, .08); border-color: rgba(252, 236, 215, .18); color: var(--mrj-cream); }
.pos-lock .pos-keypad button:active { background: var(--accent); color: var(--mrj-ink); }
.pos-lock .pos-pin-punto { border-color: var(--mrj-sand); }
.pos-lock .pos-pin-punto.is-relleno { background: var(--accent); border-color: var(--accent); }
.pos-lock .emps { display: flex; gap: var(--sp-2, 8px); justify-content: center; flex-wrap: wrap; margin: var(--sp-2, 8px) 0 4px; }
.pos-lock .emps .pos-chip { background: rgba(252, 236, 215, .08); border-color: rgba(252, 236, 215, .2); color: var(--mrj-cream); }
.pos-lock .emps .pos-chip.on, .pos-lock .emps .pos-chip.is-activo { background: var(--mrj-cream); color: var(--brown); }
.pos-lock .pos-btn--ancho { margin-top: var(--sp-2, 8px); }

/* ── Comandero (móvil, una mano) ──────────────────────────────────────── */
.pos-mob { display: grid; grid-template-rows: 56px 1fr; height: 100%; }
.pos-mob .pos-head { height: 56px; padding: 0 var(--sp-3, 12px); }
.pos-mob .pos-head .logo { min-width: 0; }
.pos-mob .pos-head .logo img { height: 30px; }
.pos-mob-body { display: grid; grid-template-rows: auto 1fr auto; min-height: 0; }
.pos-mob .read { padding: var(--sp-3, 10px) var(--sp-4, 14px); border-bottom: 1px solid var(--border); background: var(--pos-card); min-height: 110px; max-height: 190px; overflow: auto; }
.pos-mob .read .t { display: flex; align-items: center; gap: var(--sp-3, 10px); font-weight: 900; font-size: var(--fs-md, 16px); margin-bottom: 6px; }
.pos-mob .read .ln { display: flex; justify-content: space-between; font-size: var(--fs-sm, 14px); padding: 3px 0; }
.pos-mob .read .ln.sent { color: var(--muted); }
.pos-mob .mid { overflow: auto; padding: var(--sp-3, 10px) var(--sp-3, 12px); display: flex; flex-direction: column; gap: var(--sp-3, 10px); }
.pos-mob .catrow { display: flex; gap: var(--sp-2, 8px); overflow-x: auto; padding-bottom: 4px; scrollbar-width: none; flex: 0 0 auto; }
.pos-mob .pos-grid { grid-template-columns: repeat(3, 1fr); gap: var(--sp-2, 8px); }
.pos-mob .pos-prod { min-height: 88px; padding: var(--sp-3, 10px); }
.pos-mob .pos-prod .n { font-size: var(--fs-sm, 14px); }
.pos-mob .thumb { padding: var(--sp-3, 10px) var(--sp-3, 12px) 14px; background: var(--pos-card); border-top: 1px solid var(--border); display: grid; gap: var(--sp-2, 8px); }
.pos-mob .thumb .two { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-2, 8px); }
.pos-mob .pos-plan.zones { padding: var(--sp-3, 12px); gap: var(--sp-3, 14px); }
.pos-mob .zones .tables { gap: var(--sp-3, 10px); }
.pos-mob .pos-mesa { width: calc(33.33% - 7px); height: 84px; padding: var(--sp-2, 8px) var(--sp-3, 10px); }
.pos-mob .pos-modes { padding: 3px; }
/* PENDIENTE (hallazgo baja #3 del corrector de Tanda 3): 38px incumple el
   mínimo táctil de 48px de E12. Hoy no es visible — ningún .pos-mob se
   pinta todavía (CSS adelantado para la Fase C del comandero móvil, sin
   enrutar) — pero quien construya esa fase debe subir este valor a
   var(--pos-tap-min) (48px) ANTES de enrutar cualquier página real sobre
   .pos-mob, en vez de heredarlo tal cual. */
.pos-mob .pos-modes button { min-height: 38px; padding: 0 var(--sp-2, 10px); font-size: var(--fs-sm, 13px); }

/* ── Cliente QR (mesa/pickup) ──────────────────────────────────────────── */
/* `.pos-cli` es la pantalla RAÍZ del cliente QR (mesa/pickup) — equivalente
   a `.pos-shell` para este contexto, no una tarjeta: fondo --pos-ground. */
.pos-cli { display: grid; grid-template-rows: auto 1fr auto; height: 100%; background: var(--pos-ground); }
.pos-cli .hd { padding: 14px 18px 10px; text-align: center; background: var(--brown); color: var(--mrj-cream); }
.pos-cli .hd img { height: 40px; }
.pos-cli .hd .mesa { font-size: 12px; color: var(--mrj-sand); font-weight: 700; letter-spacing: .1em; text-transform: uppercase; margin-top: 4px; }
.pos-cli .timer { display: inline-flex; align-items: center; gap: var(--sp-2, 8px); margin-top: var(--sp-2, 8px); padding: 5px 12px; border-radius: 999px; background: var(--mrj-cream); font-size: var(--fs-sm, 13px); font-weight: 700; color: var(--brown); }
.pos-cli .timer.late { background: var(--pos-bad-bg); color: var(--bad-text); } /* ventana de 15 min agotada (E20): hay que reescanear */
.pos-cli .carta { overflow: auto; padding: 8px 14px 14px; } /* "menu" de la maqueta → carta, para no chocar con pos-menu-pop */
.pos-cli .sec { font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo); font-size: var(--fs-md, 16px); margin: 14px 0 8px; color: var(--brown); }
.pos-cli .it { display: grid; grid-template-columns: 64px 1fr auto; gap: var(--sp-3, 12px); align-items: center; padding: var(--sp-3, 10px) 0; border-bottom: 1px solid var(--border); }
/* Hallazgo (e): el placeholder de imagen del cliente QR llevaba un
   degradado sand→cream; se sustituye por color plano de la paleta. */
.pos-cli .it .ph { width: 64px; height: 64px; border-radius: var(--r-md); background: var(--pos-line); display: grid; place-items: center; font-size: 26px; }
.pos-cli .it .n { font-weight: 900; }
.pos-cli .it .d { font-size: 12.5px; color: var(--muted); }
.pos-cli .it .al { font-size: 11px; color: var(--warn-text); font-weight: 700; } /* alérgenos */
.pos-cli .it .pr { font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo); color: var(--brown); font-size: var(--fs-md, 16px); text-align: right; }
.pos-cli .add { width: 44px; height: 44px; border-radius: 50%; background: var(--accent); color: var(--mrj-ink); font-size: 22px; font-weight: 900; display: grid; place-items: center; margin-top: 6px; margin-left: auto; }
.pos-cli .add.q { background: var(--brown); color: var(--mrj-cream); font-family: var(--font-body); font-size: var(--fs-sm, 15px); width: auto; padding: 0 var(--sp-3, 12px); border-radius: 999px; }
.pos-cli .bar { padding: var(--sp-3, 12px) 14px 16px; border-top: 1px solid var(--border); background: var(--pos-card); }
.pos-cli .bar .sum { display: flex; justify-content: space-between; font-weight: 900; margin-bottom: var(--sp-2, 8px); }
.pos-cli .modo { padding: var(--sp-5, 24px) var(--sp-4, 20px); display: grid; gap: var(--sp-3, 14px); align-content: center; text-align: center; overflow: auto; }
.pos-cli .modo h2 { font-size: var(--fs-xl, 24px); color: var(--brown); }
.pos-cli .modo .opt { min-height: 96px; border-radius: var(--r-xl); border: 2px solid var(--border); background: var(--mrj-cream); display: grid; gap: 4px; place-content: center; font-weight: 900; font-size: var(--fs-lg, 18px); }
.pos-cli .modo .opt small { font-weight: 400; color: var(--muted); font-size: var(--fs-sm, 13px); }
.pos-cli .status { padding: var(--sp-5, 28px) var(--sp-4, 20px); text-align: center; display: grid; gap: var(--sp-3, 12px); align-content: center; overflow: auto; }
.pos-cli .status .big { font-size: 64px; }
.pos-cli .status h2 { font-size: var(--fs-xl, 24px); color: var(--brown); }
.pos-cli .status .track { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; margin: var(--sp-2, 8px) 0; }
.pos-cli .status .track div { padding: var(--sp-2, 10px) 4px; border-radius: var(--r-md); background: var(--pos-ground); font-size: 12px; font-weight: 900; color: var(--muted); }
.pos-cli .status .track div.on { background: var(--brown); color: var(--mrj-cream); }
.pos-cli .status .track div.done { background: var(--pos-ok-bg); color: var(--ok-text); }
/* Hallazgo (e): la textura de QR falsa era un degradado rayado a cuadros;
   se sustituye por un bloque de color plano de marca (placeholder honesto,
   no finge ser un QR real). */
.pos-qr { width: 150px; height: 150px; margin: 0 auto; background: var(--mrj-ink); border: 8px solid var(--pos-card); outline: 2px solid var(--pos-line); border-radius: 8px; opacity: .9; }

/* ── Obrador (antes "KDS"; E26): en vivo · producción hoy · pedidos · ayer */
.pos-obr { display: grid; grid-template-rows: 60px 1fr; height: 100%; background: var(--bg); color: var(--text); }
.pos-obr .pos-head { background: var(--brown); }
.pos-obr-tabs { display: flex; gap: 6px; padding: var(--sp-3, 10px) var(--sp-3, 12px) 0; }
/* Nota de la derecha (p.ej. "actualizado hace 1 min" o el rótulo de fase de
   un boceto, E30) — equivalente al `style="margin-left:auto;..."` puntual
   de la maqueta v4, aquí como clase para no colar estilo inline (CSP). */
.pos-obr-tabs .meta { margin-left: auto; align-self: center; font-size: 12.5px; color: var(--muted); }
/* CORREGIDO por el corrector de Tanda 3 (hallazgo media #5): 44px incumplía
   el mínimo táctil de 48px de E12; unificado con var(--pos-tap-min). */
.pos-obr-tabs button { min-height: var(--pos-tap-min); padding: 0 var(--sp-4, 16px); border-radius: 999px; font-weight: 900; color: var(--muted); background: var(--pos-card); border: 1.5px solid var(--border); display: flex; align-items: center; gap: var(--sp-2, 8px); }
.pos-obr-tabs button.on { background: var(--accent); color: var(--mrj-ink); border-color: transparent; }
.pos-obr-tabs .b { background: var(--pos-ground); border-radius: 999px; padding: 1px 8px; font-size: 12px; }
.pos-obr-tabs button.on .b { background: rgba(19, 7, 2, .2); }
.pos-obr-body { display: grid; grid-template-rows: auto 1fr; min-height: 0; }
.pos-kcols { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-3, 12px); padding: var(--sp-3, 12px); min-height: 0; }
.pos-kcol { background: var(--pos-ground); border-radius: var(--r-lg); display: grid; grid-template-rows: auto 1fr; min-height: 0; }
.pos-kcol h2 { font-size: var(--fs-sm, 15px); letter-spacing: .14em; text-transform: uppercase; font-family: var(--font-body); font-weight: 900; padding: var(--sp-3, 12px) 14px; display: flex; justify-content: space-between; color: var(--muted); }
.pos-kcol .cnt { background: var(--accent); color: var(--mrj-ink); border-radius: 999px; padding: 0 10px; font-size: var(--fs-sm, 13px); }
.pos-kcol .list { overflow: auto; padding: 0 10px 10px; display: grid; gap: var(--sp-3, 10px); align-content: start; }
/* Hallazgo (d): borde plano de 1px `--border` → mismo tratamiento
   suavizado que `.pos-prod` (color-mix al 55%, §3 SISTEMA_VISUAL_V2.md). */
.pos-kc { background: var(--pos-card); border: 1px solid color-mix(in srgb, var(--border) 55%, transparent); border-radius: var(--r-lg); border-left: 6px solid var(--mrj-sand); padding: var(--sp-3, 12px) 14px; display: grid; gap: var(--sp-2, 8px); box-shadow: var(--shadow-1); }
.pos-kc.new { border-left-color: var(--accent); }
.pos-kc.late { border-left-color: var(--bad-text); }
.pos-kc .h { display: flex; justify-content: space-between; align-items: center; }
.pos-kc .h b { font-family: var(--pos-font-display); font-size: var(--fs-xl, 20px); font-weight: 400; }
.pos-kc .h .t { font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo); font-size: var(--fs-lg, 18px); color: var(--muted); }
.pos-kc.late .h .t { color: var(--bad-text); }
.pos-kc .src { font-size: 12px; color: var(--muted); font-weight: 700; letter-spacing: .06em; text-transform: uppercase; }
.pos-kc ul { margin: 0; padding: 0; list-style: none; display: grid; gap: 4px; }
.pos-kc li { display: flex; gap: var(--sp-3, 10px); font-size: var(--fs-md, 16px); }
.pos-kc li b { color: var(--brown); font-family: var(--pos-font-display); font-weight: 400; min-width: 28px; }
.pos-kc li small { display: block; color: var(--muted); font-size: 12.5px; }
.pos-kc .act { display: flex; gap: var(--sp-2, 8px); }
.pos-kc .act .pos-btn { flex: 1; background: var(--pos-card); border-color: var(--border); color: var(--text); }
.pos-kc .act .pos-btn.primary { background: var(--accent); color: var(--mrj-ink); }
.pos-obr .pos-empty { color: var(--muted); }
.pos-ob-grid { padding: var(--sp-3, 12px); display: grid; gap: var(--sp-3, 12px); overflow: auto; min-height: 0; align-content: start; }
.pos-ob-grid.pos-two { grid-template-columns: 1fr 1fr; }
.pos-ob-card { background: var(--pos-card); border: 1px solid color-mix(in srgb, var(--border) 55%, transparent); box-shadow: var(--shadow-1); border-radius: var(--r-lg); padding: 14px 16px; display: grid; gap: var(--sp-2, 8px); align-content: start; }
.pos-ob-card h2 { font-size: var(--fs-sm, 15px); letter-spacing: .14em; text-transform: uppercase; font-family: var(--font-body); font-weight: 900; color: var(--muted); display: flex; justify-content: space-between; align-items: center; }
.pos-ob-card h2 .src { font-size: 10.5px; letter-spacing: .08em; opacity: .7; font-weight: 700; }
.pos-ob-row { display: grid; grid-template-columns: 1fr auto auto; gap: var(--sp-3, 12px); align-items: center; padding: var(--sp-2, 8px) 0; border-bottom: 1px solid var(--border); font-size: var(--fs-md, 15px); }
.pos-ob-row:last-child { border-bottom: 0; }
.pos-ob-row b { font-family: var(--pos-font-display); font-weight: 400; font-size: var(--fs-lg, 18px); color: var(--brown); min-width: 44px; text-align: right; }
.pos-ob-row small { display: block; color: var(--muted); font-size: 12px; }
.pos-ob-row .prog { width: 120px; height: 10px; border-radius: 999px; background: var(--pos-ground); overflow: hidden; }
.pos-ob-row .prog i { display: block; height: 100%; background: var(--accent); }
.pos-ob-kpis { display: grid; grid-template-columns: repeat(5, 1fr); gap: var(--sp-3, 10px); }
.pos-ob-kpi { background: var(--pos-card); border: 1px solid color-mix(in srgb, var(--border) 55%, transparent); box-shadow: var(--shadow-1); border-radius: var(--r-lg); padding: 12px 14px; text-align: left; color: var(--text); }
.pos-ob-kpi .l { font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); font-weight: 900; }
.pos-ob-kpi .v { font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo); font-size: var(--fs-xl, 28px); color: var(--brown); }
.pos-ob-kpi .s { font-size: 12px; color: var(--muted); }
.pos-ob-kpi.on { outline: 2px solid var(--accent); }

/* ── Extensiones de .pos-admin (Fase A) para los widgets de la maqueta —
   NO se toca nada de lo ya declarado arriba (nav/panel/seccion/tabla),
   solo se añaden los KPI y el layout de dos columnas del escritorio. ── */
.pos-admin__kpis { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--sp-3, 12px); }
.pos-admin__kpi { background: var(--pos-card); border: 1px solid color-mix(in srgb, var(--border) 55%, transparent); border-radius: var(--r-lg); padding: 14px 16px; box-shadow: var(--shadow-1); }
.pos-admin__kpi .l { font-size: 11.5px; font-weight: 900; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.pos-admin__kpi .v { font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo); font-size: var(--fs-2xl, 30px); color: var(--brown); margin-top: 4px; }
.pos-admin__kpi .s { font-size: 12.5px; color: var(--muted); }
.pos-admin__dos-columnas, .pos-two { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-4, 16px); }
.pos-admin__tabla td.r, .pos-admin__tabla th.r { text-align: right; }

/* ── Fichajes del equipo, en la barra del TPV (E28) ────────────────────── */
.pos-fich { padding: var(--sp-4, 16px); overflow: auto; display: grid; gap: var(--sp-3, 14px); align-content: start; }
.pos-fich .aviso { background: var(--pos-info-bg); border-radius: var(--r-lg); padding: 12px 16px; font-size: 13.5px; color: var(--brown); display: flex; gap: var(--sp-3, 10px); align-items: center; }
.pos-fgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: var(--sp-3, 12px); }
.pos-fcard { background: var(--pos-card); border: 1px solid color-mix(in srgb, var(--border) 55%, transparent); border-radius: var(--r-lg); padding: 14px 16px; display: grid; gap: var(--sp-3, 10px); box-shadow: var(--shadow-1); }
.pos-fcard.dentro { border-color: var(--ok-text); background: var(--pos-ok-bg); }
.pos-fcard.descanso { border-color: var(--warn-text); background: var(--pos-warn-bg); }
.pos-fcard .top { display: flex; align-items: center; gap: var(--sp-3, 10px); }
.pos-fcard .av { width: 42px; height: 42px; border-radius: 50%; background: var(--brown); color: var(--mrj-cream); display: grid; place-items: center; font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo); font-size: var(--fs-lg, 18px); }
.pos-fcard .top > span:last-child { display: grid; gap: 2px; min-width: 0; }
.pos-fcard .nm { display: block; font-weight: 900; font-size: var(--fs-lg, 17px); line-height: 1.15; }
.pos-fcard .rl { display: block; font-size: 12px; color: var(--muted); font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pos-fcard .est { font-size: var(--fs-sm, 13px); font-weight: 700; line-height: 1.25; max-width: 58%; }
.pos-fcard .hrs { font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo); font-size: var(--fs-xl, 22px); color: var(--brown); }
.pos-fcard .acts { display: grid; gap: var(--sp-2, 8px); }
.pos-fcard .acts.pos-two { grid-template-columns: 1fr 1fr; }
.pos-fpin { position: absolute; inset: 0; background: rgba(19, 7, 2, .6); display: grid; place-items: center; z-index: 10; } /* pide SIEMPRE el PIN de la persona que ficha, no el de la sesión abierta (E28) */
.pos-fpin .box { background: var(--pos-card); border-radius: var(--r-xl); padding: 22px; width: 360px; text-align: center; box-shadow: var(--shadow-3); }
.pos-fpin h2 { font-size: var(--fs-xl, 22px); }
.pos-fpin p { color: var(--muted); margin: 4px 0 0; }

/* ── Control — solo ADMIN, mismo componente en el TPV (modo) y en el
   escritorio de admin (E29) ───────────────────────────────────────────── */
.pos-ctrl { display: grid; grid-template-columns: 210px 1fr; min-height: 0; height: 100%; }
/* `.pos-ctrl-nav` NUEVO (§4.1 SISTEMA_VISUAL_V2.md): lista de texto sin
   iconos, activa en píldora naranja, rótulos de grupo en mayúsculas 11px.
   El markup real (navControl() en pos-ui.js) monta `<nav class="nav">`
   dentro de `.pos-ctrl` — se declara aquí con los dos selectores a la vez
   para que valga tanto para ese markup existente como para `.pos-ctrl-nav`
   suelto el día que una pantalla nueva lo use directo. */
/* CORREGIDO (maqueta v5 §9.1/§7.1 SISTEMA_VISUAL_V2.md: la navegación de
   Control pasa "de una lista suelta a un contenedor único" — es la misma
   `.nav` tarjeta que en la maqueta usa `background:var(--card)`). */
.pos-ctrl .nav,
.pos-ctrl-nav {
  background: var(--pos-card);
  border-right: 1px solid var(--border);
  padding: 12px 10px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  overflow: auto;
}
/* CORREGIDO (maqueta v5 §9.2): rótulo de grupo Lato 700 10px, letter-spacing
   .09em, en --mocha — no 900/11px/.1em/--muted. Mismo `.cgrp` que en la
   barra de categorías del TPV (la maqueta reutiliza la misma clase en las
   dos navegaciones). */
.pos-ctrl .nav .g,
.pos-ctrl-nav .g {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--mrj-mocha);
  padding: 14px 10px 6px;
}
.pos-ctrl .nav .g:first-child,
.pos-ctrl-nav .g:first-child { padding-top: 6px; }
/* CORREGIDO por el corrector de Tanda 3 (hallazgo media #5): 44px incumplía
   el mínimo táctil de 48px de E12; unificado con var(--pos-tap-min).
   Rasgo 4 (píldora para lo activo, §3): radio 999px, no --r-md. */
.pos-ctrl .nav button,
.pos-ctrl-nav button {
  min-height: var(--pos-tap-min);
  border-radius: 999px;
  padding: 0 var(--sp-3, 12px);
  text-align: left;
  color: var(--brown);
  background: transparent;
  font-weight: 700;
  font-size: var(--fs-sm, 14px);
  display: flex;
  align-items: center;
  transition: background var(--pos-t), color var(--pos-t);
}
.pos-ctrl .nav button.on,
.pos-ctrl-nav button.on { background: var(--accent); color: var(--mrj-cream); }
.pos-ctrl .nav button:not(.on):hover,
.pos-ctrl-nav button:not(.on):hover { background: var(--mrj-sand); }
/* Principio 3 ("nada de iconos en navegación") + prohibición §6.5: se
   retira por completo el estilo del icono de esta nav — Joni pidió
   quitarlos. El span sigue llegando desde navControl() si alguna sección
   trae `icono`, pero deja de pintarse; el texto de cada sección basta. */
.pos-ctrl-ico { display: none; }
/* SISTEMA_VISUAL_V2 §7.1: cada grupo lleva una franja de color (uno de los
   5 tintes de §6.2, reasignados a GRUPO en vez de a categoría de producto)
   — "agrupar por color" en vez de un icono distinto por fila. `navControl()`
   envuelve cada grupo en esta sección; `display:flex` conserva el apilado
   vertical de `.g` + botones que antes daba `.pos-ctrl-nav` directamente. */
.pos-ctrl-grp { display: flex; flex-direction: column; gap: 2px; border-left: 3px solid transparent; padding-left: 6px; margin-left: 2px; transition: border-color var(--pos-t); }
.pos-ctrl-grp--op { border-left-color: var(--cat-op, var(--mrj-sand)); }
.pos-ctrl-grp--marca { border-left-color: var(--cat-marca, var(--mrj-sand)); }
.pos-ctrl-grp--b2b { border-left-color: var(--cat-b2b, var(--mrj-sand)); }
.pos-ctrl-grp--control { border-left-color: var(--cat-control, var(--mrj-sand)); }
.pos-ctrl-grp--finanzas { border-left-color: var(--cat-finanzas, var(--mrj-sand)); }
@supports selector(:has(a)) {
  .pos-ctrl-grp:has(button.on) { border-left-width: 5px; }
}
.pos-cbox .big.pos-cbox__big--pendiente { color: var(--pos-border-2); }
.pos-ctrl .main { padding: 16px 18px; overflow: auto; display: grid; gap: 14px; align-content: start; }
.pos-ctrl h1 { font-size: var(--fs-2xl, 22px); margin-bottom: 2px; }
.pos-ctrl .sub { color: var(--muted); font-size: var(--fs-sm, 13px); margin-bottom: 4px; }
.pos-cgrid { display: grid; gap: var(--sp-3, 12px); }
.pos-cgrid.k4 { grid-template-columns: repeat(4, 1fr); }
.pos-cgrid.k3 { grid-template-columns: repeat(3, 1fr); }
.pos-cgrid.k2 { grid-template-columns: 1fr 1fr; }
.pos-cbox { background: var(--pos-card); border: 1px solid color-mix(in srgb, var(--border) 55%, transparent); border-radius: var(--r-lg); padding: 14px 16px; box-shadow: var(--shadow-1); display: grid; gap: var(--sp-2, 8px); align-content: start; }
.pos-cbox h2 { font-family: var(--font-body); font-weight: 900; font-size: 12px; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); display: flex; justify-content: space-between; align-items: center; gap: var(--sp-3, 10px); }
.pos-cbox h2 a, .pos-cbox h2 .lk { font-size: 12px; font-weight: 700; color: var(--brown); text-transform: none; letter-spacing: 0; }
.pos-cbox .big { font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo); font-size: 34px; color: var(--brown); line-height: 1; }
.pos-cbox .sm { font-size: var(--fs-sm, 13px); color: var(--muted); }
.pos-cbox.dark { background: var(--brown); color: var(--mrj-cream); border-color: transparent; }
.pos-cbox.dark h2, .pos-cbox.dark .sm { color: var(--mrj-sand); }
.pos-cbox.dark .big { color: var(--mrj-cream); }
.pos-crow { display: grid; grid-template-columns: 1fr auto; gap: var(--sp-3, 10px); align-items: center; padding: var(--sp-2, 8px) 0; border-bottom: 1px solid var(--border); font-size: var(--fs-md, 14px); }
.pos-crow:last-child { border-bottom: 0; }
.pos-crow small { display: block; color: var(--muted); font-size: 12px; }
.pos-crow b { font-family: var(--pos-font-display); font-weight: 400; color: var(--brown); font-size: var(--fs-lg, 17px); }
/* CORREGIDO por el corrector final (hallazgo BAJA, verificado en vivo con
   getBoundingClientRect: el badge de estado y las horas quedaban al mismo
   píxel exacto, cero separación — Control → Personal, bloque "Ahora
   mismo"). */
.pos-crow-info { display: flex; align-items: center; gap: var(--sp-2, 8px); }
/* CORREGIDO (maqueta v5 §9.1/§9.2, `.seg`/`.seg span`): el segmented
   control es uno de los elementos HUNDIDOS explícitos de la maqueta —
   contenedor en píldora --pos-ground, botones sin fondo propio, activo en
   --accent. No estaba usado en ningún markup real todavía (sin riesgo de
   regresión), así que se alinea entero a la maqueta en vez de solo
   sustituir el token. */
.pos-seg { display: inline-flex; gap: 3px; background: var(--pos-ground); border-radius: 999px; padding: 3px; flex-wrap: wrap; }
.pos-seg button { min-height: 38px; padding: 0 13px; border-radius: 999px; border: 0; background: transparent; color: var(--mrj-mocha); font-weight: 700; font-size: var(--fs-sm, 12px); }
.pos-seg button.on { background: var(--accent); color: var(--brown); }
.pos-bar-mini { display: flex; align-items: flex-end; gap: 4px; height: 70px; }
.pos-bar-mini i { flex: 1; background: var(--mrj-sand); border-radius: 3px 3px 0 0; display: block; }
.pos-bar-mini i.hi { background: var(--accent); }
.pos-ruta { display: grid; grid-template-columns: 1fr 150px 110px auto; gap: var(--sp-3, 10px); align-items: center; padding: 9px 0; border-bottom: 1px solid var(--border); font-size: var(--fs-md, 14px); }
.pos-ruta:last-child { border-bottom: 0; }
.pos-ruta select { min-height: 38px; border-radius: var(--r-md); border: 1.5px solid var(--border); background: var(--pos-card); padding: 0 8px; font: inherit; color: inherit; }

/* ── T2: pantalla de acceso por nombre + fichajes del equipo (E19/E28) ───
   El resto de clases de este bloque (.pos-lock, .pos-lock .emps, .pos-fich,
   .pos-fgrid, .pos-fcard, .pos-fpin, .pos-cbox, .pos-crow, .pos-badge) ya
   existían (Tanda 1, construidas por adelantado para esta tanda) — aquí
   solo se añade lo que de verdad faltaba: la posición del overlay de PIN
   sobre el cuerpo de fichajes y el armazón de la pantalla "solo fichar"
   (terminal bloqueado, sin sesión, con su barra inferior fija). */
.pos-fich { position: relative; } /* ancla de .pos-fpin (position:absolute; inset:0) */
.pos-fichajes-bloqueado { display: grid; grid-template-rows: 60px 1fr auto; min-height: 100vh; }
.pos-fichajes-bloqueado > main { min-height: 0; overflow: auto; }
.pos-fichajes-pie { padding: var(--sp-3, 12px) var(--sp-4, 16px) 14px; border-top: 1px solid var(--border); background: var(--pos-card); }
/* Nota bajo el teléfono confirmado la primera vez en un chip de acceso sin
   teléfono conocido aún en este dispositivo (E19: "sin exponer teléfonos
   completos" — la primera vez en CADA dispositivo hay que teclearlo una
   vez; pos-core.js lo recuerda despues, enmascarado, solo en ese terminal). */
.pos-lock .campo-telefono-inline { margin: 6px 0 4px; }
.pos-lock .pos-campo { background: rgba(252, 236, 215, .08); border-color: rgba(252, 236, 215, .28); color: var(--mrj-cream); }
.pos-lock .pos-campo::placeholder { color: var(--mrj-sand); opacity: .8; }

/* `.pos-ctrl-ico` se declara arriba, junto a `.pos-ctrl-nav` (§4.1): queda
   retirada por completo (display:none) — Joni pidió quitar esos iconos.
   No se duplica aquí. */

/* ═══════════════════════════════════════════════════════════════════════
   TANDA MOVIMIENTO Y RESPUESTA — encargo de Joni, 2026-09-14
   ───────────────────────────────────────────────────────────────────────
   «Necesitamos que aún sea más moderno el diseño ux/ui, con interacciones
   y modernidades mejores (…) la fluidez tiene que ser inmejorable, tiene
   que verse como la mejor app del mercado.»

   El ASPECTO no cambia (contrato: docs/maqueta-v5-aprobada.html, gana
   sobre todo): ni un color ni una forma nuevos en esta tanda. Todo lo de
   aquí abajo es MOVIMIENTO — cómo responde el dedo, cómo entra/sale una
   hoja, cómo se ve un dato que todavía no ha llegado. Techo de motion en
   el flujo de cobro: 200ms (--pos-t-cobro, ya existía). Todo lo nuevo
   respeta `prefers-reduced-motion` igual que ya lo hacía el resto de la
   capa POS (bloque de la línea ~150) — con un añadido: ese bloque solo
   apaga `.pos-shell, .pos-shell *`, y algunos componentes de esta tanda
   (`.pos-hoja`, `.pos-esqueleto`, el enlace de salto) pueden vivir FUERA
   de esa raíz, igual que ya le pasaba a `.pos-menu-pop` (comentario de la
   línea ~188) — se cubren explícitamente más abajo.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── Tokens de esta tanda ────────────────────────────────────────────
   Duraciones SUELTAS (sin curva incluida, como ya hacía --pos-t-cobro) —
   se componen con var(--ease) donde hace falta, y así una hoja puede
   pedir SOLO una duración distinta sin redeclarar toda la transición
   (técnica usada abajo en `.pos-hoja` para la asimetría abre/cierra). */
:root {
  /* Punto 1 del encargo: por debajo de 100ms, el hundimiento de CUALQUIER
     control táctil. La suelta puede ser más lenta (usa --pos-t, 180ms, en
     la mayoría de componentes de abajo) — se hunde de golpe, sale con un
     pelín de gracia, nunca al revés. */
  --pos-t-touch: 90ms;
  /* Punto 4: hojas que suben desde abajo — misma asimetría que ya fija
     §5 SISTEMA_VISUAL_V2.md para paneles superpuestos (120ms de salida,
     180ms de entrada), con sus propios tokens porque viven fuera de
     `.pos-shell` en algunos casos. */
  --pos-t-hoja-abre: 180ms;
  --pos-t-hoja-cierra: 120ms;
  /* Único valor de esta tanda por ENCIMA del techo de 200ms — a propósito:
     no es flujo de cobro, es el destello normativo de §5 SISTEMA_VISUAL_V2
     ("nueva comanda en KDS, nueva mesa pidiendo"), nunca implementado hasta
     ahora. */
  --pos-t-destello: 400ms;
}
@media (prefers-reduced-motion: reduce) {
  :root { --pos-t-touch: 0ms; --pos-t-hoja-abre: 0ms; --pos-t-hoja-cierra: 0ms; --pos-t-destello: 0ms; }
  /* Punto 7: componentes de esta tanda que pueden montarse fuera de
     `.pos-shell` (ver nota de cabecera) — el apagado general de la línea
     ~150 no les llega. */
  .pos-hoja, .pos-hoja *,
  .pos-esqueleto, .pos-esqueleto *,
  .pos-salto-contenido,
  .pos-toast { transition: none !important; animation: none !important; }
}

/* ── 1. RESPUESTA INMEDIATA AL DEDO ──────────────────────────────────
   Generaliza a TODO lo pulsable el patrón que ya usaban `.pos-btn`/
   `.pos-keypad__tecla`: hundimiento (scale) + cambio de superficie/sombra
   A LA VEZ, nunca solo escala (§5: "un scale(.97) de una tarjeta de 130px
   es casi imperceptible"). Los elementos que YA escalaban pero solo
   escalaban (`.pos-btn-producto`, `.pos-chip`, `.pos-persona`, `.pos-mesa`)
   ganan aquí su segunda propiedad; los que no tenían NINGÚN feedback de
   contacto (navegación de categorías/Control, pestañas, filtros, menú ≡,
   barra de modos, segmented control, tarjeta de encargo) lo ganan entero.
   Ninguna línea de aquí toca el color/radio de REPOSO — solo el instante
   del contacto, con `--pos-t-touch` (<100ms) puesto directamente en la
   regla `:active` (mayor especificidad → gobierna la ENTRADA; la SALIDA
   la sigue gobernando la transición base de cada componente, ya existente
   y ya ≤200ms en todos los casos). */
.pos-btn-producto:active { background: var(--pos-card); box-shadow: var(--shadow-2); transition: transform var(--pos-t-touch) var(--ease), background var(--pos-t-touch) var(--ease), box-shadow var(--pos-t-touch) var(--ease); }
.pos-chip:active { background: var(--pos-ground); border-color: var(--mrj-mocha); transition: transform var(--pos-t-touch) var(--ease), background var(--pos-t-touch) var(--ease), border-color var(--pos-t-touch) var(--ease); }
.pos-chip.is-activo:active, .pos-chip.on:active { filter: brightness(1.1); }
.pos-persona:active { background: var(--pos-ground); box-shadow: var(--pos-sombra-1); transition: transform var(--pos-t-touch) var(--ease), background var(--pos-t-touch) var(--ease), box-shadow var(--pos-t-touch) var(--ease); }
.pos-persona.on:active { filter: brightness(1.08); }
.pos-mesa:active { box-shadow: var(--pos-sombra-2); transition: transform var(--pos-t-touch) var(--ease), box-shadow var(--pos-t-touch) var(--ease); }
.pos-encargo:active { box-shadow: var(--pos-sombra-2); transition: box-shadow var(--pos-t-touch) var(--ease); }

/* Navegación/controles sin NINGÚN feedback de contacto hasta ahora. Se
   añade `transform` a la transición base (redeclarada completa, con las
   mismas propiedades que ya tenía cada uno, para no perder su transición
   de salida) y una regla `:active` con la segunda propiedad que cambia. */
.pos-modes button { transition: transform var(--pos-t-touch) var(--ease), background var(--pos-t), color var(--pos-t); }
.pos-modes button:active { transform: scale(.96); background: rgba(252, 236, 215, .14); }
.pos-modes button.on:active { background: var(--accent); filter: brightness(.94); }

.pos-catblock:active { filter: brightness(.94); }

.pos-ctrl .nav button,
.pos-ctrl-nav button { transition: transform var(--pos-t-touch) var(--ease), background var(--pos-t), color var(--pos-t); }
.pos-ctrl .nav button:active,
.pos-ctrl-nav button:active { transform: scale(.98); background: var(--pos-ground); }
.pos-ctrl .nav button.on:active,
.pos-ctrl-nav button.on:active { filter: brightness(.95); }

.pos-tabs span { transition: transform var(--pos-t-touch) var(--ease), color var(--pos-t), box-shadow var(--pos-t); }
.pos-tabs span:active { transform: scale(.96); color: var(--brown); }

.pos-seg button { transition: transform var(--pos-t-touch) var(--ease), background var(--pos-t), color var(--pos-t); }
.pos-seg button:active { transform: scale(.95); background: rgba(76, 44, 28, .08); }
.pos-seg button.on:active { filter: brightness(.94); }

.pos-obr-tabs button { transition: transform var(--pos-t-touch) var(--ease), background var(--pos-t), color var(--pos-t), border-color var(--pos-t); }
.pos-obr-tabs button:active { transform: scale(.96); background: var(--pos-card); }
.pos-obr-tabs button.on:active { filter: brightness(.94); }

.pos-admin__nav-item { transition: transform var(--pos-t-touch) var(--ease), background var(--pos-t); }
.pos-admin__nav-item:active { transform: scale(.98); background: var(--pos-ground); }

.pos-tpv__categoria { transition: transform var(--pos-t-touch) var(--ease), background var(--pos-t), border-color var(--pos-t); }
.pos-tpv__categoria:active { transform: scale(.98); background: var(--pos-ground); }

.pos-filtro { transition: transform var(--pos-t-touch) var(--ease), background var(--pos-t), border-color var(--pos-t), color var(--pos-t); }
.pos-filtro:active { transform: scale(.96); background: var(--mrj-sand); }
.pos-filtro.on:active { filter: brightness(.94); }

.pos-menu-pop button { transition: transform var(--pos-t-touch) var(--ease), background var(--pos-t); }
.pos-menu-pop button:active { transform: scale(.98); background: var(--pos-card); box-shadow: inset 0 0 0 1px var(--pos-line); }

.pos-encargo__f button { transition: transform var(--pos-t-touch) var(--ease), filter var(--pos-t); }
.pos-encargo__f button:active { transform: scale(.95); filter: brightness(.94); }

.pos-btn--caja:active,
.pos-salto-contenido:active { transition-duration: var(--pos-t-touch); } /* se declaran completas más abajo, junto a sus otras reglas */

/* ── 2. TRANSICIÓN ENTRE VISTAS ───────────────────────────────────────
   Cada modo (Mostrador/Mesas/Control/Fichajes…) es una PÁGINA distinta
   (public/tpv, public/admin, public/fichajes…) — "cambiar de vista" es
   siempre una navegación real entre documentos, el terreno exacto de las
   View Transitions nativas del navegador (cross-document): no necesitan
   ni una línea de JS. Si el navegador las soporta, las usa; si no, esta
   regla entera se ignora y la navegación sigue exactamente como hoy —
   esa ES la "comprobación de compatibilidad" del encargo, resuelta en CSS
   puro. Desvanecido + un desplazamiento muy corto, 180ms con la curva de
   siempre — por debajo del techo de 200ms aunque esto no es flujo de
   cobro (nunca lo es: entrar a Cobrar es una hoja del punto 4, no un
   cambio de página). */
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: pos-vista-sale 180ms var(--ease) both; }
::view-transition-new(root) { animation: pos-vista-entra 180ms var(--ease) both; }
@keyframes pos-vista-entra { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
@keyframes pos-vista-sale { from { opacity: 1; transform: translateY(0); } to { opacity: 0; transform: translateY(-6px); } }
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
}

/* ── 3. ESQUELETOS DE CARGA ───────────────────────────────────────────
   Bloques con la forma del contenido que va a llegar en vez de un hueco
   vacío — arena (`--pos-ground`) sobre crema (`--pos-card`), NUNCA blanco
   ni gris, con un brillo de `--mrj-sand` que recorre en diagonal. Formas
   ya recortadas a los tamaños reales del catálogo de componentes (arriba
   en este mismo fichero) para que quien monte el marcado componga con
   ellas en vez de inventar tamaños sueltos por pantalla. */
.pos-esqueleto {
  position: relative;
  overflow: hidden;
  background: var(--pos-ground);
  border-radius: var(--pos-r-control);
}
.pos-esqueleto::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(100deg, transparent 30%, color-mix(in srgb, var(--mrj-sand) 65%, transparent) 50%, transparent 70%);
  background-size: 250% 100%;
  animation: pos-brillo 1.5s ease-in-out infinite;
}
@keyframes pos-brillo { from { background-position: 150% 0; } to { background-position: -100% 0; } }
@media (prefers-reduced-motion: reduce) {
  /* Sin el brillo, el bloque de arena sigue siendo un estado de carga
     honesto (no una interfaz rota) — punto 7 del encargo. */
  .pos-esqueleto::after { animation: none; content: none; }
}
.pos-esqueleto--linea { height: 14px; border-radius: 4px; }
.pos-esqueleto--titulo { height: 22px; width: 55%; border-radius: 6px; }
.pos-esqueleto--avatar { width: 42px; height: 42px; border-radius: 50%; flex-shrink: 0; }
.pos-esqueleto--prod { min-height: 96px; border-radius: var(--r-lg); }
.pos-esqueleto--kpi { min-height: 92px; border-radius: var(--pos-r-card); }
.pos-esqueleto--mesa { width: 128px; height: 108px; border-radius: var(--pos-r-card); }
.pos-esqueleto--fila { height: 56px; border-radius: var(--r-md); }

/* Bonus normativo (§5 SISTEMA_VISUAL_V2.md, nunca implementado hasta hoy):
   "aparición de un nuevo elemento en una lista en vivo" — destello de
   `--pos-info-bg` de 400ms, el ÚNICO motion de toda la capa POS por
   encima del techo de 200ms a propósito (no es flujo de cobro). Clase
   suelta para el nodo recién insertado (nueva comanda en el Obrador,
   nueva mesa "pidiendo"). */
@keyframes pos-destello { from { background-color: var(--pos-info-bg); } }
.pos-destello { animation: pos-destello var(--pos-t-destello) var(--ease); }
@media (prefers-reduced-motion: reduce) { .pos-destello { animation: none; } }

/* ── 4. HOJAS QUE SUBEN DESDE ABAJO ───────────────────────────────────
   Componente nuevo para modificadores, cobro y el PIN de autorización
   (encargo, punto 4). Estructura de 3 partes + raíz:
     <div class="pos-hoja [is-abierta]">
       <div class="pos-hoja__velo"></div>
       <div class="pos-hoja__cuerpo">
         <div class="pos-hoja__asa"></div>
         … contenido …
       </div>
     </div>
   `.pos-hoja` es el envoltorio fijo a toda la pantalla (invisible y sin
   pointer-events mientras está cerrado, para no bloquear nada del fondo
   que no se ve — punto 5 del mismo espíritu). El velo se atenúa con
   `--mrj-ink` con alfa (nunca gris). El cuerpo sube con `translateY` desde
   fuera de la pantalla — entra en 180ms, cierra en 120ms (misma asimetría
   que ya fija §5 para paneles: "cierra más rápido de lo que abre"),
   dentro del techo de 200ms del flujo de cobro cuando se use para cobrar.
   Cierre por arrastre: el marcado añade `.is-arrastrando` mientras el dedo
   mueve el cuerpo (JS fija un `transform` en línea) — esta clase apaga la
   transición para que no compita con el gesto; al soltar, se quita y la
   transición vuelve a gobernar el asentamiento final. El asa es
   `position:sticky` dentro del propio scroll del cuerpo, así nunca se va
   con el contenido aunque la hoja sea larga. */
.pos-hoja {
  position: fixed;
  inset: 0;
  z-index: 20;
  pointer-events: none;
}
.pos-hoja.is-abierta { pointer-events: auto; }
.pos-hoja__velo {
  position: absolute;
  inset: 0;
  background: rgba(19, 7, 2, .45);
  opacity: 0;
  transition: opacity var(--pos-t-hoja-cierra) var(--ease);
}
.pos-hoja.is-abierta .pos-hoja__velo { opacity: 1; transition-duration: var(--pos-t-hoja-abre); }
.pos-hoja__cuerpo {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  max-height: 88vh;
  overflow-y: auto;
  background: var(--pos-card);
  border-radius: var(--pos-r-panel) var(--pos-r-panel) 0 0;
  box-shadow: var(--pos-sombra-2);
  display: flex;
  flex-direction: column;
  padding-bottom: env(safe-area-inset-bottom, 0px);
  transform: translateY(100%);
  transition: transform var(--pos-t-hoja-cierra) var(--ease);
}
.pos-hoja.is-abierta .pos-hoja__cuerpo { transform: translateY(0); transition-duration: var(--pos-t-hoja-abre); }
.pos-hoja.is-arrastrando .pos-hoja__cuerpo { transition: none; }
.pos-hoja__asa {
  position: sticky;
  top: 0;
  flex-shrink: 0;
  width: 44px;
  height: 5px;
  border-radius: 999px;
  background: var(--mrj-sand);
  margin: 10px auto 2px;
}

/* `.pos-autorizar` compone con `.pos-hoja` (`<div class="pos-hoja
   pos-autorizar is-abierta">`) — reutiliza el teclado/puntos de PIN que ya
   existen (`.pos-keypad`, `.pos-pin-puntos`) dentro de `.pos-hoja__cuerpo`,
   solo centra y da título/error propios. Es la hoja que una encargada
   usa para autorizar su PIN antes de entrar en Control. */
.pos-autorizar .pos-hoja__cuerpo { align-items: center; padding: 4px 24px calc(28px + env(safe-area-inset-bottom, 0px)); }
.pos-autorizar__titulo { font-family: var(--pos-font-display); font-weight: 400; font-size: 19px; color: var(--brown); margin: 6px 0 18px; text-align: center; }
.pos-autorizar__error { color: var(--bad-text); font-size: var(--fs-sm, 13px); min-height: 1.2em; margin-top: 10px; text-align: center; }

/* ── El botón de "abrir cajón" de la barra superior (texto, sin icono) ──
   Vive en `.pos-head`, junto a `.pos-head .lnk` — mismo tratamiento visual
   (borde cream translúcido) para no introducir un color nuevo. */
.pos-btn--caja {
  min-height: var(--pos-tap-min);
  padding: 0 var(--sp-3, 12px);
  border-radius: var(--pos-r-control);
  border: 1.5px solid rgba(252, 236, 215, .28);
  background: transparent;
  color: var(--mrj-cream);
  font-family: var(--font-body);
  font-weight: 700;
  font-size: var(--fs-sm, 13px);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2, 6px);
  cursor: pointer;
  transition: transform var(--pos-t-touch) var(--ease), background var(--pos-t), border-color var(--pos-t);
}
.pos-btn--caja:active { transform: scale(.95); background: rgba(252, 236, 215, .16); }
.pos-btn--caja:disabled { opacity: .45; cursor: not-allowed; }
@media (hover: hover) and (pointer: fine) {
  .pos-btn--caja:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
}

/* ── 5. AVISOS QUE NO BLOQUEAN ─────────────────────────────────────────
   `.pos-toast` ya es `position:fixed` (nunca empuja nada) y ya se cierra
   solo (temporizador existente en pos-ui.js, sin tocar). Lo único que
   faltaba: que ENTRE deslizado desde abajo en vez de aparecer de golpe.
   `@starting-style` anima la PRIMERA vez que el navegador pinta el
   elemento ya visible (el toast deja de tener `hidden`) — cero cambios en
   el JS que lo muestra. Se usa la propiedad `translate` (independiente de
   `transform`, que el toast ya usa para el centrado horizontal) para no
   pisar `translateX(-50%)`. */
@starting-style {
  .pos-toast:not([hidden]) { opacity: 0; translate: 0 16px; }
}
.pos-toast { translate: 0 0; }

/* ── 6. FOCO Y SALTO AL CONTENIDO ──────────────────────────────────────
   El anillo de foco de 3px ya existe y cumple (línea ~306). Lo que
   faltaba: un enlace de "saltar al contenido", primer elemento tabulable
   de cada página — invisible hasta que se navega con teclado, apunta al
   id del contenedor principal de esa pantalla (`#tpv-cuerpo`, `.pos-ctrl
   .main`…, lo decide quien monte el marcado de cada una). */
.pos-salto-contenido {
  position: fixed;
  top: 10px;
  left: 10px;
  z-index: 200;
  background: var(--brown);
  color: var(--mrj-cream);
  padding: 10px 16px;
  border-radius: var(--pos-r-control);
  font-weight: 700;
  font-size: var(--fs-sm, 13px);
  text-decoration: none;
  transform: translateY(-150%);
  /* `--pos-t` YA es `180ms var(--ease)` (duración + curva juntas, línea
     ~79) — un `var(--ease)` suelto detrás duplicaría la curva e invalidaría
     la propiedad entera (el mismo defecto corregido arriba en
     `.pos-btn-producto`/`.pos-keypad__tecla`, verificado en navegador). */
  transition: transform var(--pos-t);
}
.pos-salto-contenido:focus-visible {
  transform: translateY(0);
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

/* ── 8. RENDIMIENTO DEL PINTADO ────────────────────────────────────────
   Auditoría de TODO el fichero (grep de `transition:`/`animation:` contra
   width/height/top/left/margin, antes de esta tanda): cero coincidencias
   — no había ninguna animación que obligara a recalcular la maquetación,
   y todo lo añadido arriba usa transform/opacity/translate/background/
   box-shadow/filter/color/border-color, nunca una propiedad de layout.
   `contain` aísla las CUATRO zonas de más alta frecuencia de cambio de
   todo el POS (rejilla de productos en cada venta, plano de mesas,
   columnas del Obrador en vivo, parrilla de fichajes) para que un cambio
   ahí dentro no dispare un recálculo del resto de la página — sin abusar,
   solo estos cuatro sitios. */
.pos-grid,
.pos-zone .tables,
.pos-kcols,
.pos-fgrid {
  contain: layout paint;
}


/* ── Modo de edición de disponibilidad (14/09) ────────────────────────────
   El botón de agotar/reponer vivía dentro de cada tarjeta de producto, todo
   el rato. Con el catálogo real eso llena la rejilla de botones y rompe la
   retícula de la maqueta aprobada. Ahora se enciende un modo desde la
   cabecera de la rejilla, y solo entonces aparecen los botones. */
.pos-ctx { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3, 12px); }
.pos-ctx__acciones { display: flex; align-items: center; gap: var(--sp-2, 8px); flex-shrink: 0; }

/* Variante menor del botón: para acciones de cabecera que no deben pesar
   tanto como una acción principal. Mantiene el mínimo táctil de 48px. */
.pos-btn--menor {
  font-family: var(--font-body);
  font-weight: 700;
  font-size: var(--fs-sm, 13px);
  padding: 0 var(--sp-3, 14px);
  min-height: var(--pos-tap-min);
}
.pos-btn--menor.on { background: var(--accent); border-color: var(--accent); color: var(--brown); }

/* Mientras se edita, la rejilla se separa un poco más para que el botón de
   cada tarjeta respire, y las tarjetas avisan de que están en modo edición. */
.pos-grid.is-editando { gap: var(--sp-3, 12px); }
.pos-grid.is-editando .pos-prod { border-color: var(--accent); }
.pos-grid.is-editando .pos-prod .pos-btn--fantasma {
  margin-top: var(--sp-2, 8px);
  width: 100%;
  font-size: var(--fs-xs, 12px);
  min-height: 40px;
}

/* ═══════════════════════════════════════════════════════════════════════
   CICLO DE CAJA — encargo de Joni, 2026-09-14 (public/tpv/tpv.js: la hoja
   `.pos-hoja.pos-caja` y la hoja genérica de PIN de encargada que reutiliza
   `.pos-autorizar`, sin tocar ese fichero). Contrato visual: SOLO tokens ya
   declarados arriba y en el design system del OS — ni un color, ni una
   forma, ni una sombra nuevos. `.pos-caja` compone con `.pos-hoja` igual
   que `.pos-autorizar` (misma estructura de 4 partes) pero SIN el
   `align-items:center` de esa — aquí el contenido es una lista de botones y
   campos a lo ancho, no un teclado de PIN centrado.
   ═══════════════════════════════════════════════════════════════════════ */
.pos-caja .pos-hoja__cuerpo { padding: 4px 20px calc(24px + env(safe-area-inset-bottom, 0px)); }
.pos-caja__contenido { display: flex; flex-direction: column; gap: var(--sp-2, 8px); max-width: 420px; margin: 0 auto; width: 100%; }
.pos-caja__titulo { font-family: var(--pos-font-display); font-weight: 400; font-size: 19px; color: var(--brown); margin: 6px 0 4px; }

/* Estado igual a color pastel + palabra (rasgo 5, SISTEMA_VISUAL_V2 §12) —
   nunca un color a secas. Cerrada = neutro hundido; abierta = pastel OK. */
.pos-caja__estado {
  margin: 0 0 4px;
  padding: 10px 14px;
  border-radius: var(--pos-r-control);
  font-size: var(--fs-sm, 13.5px);
  line-height: 1.5;
}
.pos-caja__estado--cerrada { background: var(--pos-ground); color: var(--mrj-mocha); }
.pos-caja__estado--abierta { background: var(--pos-ok-bg); color: var(--ok-text); }
.pos-caja__aviso {
  margin: 0 0 4px;
  padding: 10px 14px;
  border-radius: var(--pos-r-control);
  background: var(--pos-warn-bg);
  color: var(--warn-text);
  font-size: var(--fs-sm, 13.5px);
  line-height: 1.5;
  font-weight: 700;
}
.pos-caja__nota { margin: 0 0 4px; font-size: var(--fs-sm, 13px); color: var(--mrj-mocha); line-height: 1.5; }
.pos-caja__nota--menor { font-size: var(--fs-xs, 11.5px); }
.pos-caja__error { color: var(--bad-text); font-size: var(--fs-sm, 13px); min-height: 1.2em; margin: 2px 0 0; }

/* Botones de la hoja: a lo ancho (`.pos-btn--ancho` ya existe pero limita a
   320px — aquí conviene todo el ancho del panel, 420px). Se redeclara solo
   `max-width` para no tocar el resto de sitios donde ya se usa esa clase. */
.pos-caja .pos-btn--ancho { max-width: none; margin-top: 6px; }
.pos-caja__secundarias { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-2, 8px); margin-top: 6px; }
.pos-caja__secundarias .pos-btn { margin-top: 0; }

/* ── T8: hojas de cobro por separado / descuento de mesa / mover-unir ────
   Todas comparten el chasis `.pos-hoja.pos-caja` — solo añaden estos
   componentes menores propios. ──────────────────────────────────────── */
.pos-parcial-lineas { display: flex; flex-direction: column; gap: 6px; max-height: 40vh; overflow: auto; }
.pos-parcial-linea {
  display: flex; align-items: center; gap: var(--sp-3, 10px);
  min-height: var(--pos-tap-min); padding: 0 var(--sp-3, 10px);
  border: 1px solid var(--pos-line); border-radius: var(--r-md);
  background: var(--pos-ground); cursor: pointer;
}
.pos-parcial-linea.on { border-color: var(--accent); background: var(--pos-info-bg); }
.pos-parcial-linea input { width: 20px; height: 20px; flex-shrink: 0; accent-color: var(--accent); }
.pos-parcial-linea .n { flex: 1; font-weight: 700; }
.pos-parcial-linea .p { font-family: var(--pos-font-display); font-weight: 400; font-variant-numeric: tabular-nums; }
.pos-caja-stepper { display: flex; align-items: center; justify-content: center; gap: var(--sp-4, 16px); margin: 6px 0; }
.pos-caja-stepper button { min-width: var(--pos-tap-min); min-height: var(--pos-tap-min); font-size: 20px; font-weight: 900; }
.pos-caja-stepper .n { font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo); font-size: 28px; color: var(--brown); min-width: 32px; text-align: center; }

/* ── Desglose de monedas y billetes (REQ-CAJA-002/008) ───────────────── */
.pos-caja-denoms {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  margin: 8px 0;
  max-height: 46vh;
  overflow-y: auto;
  padding-right: 2px;
}
.pos-caja-denom {
  display: grid;
  grid-template-columns: 44px 1fr 64px;
  align-items: center;
  gap: 8px;
  min-height: var(--pos-tap-min);
  padding: 4px 8px;
  border-radius: var(--pos-r-control);
  background: var(--pos-ground);
}
.pos-caja-denom .et { font-weight: 700; font-size: var(--fs-sm, 13px); color: var(--brown); }
.pos-caja-denom input {
  width: 100%;
  /* Integrador T5: era 40px, por debajo del objetivo táctil mínimo de la
     marca (--pos-tap-min, 48px, §11/§12 SISTEMA_VISUAL_V2) — este input es
     el que cuenta cada denominación al abrir/cerrar caja, se toca de
     verdad en el terminal. */
  min-height: var(--pos-tap-min);
  border: 1px solid var(--pos-line);
  border-radius: var(--r-md, 10px);
  background: var(--pos-card);
  color: var(--brown);
  font-family: var(--pos-font-display);
  font-weight: 400;
  font-size: 16px;
  text-align: center;
  padding: 0 4px;
}
.pos-caja-denom input:focus-visible { outline: 3px solid var(--accent); outline-offset: 1px; }
.pos-caja-denom .sub { font-size: 11px; color: var(--mrj-mocha); text-align: right; }
.pos-caja-total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding: 12px 14px;
  border-radius: var(--pos-r-control);
  background: var(--pos-card);
  border: 1px solid var(--pos-line);
  margin: 4px 0 8px;
}
.pos-caja-total span { font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--mrj-mocha); }
.pos-caja-total strong { font-family: var(--pos-font-display); font-weight: 400; font-size: 26px; color: var(--brown); }

/* ── Campos de texto (importe/motivo) — reutiliza `.pos-campo` ya
   existente (línea ~580); aquí se anula el `text-align:center`/`max-width`
   pensados para el login, y se añade la variante `textarea`. ──────────── */
.pos-caja-campo { display: flex; flex-direction: column; gap: 4px; margin: 4px 0; }
.pos-caja-campo span { font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--mrj-mocha); }
.pos-caja-campo .pos-campo { max-width: none; text-align: left; }
.pos-campo--textarea { min-height: 72px; padding: 10px 12px; text-align: left; resize: vertical; font-family: var(--font-body); font-size: var(--fs-sm, 14px); line-height: 1.4; }

/* ── Resultado del arqueo (rasgo 5: pastel + palabra, nunca un signo) ──── */
.pos-caja-resultado {
  border-radius: var(--pos-r-card);
  padding: 16px;
  margin: 6px 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
}
.pos-caja-resultado--ok { background: var(--pos-ok-bg); }
.pos-caja-resultado--warn { background: var(--pos-warn-bg); }
.pos-caja-resultado--bad { background: var(--pos-bad-bg); }
.pos-caja-resultado .pos-badge { font-size: 13px; padding: 0 12px; height: 26px; }
.pos-caja-resultado .filas { display: flex; gap: 24px; }
.pos-caja-resultado .filas > div { display: flex; flex-direction: column; gap: 2px; }
.pos-caja-resultado .filas span { font-size: 10px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--mrj-mocha); }
.pos-caja-resultado .filas b { font-family: var(--pos-font-display); font-weight: 400; font-size: 20px; color: var(--brown); }

/* ── Aviso de caja sin arquear ("como Qamarero", punto 5) — banner fijo
   igual que `.pos-banner-offline` pero SIEMPRE en tono aviso (pastel warn):
   es un recordatorio, nunca un error. Vive fuera de `.pos-shell` (se
   inserta como primer hijo de <body> desde tpv.js), así que se cubre a
   mano en el bloque de `prefers-reduced-motion` de más arriba y en la
   comprobación de "otro diálogo abierto" del bloqueo por inactividad
   (ya cubierta: mira cualquier `.pos-hoja.is-abierta`, esto no lo es). ─── */
.pos-banner-caja {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  flex-wrap: wrap;
  width: 100%;
  min-height: 40px;
  padding: 8px 16px;
  background: var(--pos-warn-bg);
  color: var(--warn-text);
  font-size: var(--fs-sm, 13.5px);
  font-weight: 700;
  text-align: center;
}
.pos-banner-caja__boton {
  border: 1.5px solid var(--warn-text);
  border-radius: var(--pos-r-control);
  padding: 6px 14px;
  min-height: 36px;
  font-weight: 700;
  font-size: var(--fs-sm, 13px);
  color: var(--warn-text);
  background: transparent;
  transition: background var(--pos-t), transform var(--pos-t-touch) var(--ease);
}
.pos-banner-caja__boton:active { transform: scale(.96); background: rgba(154, 98, 6, .12); }

/* ── Motivos de un toque en los movimientos de caja (14/09) ───────────────
   La verificación del turno completo señaló esto como el punto más torpe de
   todo el recorrido: la empleada tenía que escribir la razón entera a mano en
   un teclado táctil, con prisa y las manos ocupadas. Ahora los cuatro motivos
   reales se ponen de un toque y el campo escrito sigue debajo para matizar. */
.pos-motivos {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2, 8px);
  margin-top: var(--sp-2, 6px);
}
.pos-motivos .pos-chip {
  font-size: var(--fs-sm, 13px);
  min-height: var(--pos-tap-min);
}
.pos-caja-campo__ayuda {
  font-weight: 400;
  color: var(--mrj-mocha);
}

/* El desglose de monedas es más alto que la hoja y nada avisaba de que había
   más filas debajo: los billetes de 100 y 200 quedaban fuera de la vista y
   parecía que no existían. Una sombra interior en el borde inferior insinúa
   que la lista continúa, y desaparece al llegar al final. */
.pos-caja__contenido { overflow-y: auto; }
/* La altura y el scroll ya los fija la regla de arriba: aquí solo se añade la
   pista visual. El primer par de capas pinta el "suelo" del área visible y el
   segundo la sombra, que solo asoma mientras quede contenido por debajo. */
.pos-caja-denoms {
  background:
    linear-gradient(var(--pos-card) 30%, transparent) top / 100% 24px no-repeat local,
    linear-gradient(transparent, var(--pos-card) 70%) bottom / 100% 24px no-repeat local,
    radial-gradient(farthest-side at 50% 0, rgba(19, 7, 2, .10), transparent) top / 100% 10px no-repeat scroll,
    radial-gradient(farthest-side at 50% 100%, rgba(19, 7, 2, .10), transparent) bottom / 100% 10px no-repeat scroll;
}

/* ── Turno sin arquear (14/09) ────────────────────────────────────────────
   Un turno que se cerró solo a medianoche sin que nadie contara el dinero.
   Antes de construir el arqueo tardío, ese dinero quedaba fuera de todo
   control para siempre. Ahora se puede contar, pero solo sirve si quien abre
   por la mañana lo ve: por eso este aviso pesa más que el resto y va el
   primero de la hoja. */
.pos-caja__pendiente {
  border: 2px solid var(--accent);
  border-radius: var(--pos-r-card, 18px);
  padding: var(--sp-3, 14px);
  margin-bottom: var(--sp-3, 14px);
  background: var(--pos-warn-bg);
}
.pos-caja__aviso--fuerte {
  font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo);
  font-size: var(--fs-md, 15px);
  line-height: 1.45;
  color: var(--brown);
  margin: 0 0 var(--sp-3, 12px);
}

/* NOTA: las reglas `.pos-caja-denoms` y `.pos-caja-denom` de más arriba ya no
   se usan — ese marcado se sustituyó por `.pos-cuenta` el 14/09. Se dejan sin
   borrar por la regla del proyecto de no eliminar nada, pero son CSS muerto:
   si alguien las toca buscando cambiar el contador, no verá ningún efecto. */

/* ── Ventana de contar caja (14/09) ───────────────────────────────────────
   Contar caja no es teclear catorce números: es coger un puñado de monedas de
   veinte céntimos, contarlas y apuntar cuántas hay. Dos columnas, monedas y
   billetes, para que quepa entero sin deslizar; botones del tamaño de un dedo
   en vez del teclado del sistema; el subtotal aparece solo donde hay algo, así
   el ojo va directo a lo que ya está contado; y el total no se mueve de abajo. */
.pos-cuenta {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-3, 14px) var(--sp-4, 18px);
  align-items: start;
}
.pos-cuenta-grupo { display: flex; flex-direction: column; gap: var(--sp-2, 6px); }
.pos-cuenta-grupo .pos-cgrp { margin: 0 0 var(--sp-1, 4px); }

.pos-cuenta-fila {
  display: grid;
  grid-template-columns: 54px var(--pos-tap-min) 56px var(--pos-tap-min) 1fr;
  align-items: center;
  gap: var(--sp-2, 6px);
  padding: 3px 6px;
  border-radius: var(--pos-r-control, 12px);
  border: 1px solid transparent;
}
/* Una fila con algo contado se separa del resto: en un vistazo se ve por dónde
   vas sin tener que leer los números uno a uno. */
.pos-cuenta-fila.tiene { background: var(--pos-card); border-color: var(--pos-line); }
.pos-cuenta-fila .et { font-weight: 700; font-size: var(--fs-sm, 13px); }
.pos-cuenta-fila input {
  width: 100%;
  min-height: var(--pos-tap-min);
  text-align: center;
  font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo);
  font-size: var(--fs-md, 16px);
  border-radius: var(--pos-r-control, 12px);
  border: 1px solid var(--pos-line);
  background: var(--pos-ground);
  color: var(--brown);
  /* Las flechitas del navegador son diminutas y no se aciertan con el dedo:
     para eso están los botones de al lado. */
  appearance: textfield;
  -moz-appearance: textfield;
}
.pos-cuenta-fila input::-webkit-outer-spin-button,
.pos-cuenta-fila input::-webkit-inner-spin-button { appearance: none; margin: 0; }
.pos-cuenta-fila .sub {
  text-align: right;
  font-size: var(--fs-sm, 13px);
  font-weight: 700;
  color: var(--mrj-mocha);
  font-variant-numeric: tabular-nums;
}

.pos-cuenta-btn {
  min-width: var(--pos-tap-min);
  min-height: var(--pos-tap-min);
  border-radius: var(--pos-r-control, 12px);
  border: 1px solid var(--pos-line);
  background: var(--pos-card);
  color: var(--brown);
  font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  transition: transform var(--pos-t-touch, 90ms) var(--ease), background var(--pos-t-touch, 90ms) var(--ease);
}
.pos-cuenta-btn:active { transform: scale(.94); background: var(--mrj-sand); }
.pos-cuenta-btn:disabled { opacity: .35; cursor: default; }

/* El total se queda pegado abajo mientras se cuenta: es el número que importa
   y no debe haber que buscarlo. */
.pos-caja-total {
  position: sticky;
  bottom: 0;
  display: flex;
  align-items: center;
  gap: var(--sp-3, 12px);
  margin-top: var(--sp-3, 14px);
  padding: var(--sp-3, 12px) var(--sp-3, 14px);
  border-top: 1px solid var(--pos-line);
  background: var(--pos-card);
  border-radius: var(--pos-r-card, 18px);
}
.pos-caja-total > span {
  font-size: var(--fs-xs, 11px);
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--mrj-mocha);
}
.pos-caja-total > strong {
  margin-left: auto;
  font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo);
  font-size: 30px;
  font-variant-numeric: tabular-nums;
}
.pos-caja-total .pos-btn--menor { flex-shrink: 0; }

/* En un móvil de comandero no caben dos columnas: pasan a una. */
@media (max-width: 640px) {
  .pos-cuenta { grid-template-columns: 1fr; }
}

/* ── Mermas al cierre (encargo 15/09/2026) — vive dentro de `.pos-caja`,
   reutiliza tokens/clases ya existentes (`.pos-caja-campo`, `.pos-chip`,
   `.pos-cuenta-fila`, `.pos-cuenta-btn`, `.pos-cgrp`): contrato visual v5,
   fondo arena/tarjeta crema, sin gris ni blanco puro, sin sombra negra,
   Paytone One solo en la cifra de cantidad, un único naranja por vista
   (el acento ya en uso por `.pos-btn--primario`/`.pos-chip.on`). ─────────*/
.pos-merma-resultados {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 220px;
  overflow-y: auto;
}
.pos-merma-resultado {
  min-height: var(--pos-tap-min);
  text-align: left;
  padding: 10px 14px;
  border-radius: var(--pos-r-control, 12px);
  border: 1px solid var(--pos-line);
  background: var(--pos-card);
  color: var(--brown);
  font-size: var(--fs-sm, 13px);
  font-weight: 700;
  cursor: pointer;
  transition: background var(--pos-t-touch, 90ms) var(--ease), border-color var(--pos-t-touch, 90ms) var(--ease);
}
.pos-merma-resultado:active { transform: scale(.98); background: var(--mrj-sand); }
.pos-merma-resultado:hover { border-color: var(--mocha, var(--brown)); }

.pos-merma-elegido {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2, 8px);
  padding: var(--sp-3, 12px);
  border-radius: var(--pos-r-card, 18px);
  border: 1px solid var(--pos-line);
  background: var(--pos-card);
}
.pos-merma-elegido__nombre {
  margin: 0;
  font-family: var(--pos-font-display);
  font-weight: 400;
  font-size: 17px;
  color: var(--brown);
}
.pos-merma-elegido .pos-cuenta-fila .pos-num {
  min-width: 40px;
  text-align: center;
  font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo);
  font-size: 20px;
  color: var(--brown);
}

.pos-merma-lista {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.pos-merma-linea {
  display: flex;
  align-items: center;
  gap: var(--sp-2, 8px);
  padding: 10px 12px;
  border-radius: var(--pos-r-control, 12px);
  background: var(--pos-card);
  border: 1px solid var(--pos-line);
}
.pos-merma-linea__info {
  flex: 1;
  font-size: var(--fs-sm, 13px);
  color: var(--brown);
  line-height: 1.4;
}
.pos-merma-linea__info i { color: var(--mrj-mocha); font-style: normal; }
.pos-merma-linea__quitar {
  flex-shrink: 0;
  min-width: var(--pos-tap-min);
  min-height: var(--pos-tap-min);
  border: 1px solid var(--pos-line);
  border-radius: var(--pos-r-control, 12px);
  background: transparent;
  color: var(--brown);
  font-size: var(--fs-sm, 13px);
  font-weight: 700;
  cursor: pointer;
}
.pos-merma-linea__quitar:active { transform: scale(.94); background: var(--mrj-sand); }

/* ═══════════════════════════════════════════════════════════════════════
   V3 · CHASIS DE LA PANTALLA DE VENTA
   ───────────────────────────────────────────────────────────────────────
   Lo que se copia de las referencias, y por qué:

   · **Lienzo y tarjetas.** Antes todo era del mismo crema y las zonas se
     distinguían por un borde. En las seis referencias hay tres niveles —
     lienzo, tarjeta, elemento hundido— y eso es lo que hace que una
     pantalla llena de datos se lea sin esfuerzo. El crema de LaMaruja pasa
     a ser el LIENZO (el fondo de alrededor) y las zonas de trabajo son
     tarjetas claras encima.

   · **Los filtros son chips, no cajones.** El bloque de categoría ocupaba
     una franja enorme de color saturado a lo ancho de la pantalla: tres
     colorones compitiendo con el producto, que es lo que de verdad hay que
     mirar. En las referencias el filtro es una fila de píldoras discretas
     donde SOLO la activa lleva color. El color fuerte se guarda para lo que
     importa: el precio y el botón de cobrar.
   ═══════════════════════════════════════════════════════════════════════ */
.pos-tpv-body { background: var(--pos-v3-lienzo); gap: 12px; padding: 12px; }

.pos-prods,
.pos-ticket {
  background: var(--pos-v3-carta);
  border: 1px solid var(--pos-v3-linea);
  border-radius: var(--pos-v3-r-carta);
  box-shadow: var(--pos-v3-sombra-1);
}

/* Cabecera de la zona de producto: título grande con su contexto debajo,
   como el "Point of Sale / Dashboard • Pos" de la referencia. */
.pos-ctx h2 { font-size: 22px; font-weight: 800; letter-spacing: -.02em; color: var(--pos-v3-texto); }

/* ── Filtros de categoría como chips ──────────────────────────────────── */
.pos-catbar__bloques { gap: 6px; }
.pos-catblock {
  min-height: 40px;
  flex: 0 0 auto;
  border-radius: 999px;
  padding: 7px 14px;
  flex-direction: row;
  align-items: center;
  gap: 7px;
  background: var(--pos-v3-hundido);
  border: 1px solid var(--pos-v3-linea);
  color: var(--pos-v3-texto);
  box-shadow: none;
  font-weight: 700;
}
.pos-catblock .nm { font-size: 14px; font-weight: 700; }
/* El contador va en su propia burbuja, como los "8 Table" de la referencia */
.pos-catblock .n {
  font-size: 11.5px;
  font-weight: 800;
  opacity: 1;
  background: rgba(76, 44, 28, .08);
  color: var(--pos-v3-suave);
  border-radius: 999px;
  padding: 1px 7px;
  min-width: 22px;
  text-align: center;
}
/* El tinte de categoría no pinta el chip entero: es un punto de color a la
   izquierda. Se conserva la información (cada bloque tiene su color) sin
   que tres franjas saturadas se coman la pantalla. */
.pos-catblock::before {
  content: ''; width: 8px; height: 8px; border-radius: 999px; flex: 0 0 auto;
  background: var(--cat-control);
}
.pos-catblock--tinte-op::before { background: var(--cat-op); }
.pos-catblock--tinte-marca::before { background: var(--cat-marca); }
.pos-catblock--tinte-b2b::before { background: var(--cat-b2b); }
.pos-catblock--tinte-control::before { background: var(--cat-control); }
.pos-catblock--tinte-finanzas::before { background: var(--cat-finanzas); }
/* Los tintes ya no rellenan el chip */
.pos-catblock--tinte-op,
.pos-catblock--tinte-marca,
.pos-catblock--tinte-b2b,
.pos-catblock--tinte-control,
.pos-catblock--tinte-finanzas { background: var(--pos-v3-hundido); color: var(--pos-v3-texto); }
/* Activo: relleno de acento, como la píldora activa de las seis referencias */
.pos-catblock.on,
.pos-catblock--tinte-op.on,
.pos-catblock--tinte-marca.on,
.pos-catblock--tinte-b2b.on,
.pos-catblock--tinte-control.on,
.pos-catblock--tinte-finanzas.on {
  background: var(--mrj-orange);
  border-color: var(--mrj-orange);
  color: var(--mrj-ink);
  box-shadow: 0 4px 12px -4px rgba(245, 152, 0, .55);
}
.pos-catblock.on .n { background: rgba(19, 7, 2, .14); color: var(--mrj-ink); }
.pos-catblock.on::before { background: var(--mrj-ink); }

/* ── Tarjeta de producto ──────────────────────────────────────────────── */
/* Blanca sobre la tarjeta, con la franja de categoría arriba y el precio en
   el acento — el patrón exacto de RestroBit/Croizan/D.CC. */
.pos-prod {
  background: var(--pos-v3-carta);
  border: 1px solid var(--pos-v3-linea);
  border-radius: var(--pos-v3-r-control);
  box-shadow: none;
  transition: transform 120ms var(--ease), box-shadow 160ms var(--ease), border-color 160ms var(--ease);
}
.pos-prod:hover { box-shadow: var(--pos-v3-sombra-2); border-color: var(--pos-v3-linea-fuerte); }
.pos-prod:active { transform: scale(.97); }
.pos-prod--sinfoto { background: var(--pos-v3-carta); }
.pos-prod .n { color: var(--pos-v3-texto); font-weight: 700; }
.pos-prod .v { color: var(--pos-v3-suave); font-weight: 600; }
/* El precio en el color de acento: es lo que se busca con la vista */
.pos-prod .p { color: var(--mrj-orange); font-weight: 800; letter-spacing: -.01em; }

/* ── Panel del ticket ─────────────────────────────────────────────────── */
.pos-ticket h2 { font-size: 20px; font-weight: 800; letter-spacing: -.02em; }
/* El total, enorme, como el "Total $4500" de la referencia */
.pos-tfoot { background: var(--pos-v3-hundido); border-top: 1px solid var(--pos-v3-linea); }

/* La franja de color de cada tarjeta sobraba: con el filtro activo ya se
   sabe en qué categoría estás, y en las referencias el único color de la
   tarjeta es el precio. Quitarla deja respirar la rejilla y se ganan 4px
   de alto por tarjeta. */
.pos-prod[data-tinte]::before { display: none; }

/* Campos: hundidos dentro de la tarjeta, sin borde marcado — el buscador de
   las referencias es un rectángulo gris claro con el icono dentro. */
.pos-prods .pos-campo,
.pos-catbar__buscar .pos-campo {
  background: var(--pos-v3-hundido);
  border: 1px solid var(--pos-v3-linea);
  border-radius: var(--pos-v3-r-control);
}
.pos-prods .pos-campo:focus,
.pos-catbar__buscar .pos-campo:focus {
  background: var(--pos-v3-carta);
  border-color: var(--mrj-orange);
}

/* Aire entre el título de la sección y la rejilla */
.pos-ctx { margin-bottom: var(--sp-4, 16px); }
.pos-catbar { margin-bottom: var(--sp-4, 16px); }

/* Botones secundarios de la cabecera de zona (Editar disponibilidad): en las
   referencias son botones blancos con borde fino, nunca cajas crema. */
.pos-prods .pos-btn--fantasma,
.pos-ctx .pos-btn {
  background: var(--pos-v3-carta);
  border: 1px solid var(--pos-v3-linea-fuerte);
  color: var(--pos-v3-texto);
  font-weight: 700;
  border-radius: var(--pos-v3-r-control);
}

/* ── V3 · Chips ───────────────────────────────────────────────────────── */
/* Una sola forma de "opción que se toca" en toda la app: fondo hundido,
   borde fino, y relleno de acento cuando está elegida. Es el patrón de las
   seis referencias y sustituye a los tres estilos distintos que había. */
.pos-chip {
  background: var(--pos-v3-hundido);
  border: 1px solid var(--pos-v3-linea);
  color: var(--pos-v3-texto);
  font-weight: 700;
  border-radius: 999px;
  transition: background 140ms var(--ease), border-color 140ms var(--ease), transform 120ms var(--ease);
}
.pos-chip:hover { border-color: var(--pos-v3-linea-fuerte); }
.pos-chip:active { transform: scale(.97); }
.pos-chip.on {
  background: var(--mrj-orange);
  border-color: var(--mrj-orange);
  color: var(--mrj-ink);
  box-shadow: 0 4px 12px -4px rgba(245, 152, 0, .5);
}
.pos-chip .pos-num { font-weight: 800; }
.pos-chip.on .pos-num { color: var(--mrj-ink); }

/* ── V3 · La ventana de elección ──────────────────────────────────────── */
.pos-eleccion__caja {
  background: var(--pos-v3-carta);
  border: 1px solid var(--pos-v3-linea);
  border-radius: var(--pos-v3-r-lienzo);
  box-shadow: var(--pos-v3-sombra-3);
}
.pos-eleccion__cab { border-bottom: 1px solid var(--pos-v3-linea); padding: 18px 20px; }
.pos-eleccion__cab h3 { font-size: 21px; font-weight: 800; letter-spacing: -.02em; color: var(--pos-v3-texto); }
.pos-eleccion__sub { color: var(--pos-v3-suave); }
.pos-eleccion__cuerpo { padding: 20px; gap: 20px; }
/* El rótulo de cada grupo, en versalitas pequeñas — como "ORDER ITEMS" o
   "Spice Level" de las referencias. */
.pos-eleccion__grupo .pos-cgrp {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--pos-v3-suave);
  margin: 0;
}
.pos-eleccion__pie { background: var(--pos-v3-hundido); border-top: 1px solid var(--pos-v3-linea); padding: 14px 20px; }
.pos-eleccion__falta { color: var(--pos-v3-suave); }

/* ── V3 · Botón principal ─────────────────────────────────────────────── */
/* Barra ancha de acento con el importe dentro: «Añadir · 78,00 €»,
   «Cobrar · 78,00 €». Es el remate de todas las referencias y lo que hace
   que nunca haya duda de cuál es el siguiente paso. */
.pos-btn--primario {
  background: var(--mrj-orange);
  color: var(--mrj-ink);
  border: 0;
  font-weight: 800;
  letter-spacing: -.01em;
  border-radius: var(--pos-v3-r-control);
  box-shadow: 0 6px 16px -6px rgba(245, 152, 0, .6);
  transition: transform 120ms var(--ease), box-shadow 160ms var(--ease), filter 160ms var(--ease);
}
.pos-btn--primario:hover { filter: brightness(1.04); box-shadow: 0 10px 22px -8px rgba(245, 152, 0, .7); }
.pos-btn--primario:active { transform: translateY(1px) scale(.995); }
.pos-btn--primario:disabled { background: var(--pos-v3-hundido); color: var(--pos-v3-suave); box-shadow: none; border: 1px solid var(--pos-v3-linea); }
.pos-btn--grande { min-height: 56px; font-size: 16px; }

/* ── V3 · Sabores con contador ────────────────────────────────────────── */
/* Joni, 15/09/2026: «añade un botón de +/−1 y así podemos elegir varios
   sabores a la vez en un producto». Una fila por sabor, con su precio y su
   contador — el patrón de "Add to cart" con stepper que tienen D.CC y
   Fummo. Dos columnas en cuanto hay sitio: con 31 sabores de Marujitas, una
   sola columna obligaría a deslizar media pantalla. */
.pos-sabores { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 8px; }
.pos-sabor {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 8px 10px 8px 14px;
  background: var(--pos-v3-hundido);
  border: 1px solid var(--pos-v3-linea);
  border-radius: var(--pos-v3-r-control);
  transition: border-color 140ms var(--ease), background 140ms var(--ease);
}
/* Un sabor con unidades pedidas se distingue de un vistazo: es lo que se
   repasa antes de tocar «Añadir». */
.pos-sabor.tiene { background: var(--pos-v3-carta); border-color: var(--mrj-orange); box-shadow: 0 2px 10px -4px rgba(245, 152, 0, .45); }
.pos-sabor.out { opacity: .5; }
.pos-sabor__n { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.pos-sabor__n .nm { font-weight: 700; font-size: 14px; color: var(--pos-v3-texto); overflow-wrap: break-word; }
.pos-sabor__n .p { font-size: 12.5px; color: var(--pos-v3-suave); font-weight: 700; }
.pos-sabor__contador { display: flex; align-items: center; gap: 4px; flex: 0 0 auto; }
.pos-sabor__contador strong { min-width: 2ch; text-align: center; font-weight: 800; font-size: 15px; }
.pos-sabor__paso {
  width: 34px; height: 34px; border-radius: 999px;
  border: 1px solid var(--pos-v3-linea-fuerte);
  background: var(--pos-v3-carta);
  color: var(--pos-v3-texto);
  font-size: 17px; font-weight: 800; line-height: 1;
  display: flex; align-items: center; justify-content: center;
  transition: transform 120ms var(--ease), background 140ms var(--ease);
}
.pos-sabor__paso:active { transform: scale(.9); }
.pos-sabor__paso:disabled { opacity: .35; }
/* El + es el que se toca todo el rato: va en el acento, como el botón
   circular naranja de las tarjetas de RestroBit. */
.pos-sabor__paso.mas { background: var(--mrj-orange); border-color: var(--mrj-orange); color: var(--mrj-ink); }

/* El aviso del pack: gris mientras es un recordatorio («2 más y sale el
   Pack 12»), y en el acento cuando ya está aplicado y hay ahorro que
   cantar. */
.pos-eleccion__falta.es-pack { color: var(--mrj-orange); font-weight: 800; }

/* ═══════════════════════════════════════════════════════════════════════
   V3 · EL PANEL DEL PEDIDO
   ───────────────────────────────────────────────────────────────────────
   En las seis referencias cada línea es una tarjetita con su cantidad, sus
   extras debajo en pequeño y su importe a la derecha; los totales van
   separados por una línea fina, con el total final grande; y abajo, la
   barra de acción. Aquí se adopta eso mismo: lo que antes era una fila de
   texto con una x pequeña ahora es un objeto que se puede tocar.
   ═══════════════════════════════════════════════════════════════════════ */
.pos-lines { padding: 10px; display: flex; flex-direction: column; gap: 8px; }
.pos-line {
  grid-template-columns: 32px 1fr auto;
  gap: 10px;
  padding: 10px;
  background: var(--pos-v3-hundido);
  border: 1px solid var(--pos-v3-linea);
  border-radius: var(--pos-v3-r-control);
  min-height: 52px;
  transition: border-color 140ms var(--ease);
}
.pos-line:hover { background: var(--pos-v3-hundido); border-color: var(--pos-v3-linea-fuerte); }
/* La cantidad, en burbuja de acento: es el dato que se comprueba de un
   vistazo al repasar la cuenta en voz alta. */
.pos-line .q {
  background: color-mix(in srgb, var(--mrj-orange) 18%, transparent);
  color: var(--pos-v3-texto);
  border: 0;
  font-weight: 800;
  font-size: 13px;
}
.pos-line .n { font-weight: 700; font-size: 14px; color: var(--pos-v3-texto); }
.pos-line .m { font-size: 12px; color: var(--pos-v3-suave); font-weight: 600; }
.pos-line .p { font-weight: 800; color: var(--pos-v3-texto); font-size: 15px; }
.pos-line .del {
  color: var(--pos-v3-suave);
  border-radius: 999px;
  font-size: 15px;
  transition: background 140ms var(--ease), color 140ms var(--ease);
}
.pos-line .del:hover { background: var(--pos-v3-alerta-bg); color: var(--pos-v3-alerta-tx); }

/* ── Totales ──────────────────────────────────────────────────────────── */
.pos-tfoot { padding: 14px 16px 16px; gap: 8px; }
.pos-tot { font-size: 14px; color: var(--pos-v3-suave); font-weight: 600; }
.pos-tot .v { color: var(--pos-v3-texto); font-weight: 700; }
/* El total final, grande y en Inter Bold — el «Total: 4500$» de RestroBit */
.pos-tfoot .pos-tot:not(.sm) .l { color: var(--pos-v3-suave); font-size: 12px; letter-spacing: .08em; text-transform: uppercase; font-weight: 800; }
.pos-tfoot .pos-tot:not(.sm) .v { font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo); font-size: 30px; letter-spacing: -.03em; color: var(--pos-v3-texto); }

/* ── Cabecera del panel ───────────────────────────────────────────────── */
.pos-ticket > .pos-thead,
.pos-ticket > div:first-child { border-bottom: 1px solid var(--pos-v3-linea); }
.pos-empty { color: var(--pos-v3-suave); font-size: 14px; }

/* ═══════════════════════════════════════════════════════════════════════
   V3 · EL COBRO, EN VENTANA CENTRADA
   ───────────────────────────────────────────────────────────────────────
   Como el «Collect Payment» de RestroBit: una ventana sobre la app con el
   importe grande arriba, los métodos como tarjetas, los atajos de billete,
   el teclado y dos botones abajo. Antes era un panel embutido en la
   columna del ticket, donde a poca altura de pantalla el botón de cobrar
   acababa tapando las teclas (Joni, 15/09/2026: «el botón de cobrar se
   solapa con los números»). En una ventana centrada hay sitio de sobra y
   el problema no puede volver.
   ═══════════════════════════════════════════════════════════════════════ */
.pos-tpv-body .pos-paysheet {
  position: fixed;
  inset: 0;
  z-index: 40;
  background: transparent;
  padding: 0;
  display: grid;
  place-items: center;
  overflow: visible;
}
/* El velo lo pinta la propia hoja: no hay un elemento aparte que tocar. */
.pos-tpv-body .pos-paysheet::before {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(19, 7, 2, .46);
}
/* Todo el contenido, dentro de una caja centrada */
.pos-tpv-body .pos-paysheet > * { position: relative; z-index: 1; }
.pos-tpv-body .pos-paysheet {
  grid-template-rows: auto auto minmax(0, 1fr);
  align-content: center;
  justify-items: stretch;
}
.pos-tpv-body .pos-paysheet > h2,
.pos-tpv-body .pos-paysheet > .methods,
.pos-tpv-body .pos-paysheet > .pos-paysheet__cobro {
  width: min(460px, calc(100vw - 32px));
  margin: 0 auto;
  background: var(--pos-v3-carta);
  padding: 0 22px;
}
.pos-tpv-body .pos-paysheet > h2 {
  padding: 20px 22px 14px;
  border-radius: var(--pos-v3-r-lienzo) var(--pos-v3-r-lienzo) 0 0;
  box-shadow: var(--pos-v3-sombra-3);
  font-size: 21px;
}
.pos-tpv-body .pos-paysheet > .methods { padding: 0 22px 14px; }
.pos-tpv-body .pos-paysheet > .pos-paysheet__cobro {
  padding: 0 22px 20px;
  border-radius: 0 0 var(--pos-v3-r-lienzo) var(--pos-v3-r-lienzo);
  box-shadow: var(--pos-v3-sombra-3);
  max-height: 70vh;
  overflow: auto;
}
/* Los métodos, como las tarjetas Cash/Card/UPI de la referencia */
.pos-paysheet .pos-paysheet__metodo {
  min-height: 54px;
  border-radius: var(--pos-v3-r-control);
  border: 1px solid var(--pos-v3-linea);
  background: var(--pos-v3-hundido);
  font-weight: 700;
  color: var(--pos-v3-texto);
}
.pos-paysheet .pos-paysheet__metodo.on {
  border-color: var(--mrj-orange);
  background: var(--mrj-orange);
  color: var(--mrj-ink);
  box-shadow: 0 6px 16px -6px rgba(245, 152, 0, .55);
}
/* El importe, enorme y en Inter Bold */
.pos-paysheet .amount {
  font-family: var(--pos-font-display);
  font-weight: var(--pos-peso-titulo);
  font-size: 40px;
  letter-spacing: -.03em;
  color: var(--pos-v3-texto);
}

/* Las tres zonas de la hoja (título, métodos, cuerpo) son hijas directas del
   grid, así que el `gap` de la hoja las separaba en tres cajas flotando con
   huecos entre medias. Se pegan en una sola ventana: gap a cero, sombra
   solo en los extremos y el fondo continuo en las tres. */
.pos-tpv-body .pos-paysheet { gap: 0; }
.pos-tpv-body .pos-paysheet > h2 { box-shadow: 0 -18px 40px -20px rgba(19, 7, 2, .4); }
.pos-tpv-body .pos-paysheet > .methods { box-shadow: none; }
.pos-tpv-body .pos-paysheet > .pos-paysheet__cobro { box-shadow: 0 28px 56px -16px rgba(19, 7, 2, .28); }
/* El teclado, dentro de la ventana, no necesita la altura de una pantalla
   entera: teclas algo menores dejan sitio al botón sin apretarlo. */
.pos-tpv-body .pos-paysheet .pos-keypad__tecla { min-height: 52px; }
.pos-tpv-body .pos-paysheet .pos-keypad { grid-template-columns: repeat(3, 1fr); width: 100%; }
/* Las teclas, en el gris hundido del sistema (antes heredaban el crema de
   marca y competían con el naranja del botón). */
.pos-keypad__tecla { background: var(--pos-v3-hundido); border-color: var(--pos-v3-linea); color: var(--pos-v3-texto); font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo); border-radius: var(--pos-v3-r-control); }
.pos-keypad__tecla:active { background: var(--pos-v3-carta); }
/* Los atajos de importe, como los «50$ 65$ 75$» de RestroBit */
.pos-paysheet .pos-chip { min-height: 44px; }

/* Con `grid-template-rows` la tercera fila se quedaba con todo el alto libre
   y su contenido se centraba dentro, dejando un hueco entre los métodos y el
   teclado: la ventana parecía partida en dos. Con una columna flex las tres
   zonas quedan pegadas y la ventana mide lo que mide su contenido. */
.pos-tpv-body .pos-paysheet {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0;
}
.pos-tpv-body .pos-paysheet > h2,
.pos-tpv-body .pos-paysheet > .methods,
.pos-tpv-body .pos-paysheet > .pos-paysheet__cobro { flex: 0 0 auto; }
.pos-tpv-body .pos-paysheet > .pos-paysheet__cobro { max-height: 62vh; }

/* La navegación lateral se probó el 15/09/2026 y Joni la rechazó al verla:
   «La columna lateral no me gusta, volvemos a poner las secciones en la barra
   superior, pero lo vamos a hacer como botones modernos». El bloque completo
   queda guardado en el scratchpad de esa sesión por si algún día se retoma
   (una pantalla de obrador o un monitor vertical serían buenos candidatos).
   Lo que SÍ se queda de aquella pasada: la cabecera y su navegación usan ya
   los tokens del sistema V3, que es lo que se rehace ahora en horizontal. */


/* El ahorro del pack, en el color de acento: es una buena noticia, no una
   resta más. */
.pos-tot--pack .l, .pos-tot--pack .v { color: var(--mrj-orange); font-weight: 800; }

/* ═══════════════════════════════════════════════════════════════════════
   V3 · EL PLANO DE MESAS
   ───────────────────────────────────────────────────────────────────────
   En las referencias, **lo libre es neutro** y el color se reserva a lo que
   pide atención: una mesa ocupada, una que pide la cuenta, una que lleva
   demasiado sin atender. Aquí todas las mesas salían en arena —incluidas
   las nueve libres— y la pantalla entera pedía atención, que es lo mismo
   que no pedirla.
   ═══════════════════════════════════════════════════════════════════════ */
.pos-mesa {
  border-radius: var(--pos-v3-r-carta);
  border-width: 1px;
  box-shadow: var(--pos-v3-sombra-1);
  transition: transform 120ms var(--ease), box-shadow 160ms var(--ease);
}
.pos-mesa:hover { box-shadow: var(--pos-v3-sombra-2); }
.pos-mesa:active { transform: scale(.98); }
/* Libre: neutro. Es el estado que NO hay que mirar. */
.pos-mesa.libre { border-color: var(--pos-v3-linea); background: var(--pos-v3-carta); color: var(--pos-v3-suave); }
.pos-mesa.libre .n { color: var(--pos-v3-texto); }
/* Ocupada: la mesa está dando dinero — acento suave, con su borde */
.pos-mesa.ocupada { border-color: color-mix(in srgb, var(--mrj-orange) 45%, transparent); background: color-mix(in srgb, var(--mrj-orange) 10%, var(--pos-v3-carta)); color: var(--pos-v3-texto); }
/* Pidiendo: acento pleno, es lo que hay que atender YA */
.pos-mesa.pidiendo { border-color: var(--mrj-orange); background: var(--mrj-orange); color: var(--mrj-ink); }
/* Pide cuenta: borde marcado, sin relleno — llama sin gritar */
.pos-mesa.cuenta { border-color: var(--mrj-orange); border-width: 2px; background: var(--pos-v3-carta); color: var(--pos-v3-texto); }
.pos-mesa.preparando { border-color: color-mix(in srgb, var(--pos-v3-espera-tx) 40%, transparent); background: var(--pos-v3-espera-bg); color: var(--pos-v3-espera-tx); }
.pos-mesa.atascada { border-color: color-mix(in srgb, var(--pos-v3-alerta-tx) 45%, transparent); background: var(--pos-v3-alerta-bg); color: var(--pos-v3-alerta-tx); }
.pos-mesa.seleccionada { outline: 3px solid var(--mrj-orange); outline-offset: 2px; }
/* La zona, como rótulo de sección */
.pos-zone h3 { font-family: var(--pos-font-display); font-weight: var(--pos-peso-titulo); font-size: 15px; color: var(--pos-v3-texto); letter-spacing: -.01em; }
/* La leyenda, en chips pequeños */
.pos-plan { background: transparent; }

/* ═══════════════════════════════════════════════════════════════════════
   V3 · CONTROL (las tarjetas de dato)
   ───────────────────────────────────────────────────────────────────────
   Las tarjetas de KPI de las referencias son blancas con la cifra enorme, la
   etiqueta en versalitas grises y, cuando toca, un delta con flecha de
   color. Aquí eran cajas de arena que competían entre sí; con el lienzo
   nuevo, blanco sobre gris cálido ya separa de sobra.
   ═══════════════════════════════════════════════════════════════════════ */
.pos-kpi {
  background: var(--pos-v3-carta);
  border: 1px solid var(--pos-v3-linea);
  border-radius: var(--pos-v3-r-carta);
  box-shadow: var(--pos-v3-sombra-1);
}
.pos-kpi .l { color: var(--pos-v3-suave); }
.pos-kpi .v { font-weight: var(--pos-peso-titulo); font-size: 34px; letter-spacing: -.03em; color: var(--pos-v3-texto); }
.pos-kpi .s { color: var(--pos-v3-suave); }
/* La destacada lleva el acento en la cifra, no un fondo distinto: así la
   fila de tarjetas se lee como una fila, no como cuatro objetos sueltos. */
.pos-kpi--destacada { background: var(--pos-v3-carta); border-color: color-mix(in srgb, var(--mrj-orange) 40%, transparent); }
.pos-kpi--destacada .v { color: var(--mrj-orange); }
.pos-kpi--destacada .l, .pos-kpi--destacada .s { color: var(--pos-v3-suave); }

/* Los paneles y secciones de Control, sobre el mismo lienzo */
.pos-ctrl-grp { color: var(--pos-v3-suave); font-size: 11px; letter-spacing: .08em; }

/* V3 · Control: submenú y contenido sobre el lienzo nuevo */
.pos-ctrl { background: var(--pos-v3-lienzo); }
.pos-ctrl .nav,
.pos-ctrl-nav { background: var(--pos-v3-carta); border-right: 1px solid var(--pos-v3-linea); }
.pos-ctrl .nav .g,
.pos-ctrl-nav .g { color: var(--pos-v3-suave); }
.pos-ctrl .nav button,
.pos-ctrl-nav button { color: var(--pos-v3-texto); border-radius: var(--pos-v3-r-control); font-weight: 700; }
.pos-ctrl .nav button:hover,
.pos-ctrl-nav button:hover { background: var(--pos-v3-hundido); }
.pos-ctrl .nav button.on,
.pos-ctrl-nav button.on { background: var(--mrj-orange); color: var(--mrj-ink); box-shadow: 0 6px 16px -8px rgba(245, 152, 0, .7); }
/* Cada bloque del cuerpo, en tarjeta blanca como en las referencias.
   CORREGIDO el 15/09/2026 (Joni: «los titulos están tocando el borde del
   cuadrado que los contiene»): la primera versión daba fondo, borde y radio
   a CUALQUIER div del cuerpo, pero sin padding — así que el título quedaba
   pegado al borde de su propia caja. Y además envolvía en tarjeta el título
   de la página, que no es una tarjeta: es el encabezado de la pantalla.
   Ahora se nombran las cajas de verdad, y cada una lleva su aire. */
.pos-ctrl .main > section,
.pos-ctrl .main > .pos-cbox,
.pos-ctrl .main > .pos-lista-mini,
.pos-ctrl .main > .pos-grafico-barras {
  background: var(--pos-v3-carta);
  border: 1px solid var(--pos-v3-linea);
  border-radius: var(--pos-v3-r-carta);
  box-shadow: var(--pos-v3-sombra-1);
  padding: 20px;
}
/* El encabezado de la pantalla NO es una caja: va sobre el lienzo, como el
   «Point of Sale (POS)» de RestroBit o el «Dashboard» de D.CC. */
.pos-ctrl .main > .pos-ctrl__titulo,
.pos-ctrl .main > h2:first-child {
  background: transparent;
  border: 0;
  box-shadow: none;
  padding: 0 4px;
}
/* Los títulos dentro de cada caja: aire debajo y jerarquía clara */
.pos-ctrl .main > section > h3:first-child,
.pos-ctrl .main > .pos-cbox > h3:first-child,
.pos-ctrl .main .pos-cbox__titulo {
  font-family: var(--pos-font-display);
  font-weight: var(--pos-peso-titulo);
  font-size: 16px;
  letter-spacing: -.01em;
  color: var(--pos-v3-texto);
  margin: 0 0 14px;
}

/* ═══════════════════════════════════════════════════════════════════════
   V4 · LA BARRA SUPERIOR — botones de sección
   ───────────────────────────────────────────────────────────────────────
   Joni, 15/09/2026, tras probar la columna lateral:
   «volvemos a poner las secciones en la barra superior, pero lo vamos a
   hacer como botones modernos, es decir, como si fuese un botón en si que
   lo pulsas y se abre esa sección» · «Las secciones de la barra superior
   deben de estar alineadas, con el mismo espacio entre el texto POS y el
   botón de la caja, cambiandole la tipografía y con los detalles
   interactivos que hablamos» · «pon inter bold para los titulos de las
   secciones también, estas deben de ser el alma de nuestra app».

   Así que: Inter Bold, botones de verdad (no pestañas planas), el mismo
   aire a izquierda y derecha, y el activo con el acento de marca.
   ═══════════════════════════════════════════════════════════════════════ */
.pos-head {
  gap: 28px;                      /* el MISMO hueco entre marca↔secciones y secciones↔caja */
  padding: 0 20px;
  background: var(--brown);
}
/* La marca: logotipo + POS + Fichajes, los dos como botones */
.pos-head .logo { gap: 12px; }
.pos-marca__pos,
.pos-marca__accion {
  font-family: var(--pos-font-display);
  font-weight: var(--pos-peso-titulo);
  letter-spacing: -.01em;
  border-radius: 999px;
  border: 1px solid rgba(252, 236, 215, .22);
  background: rgba(252, 236, 215, .10);
  color: var(--mrj-cream);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  transition: background 140ms var(--ease), border-color 140ms var(--ease), transform 120ms var(--ease);
}
.pos-marca__pos { font-size: 13px; padding: 6px 13px; }
.pos-marca__accion { font-size: 12.5px; padding: 6px 12px; }
.pos-marca__pos:hover,
.pos-marca__accion:hover { background: rgba(252, 236, 215, .18); border-color: rgba(252, 236, 215, .38); }
.pos-marca__pos:active,
.pos-marca__accion:active { transform: scale(.96); }
/* POS es el botón de Control: cuando estás DENTRO, lleva el acento */
.pos-pantalla-tpv[data-modo="control"] .pos-marca__pos,
.pos-marca__pos.on { background: var(--mrj-orange); border-color: var(--mrj-orange); color: var(--mrj-ink); }
/* El contador de quién está dentro, en burbuja */
.pos-marca__accion .b {
  background: var(--mrj-orange);
  color: var(--mrj-ink);
  border-radius: 999px;
  font-size: 10.5px;
  font-weight: 800;
  padding: 1px 6px;
  min-width: 18px;
  text-align: center;
}

/* ── Las secciones ─────────────────────────────────────────────────────── */
.pos-modes {
  gap: 6px;
  margin-left: 0;
  justify-content: flex-start;
  align-items: center;
}
.pos-modes button {
  font-family: var(--pos-font-display);
  font-weight: var(--pos-peso-titulo);
  font-size: 14px;
  letter-spacing: -.01em;
  padding: 9px 16px;
  min-height: 40px;
  border-radius: var(--pos-v3-r-control);
  border: 1px solid transparent;
  background: rgba(252, 236, 215, .07);
  color: color-mix(in srgb, var(--mrj-cream) 82%, transparent);
  display: inline-flex;
  align-items: center;
  gap: 8px;
  transition: background 150ms var(--ease), color 150ms var(--ease), transform 120ms var(--ease), box-shadow 180ms var(--ease);
}
.pos-modes button:hover {
  background: rgba(252, 236, 215, .16);
  color: var(--mrj-cream);
  transform: translateY(-1px);
}
.pos-modes button:active { transform: translateY(0) scale(.97); }
/* El activo: botón de acento con elevación — se ve desde el otro lado del
   mostrador cuál es la pantalla en la que estás. */
.pos-modes button.on {
  background: var(--mrj-orange);
  color: var(--mrj-ink);
  border-color: var(--mrj-orange);
  box-shadow: 0 8px 20px -8px rgba(245, 152, 0, .85);
}
.pos-modes button .b {
  background: rgba(19, 7, 2, .16);
  color: inherit;
  border-radius: 999px;
  font-family: var(--pos-font);
  font-size: 11px;
  font-weight: 800;
  padding: 1px 7px;
}
.pos-modes button.on .b { background: rgba(19, 7, 2, .22); }
/* El bloque de la derecha (caja + usuario + menú) se separa lo mismo que la
   marca por la izquierda: la barra queda ópticamente equilibrada. */
.pos-head .who { margin-left: auto; gap: 12px; }

/* ═══════════════════════════════════════════════════════════════════════
   V4 · VENTANAS (`.pos-ventana`) — el chasis común
   ───────────────────────────────────────────────────────────────────────
   Joni: «todas las ventanas que se abran tiene que ser así», con «mejor
   diseño» y «una estructura visual clara». Una sola forma de ventana para
   todo el TPV: velo, caja centrada, cabecera con título y subtítulo, cuerpo
   que scrollea y pie opcional. Lo que cambia de una ventana a otra es el
   contenido, nunca el chasis.
   ═══════════════════════════════════════════════════════════════════════ */
.pos-ventana { position: fixed; inset: 0; z-index: 45; display: flex; align-items: center; justify-content: center; padding: 20px; }
.pos-ventana__velo { position: absolute; inset: 0; background: rgba(19, 7, 2, .5); backdrop-filter: blur(2px); }
.pos-ventana__caja {
  position: relative;
  width: min(560px, 100%);
  max-height: min(86vh, 760px);
  display: flex;
  flex-direction: column;
  background: var(--pos-v3-carta);
  border: 1px solid var(--pos-v3-linea);
  border-radius: var(--pos-v3-r-lienzo);
  box-shadow: var(--pos-v3-sombra-3);
  overflow: hidden;
}
.pos-ventana__caja--ancha { width: min(920px, 100%); }
.pos-ventana__cab {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
  padding: 20px 22px 16px;
  border-bottom: 1px solid var(--pos-v3-linea);
}
.pos-ventana__cab h3 {
  font-family: var(--pos-font-display);
  font-weight: var(--pos-peso-titulo);
  font-size: 21px;
  letter-spacing: -.02em;
  color: var(--pos-v3-texto);
  margin: 0;
}
.pos-ventana__sub { color: var(--pos-v3-suave); font-size: var(--fs-sm, .875rem); margin: 3px 0 0; }
.pos-ventana__cuerpo { flex: 1; min-height: 0; overflow: auto; padding: 20px 22px; }
.pos-ventana__pie {
  display: flex; align-items: center; justify-content: flex-end; gap: 10px;
  padding: 14px 22px;
  border-top: 1px solid var(--pos-v3-linea);
  background: var(--pos-v3-hundido);
}
@media (max-width: 640px) {
  .pos-ventana { padding: 0; }
  .pos-ventana__caja, .pos-ventana__caja--ancha { width: 100%; height: 100%; max-height: 100%; border-radius: 0; border: 0; }
}

/* ═══════════════════════════════════════════════════════════════════════
   V4 · LA CABECERA, CLARA
   ───────────────────────────────────────────────────────────────────────
   Hallazgo del revisor de diseño (15/09/2026), prioridad alta: «las seis
   referencias nunca ponen la navegación en una barra sólida de color oscuro
   a sangre completa». La app vive dentro de tarjetas claras sobre un lienzo,
   y la cabecera es una de esas tarjetas — no un bloque de marca.

   Con el fondo claro hay que mover TODO lo que colgaba del marrón, o queda
   texto crema sobre blanco: el logotipo (pasa a la versión a color), los
   botones de la marca, los modos, el botón de Caja, el de menú, el nombre
   del usuario y la máscara de degradado del scroll de los modos.
   ═══════════════════════════════════════════════════════════════════════ */
.pos-head {
  background: var(--pos-v3-carta);
  color: var(--pos-v3-texto);
  border-bottom: 1px solid var(--pos-v3-linea);
  box-shadow: var(--pos-v3-sombra-1);
}
.pos-head .logo span { color: var(--pos-v3-texto); }

/* POS y Fichajes: botones claros con borde fino */
.pos-marca__pos,
.pos-marca__accion {
  background: var(--pos-v3-hundido);
  border-color: var(--pos-v3-linea);
  color: var(--pos-v3-texto);
}
.pos-marca__pos:hover,
.pos-marca__accion:hover { background: var(--pos-v3-carta); border-color: var(--pos-v3-linea-fuerte); }

/* Las secciones */
.pos-modes button { background: transparent; color: var(--pos-v3-suave); }
.pos-modes button:hover { background: var(--pos-v3-hundido); color: var(--pos-v3-texto); }
/* El activo no cambia: sigue siendo el botón de acento */
.pos-modes button .b { background: var(--pos-v3-hundido); color: var(--pos-v3-suave); }
.pos-modes button.on .b { background: rgba(19, 7, 2, .16); color: var(--mrj-ink); }

/* La máscara que difumina el scroll de los modos estaba pintada EN MARRÓN:
   sobre la cabecera clara dejaba un manchón. */
/* La pista de scroll de los modos estaba pintada con cuatro degradados EN
   MARRÓN (regla de la línea ~1130). Sobre la cabecera clara dejaban un
   manchón oscuro flotando a la derecha. Se rehacen en el color de la
   cabecera: misma pista, ahora invisible cuando no hay nada que desplazar. */
.pos-head .pos-modes {
  background:
    linear-gradient(to right, var(--pos-v3-carta) 30%, rgba(255, 255, 255, 0)) 0 0 / 24px 100% no-repeat local,
    linear-gradient(to left, var(--pos-v3-carta) 30%, rgba(255, 255, 255, 0)) 100% 0 / 24px 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%, rgba(19, 7, 2, .10), rgba(19, 7, 2, 0)) 0 0 / 10px 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, rgba(19, 7, 2, .10), rgba(19, 7, 2, 0)) 100% 0 / 10px 100% no-repeat scroll;
}

/* Caja, menú y usuario */
.pos-btn--caja {
  background: var(--pos-v3-hundido);
  border: 1px solid var(--pos-v3-linea-fuerte);
  color: var(--pos-v3-texto);
  font-family: var(--pos-font-display);
  font-weight: var(--pos-peso-titulo);
}
.pos-btn--caja:hover { background: var(--pos-v3-carta); }
.pos-head .lnk {
  border: 1px solid var(--pos-v3-linea-fuerte);
  color: var(--pos-v3-texto);
  background: var(--pos-v3-hundido);
  border-radius: var(--pos-v3-r-control);
}
.pos-head .lnk:hover { background: var(--pos-v3-carta); }
.pos-head .who .nombre { color: var(--pos-v3-texto); }
.pos-head .who .rol { color: var(--pos-v3-suave); }
.pos-head .pos-badge.info { background: var(--pos-v3-info-bg); color: var(--pos-v3-info-tx); }

/* ── Píldoras de estado a la paleta V3 ─────────────────────────────────── */
/* Otro hallazgo del revisor: `.pos-badge` seguía con los pasteles apagados
   de antes del rediseño mientras la tarjeta que los rodea ya era V3. */
.pos-badge.ok { background: var(--pos-v3-ok-bg); color: var(--pos-v3-ok-tx); }
.pos-badge.warn { background: var(--pos-v3-espera-bg); color: var(--pos-v3-espera-tx); }
.pos-badge.bad { background: var(--pos-v3-alerta-bg); color: var(--pos-v3-alerta-tx); }
.pos-badge.info { background: var(--pos-v3-info-bg); color: var(--pos-v3-info-tx); }

/* ── V4 · Control, micro-interacciones ─────────────────────────────────── */
/* Del agente de Control: elevación al pasar por encima, SOLO en dispositivos
   con puntero y para quien no ha pedido menos movimiento. Bajo
   `prefers-reduced-motion` no se queda "saltando" — sencillamente no hay
   elevación, que es lo que pide esa preferencia. */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .pos-kpi,
  .pos-ctrl .main > section,
  .pos-ctrl .main > .pos-cbox,
  .pos-ctrl .main > .pos-lista-mini,
  .pos-ctrl .main > .pos-grafico-barras {
    transition: transform var(--pos-t), box-shadow var(--pos-t);
  }
  .pos-kpi:hover,
  .pos-ctrl .main > section:hover,
  .pos-ctrl .main > .pos-cbox:hover,
  .pos-ctrl .main > .pos-lista-mini:hover,
  .pos-ctrl .main > .pos-grafico-barras:hover {
    transform: translateY(-2px);
    box-shadow: var(--pos-v3-sombra-2);
  }
}
/* Las filas de las listas (Más vendidos, Avisos), como el «Recent Activity»
   de D.CC: se iluminan al pasar por encima. */
.pos-lista-mini .fila,
.pos-crow { border-radius: var(--pos-v3-r-control); transition: background var(--pos-t); }
@media (hover: hover) {
  .pos-lista-mini .fila:hover,
  .pos-crow:hover { background: var(--pos-v3-hundido); }
}

/* ═══════════════════════════════════════════════════════════════════════
   V4 · EL PLANO DE MESAS, COMO NUESTRO PLANO DE VERDAD
   ───────────────────────────────────────────────────────────────────────
   Joni, 15/09/2026: «La parte de las mesas ponlas también con nuestras
   mesas ya, tienes la captura de cómo son en la carpeta de las capturas de
   qamarero». Las dos capturas (`Control-Mesas.png`, Salón S1–S10, y
   `Control-MesasTerraza.png`, Terraza T11–T16) enseñan lo mismo:

     · un lienzo gris con un punteado fino, que es el suelo de la sala;
     · mesas cuadradas BLANCAS de esquinas muy redondeadas;
     · el nombre largo arriba, diminuto, en versalitas grises («MESA 7»);
     · el identificador corto enorme en el centro («S7»);
     · y NINGUNA silla dibujada.

   Las dos sillas de `::before`/`::after` del bloque V2 se apagan aquí. La
   auditoría de diseño pedía subirlas a cuatro, pero eso venía de nuestro
   plano anterior, no de las capturas — y en esta campaña manda la captura.

   Lo que las capturas NO traen es la posición de cada mesa en la sala: el
   plano de Qamarero es un editor de coordenadas y nuestra tabla `mesas` no
   guarda ninguna. Así que las mesas se colocan en rejilla por zona. Antes
   que inventarse un plano que no se corresponde con la sala, una rejilla
   honesta.
   ═══════════════════════════════════════════════════════════════════════ */
.pos-mesa::before,
.pos-mesa::after { content: none; }

.pos-lienzo {
  border-radius: var(--pos-v3-r-lienzo);
  border: 1px solid var(--pos-v3-linea);
  background-color: var(--pos-v3-lienzo);
  /* El punteado del suelo: un punto por celda de 22px, del mismo marrón de
     las líneas pero muy rebajado. */
  background-image: radial-gradient(circle at 1px 1px, rgba(76, 44, 28, .16) 1px, transparent 0);
  background-size: 22px 22px;
  padding: 22px;
}
.pos-lienzo .tables { display: flex; flex-wrap: wrap; gap: 18px; }

.pos-mesa {
  width: 136px;
  height: 118px;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 4px;
  padding: 12px 10px;
  border-radius: 20px;
}
/* El rótulo largo: versalitas muy espaciadas, como en la captura. */
/* Una mesa libre pinta su texto en el marrón suave (bloque V3), pero el
   identificador grande es el protagonista y va en tinta plena, como en la
   captura. El selector del V3 apuntaba a `.n`, que no existe en el marcado:
   la clase del rótulo es `.nm`. */
.pos-mesa.libre .nm { color: var(--pos-v3-texto); }
.pos-mesa .lg {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--pos-v3-suave);
  line-height: 1;
}
/* El identificador corto es el protagonista de la mesa. */
.pos-mesa .nm {
  font-family: var(--pos-font-display);
  font-weight: var(--pos-peso-titulo);
  font-size: 32px;
  letter-spacing: -.03em;
  line-height: 1.05;
}
/* El pie (estado + importe + tiempo) solo existe cuando la mesa NO está
   libre: una mesa libre es el cuadrado limpio de la captura. */
.pos-mesa .pie { display: flex; flex-direction: column; align-items: center; gap: 1px; margin-top: 2px; }
.pos-mesa .s { font-size: 9.5px; letter-spacing: .1em; opacity: .85; }
.pos-mesa .imp { font-size: 15px; font-weight: var(--pos-peso-titulo); }
.pos-mesa .t { font-size: 10.5px; color: inherit; opacity: .7; font-weight: 600; }
/* En una mesa de color pleno (pidiendo) el importe y el tiempo heredan su
   tinta — nunca el marrón del bloque V2, que ahí no se lee. */
.pos-mesa.pidiendo .t, .pos-mesa.preparando .t, .pos-mesa.atascada .t { color: inherit; }

/* El rótulo de zona, con su recuento al lado en gris. */
.pos-zone h3 { margin-bottom: 12px; }
.pos-zone__n {
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .02em;
  color: var(--pos-v3-suave);
  text-transform: none;
}
.pos-mob .pos-lienzo { padding: 12px; }
.pos-mob .pos-lienzo .tables { gap: 10px; }
.pos-mob .pos-mesa { width: calc(33.33% - 7px); height: 104px; }

/* ═══════════════════════════════════════════════════════════════════════
   V4 · FILAS DE OPCIÓN Y CASILLAS DE DATO (dentro de una ventana)
   ───────────────────────────────────────────────────────────────────────
   La ventana de opciones de una mesa era un desplegable de texto colgando
   del «⋯». Ahora las acciones son filas grandes de tocar, con su nota
   debajo y la flecha a la derecha, y encima va una fila de casillas con los
   datos REALES de la cuenta (estado, tiempo, comensales, líneas, total).
   Una casilla sin dato detrás no se pinta: nunca un hueco relleno.
   ═══════════════════════════════════════════════════════════════════════ */
.pos-datos { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; }
.pos-dato {
  flex: 1 1 96px;
  display: flex; flex-direction: column; gap: 3px;
  padding: 10px 12px;
  background: var(--pos-v3-hundido);
  border: 1px solid var(--pos-v3-linea);
  border-radius: var(--pos-v3-r-control);
}
.pos-dato .l { font-size: 10.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--pos-v3-suave); }
.pos-dato .v {
  font-family: var(--pos-font-display);
  font-weight: var(--pos-peso-titulo);
  font-size: 17px;
  letter-spacing: -.02em;
  color: var(--pos-v3-texto);
  font-variant-numeric: tabular-nums;
}

.pos-opciones { display: flex; flex-direction: column; gap: 8px; }
.pos-opcion {
  display: flex; align-items: center; gap: 12px;
  width: 100%;
  padding: 14px 16px;
  text-align: left;
  background: var(--pos-v3-carta);
  border: 1px solid var(--pos-v3-linea);
  border-radius: var(--pos-v3-r-carta);
  color: var(--pos-v3-texto);
  cursor: pointer;
  transition: border-color 140ms var(--ease), background 140ms var(--ease), transform 120ms var(--ease);
}
.pos-opcion:hover { border-color: var(--mrj-orange); background: var(--pos-v3-hundido); }
.pos-opcion:active { transform: scale(.99); }
.pos-opcion__txt { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.pos-opcion__txt b {
  font-family: var(--pos-font-display);
  font-weight: var(--pos-peso-titulo);
  font-size: 15.5px;
  letter-spacing: -.01em;
}
.pos-opcion__txt small { color: var(--pos-v3-suave); font-size: 12.5px; line-height: 1.35; }
.pos-opcion__ir { color: var(--pos-v3-suave); font-size: 22px; line-height: 1; transition: transform 140ms var(--ease), color 140ms var(--ease); }
.pos-opcion:hover .pos-opcion__ir { color: var(--mrj-orange); transform: translateX(3px); }

/* ═══════════════════════════════════════════════════════════════════════
   V4 · UN SOLO SUELO PARA TODA LA APP
   ───────────────────────────────────────────────────────────────────────
   El lienzo gris cálido del sistema V3 solo lo pintaba la pantalla de venta
   (`.pos-tpv-body`). Mesas, Recogidas, Encargos, Obrador y Control se
   quedaban sobre el crema del chasis, así que la app cambiaba de suelo al
   cambiar de sección — justo lo que las referencias no hacen nunca: en las
   seis, el lienzo es uno y las tarjetas se mueven encima.

   Se pinta en `#tpv-cuerpo` (el contenedor de TODAS las secciones) en vez
   de repetir la regla sección a sección.
   ═══════════════════════════════════════════════════════════════════════ */
#tpv-cuerpo { background: var(--pos-v3-lienzo); }

/* La tira de mesas de la cuenta es una tarjeta más del lienzo, no un bloque
   de arena: con el suelo gris de arriba, el `--pos-ground` del bloque V2 se
   veía como un parche de otro material. */
.pos-mesas-tira {
  background: var(--pos-v3-carta);
  border: 1px solid var(--pos-v3-linea);
  border-radius: var(--pos-v3-r-carta);
  box-shadow: var(--pos-v3-sombra-1);
  padding: 10px 14px;
  gap: 8px;
}

/* Descuento y Vaciar: fila fina a la derecha, encima del botón de cobro. */
.pos-tfoot__menores { display: flex; justify-content: flex-end; gap: 6px; margin-bottom: 4px; }
.pos-tfoot__menores .pos-btn { flex: 0 0 auto; }

/* Rótulo de bloque dentro de una ventana: Inter Bold, con aire arriba y
   poco abajo, para que el título «pese» sobre lo que abre. */
.pos-bloque-titulo {
  font-family: var(--pos-font-display);
  font-weight: var(--pos-peso-titulo);
  font-size: 12px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--pos-v3-suave);
  margin: 22px 0 10px;
}
.pos-ventana__cuerpo > .pos-bloque-titulo:first-child { margin-top: 0; }
/* Tercera línea opcional de una casilla de dato (el detalle de debajo). */
.pos-dato .s { font-size: 11.5px; color: var(--pos-v3-suave); line-height: 1.3; }

/* Nota honesta de "esto todavía no está" dentro de una pantalla que SÍ
   funciona: se lee, no grita, y nunca ocupa el sitio del contenido real. */
.pos-nota-fase {
  margin: 0 0 12px;
  font-size: 12.5px;
  color: var(--pos-v3-suave);
  line-height: 1.4;
}

/* ═══════════════════════════════════════════════════════════════════════
   V4 · CONTROL: TODO BLOQUE ES UNA TARJETA, Y LAS TARJETAS RESPONDEN
   ───────────────────────────────────────────────────────────────────────
   Joni: «hay que mejorar también mucho la sección de Control, donde los
   títulos están tocando el borde del cuadrado que los contiene; las
   tarjetas hay que hacerlas más modernas e interactivas».

   El aire de los títulos lo arregló el bloque V3. Lo que quedaba: los dos
   bloques de abajo del Resumen —«Más vendidos» y el gráfico de ventas por
   hora— no eran tarjetas, flotaban sueltos sobre el lienzo mientras los
   cuatro KPI de arriba sí lo eran. En las seis referencias TODO bloque de
   un panel vive dentro de su tarjeta; media pantalla con tarjeta y media
   sin ella es lo que hacía que Control se viera a medio hacer.

   «Interactivas» = que respondan al dedo y al ratón: se levantan un punto y
   la sombra crece. Nada de animaciones de adorno.
   ═══════════════════════════════════════════════════════════════════════ */
.pos-lista-mini,
.pos-grafico-barras {
  background: var(--pos-v3-carta);
  border: 1px solid var(--pos-v3-linea);
  border-radius: var(--pos-v3-r-carta);
  box-shadow: var(--pos-v3-sombra-1);
  padding: 18px 20px;
}
.pos-lista-mini > h2,
.pos-grafico-barras > h2 {
  font-family: var(--pos-font-display);
  font-weight: var(--pos-peso-titulo);
  font-size: 16px;
  letter-spacing: -.01em;
  color: var(--pos-v3-texto);
  margin: 0 0 12px;
}
.pos-lista-mini .fila { border-bottom-color: var(--pos-v3-linea); }
.pos-lista-mini .fila .thumb {
  width: 40px; height: 40px;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--pos-v3-r-control);
  background: var(--pos-v3-hundido);
  border: 1px solid var(--pos-v3-linea);
  font-family: var(--pos-font-display);
  font-weight: var(--pos-peso-titulo);
  color: var(--pos-v3-suave);
}

/* Las tarjetas de dato responden: un punto de elevación y la sombra al
   siguiente escalón. La transición va en las dos direcciones, para que
   soltar el ratón no dé un salto seco. */
.pos-kpi,
.pos-lista-mini,
.pos-grafico-barras {
  transition: transform 160ms var(--ease), box-shadow 160ms var(--ease), border-color 160ms var(--ease);
}
.pos-kpi:hover,
.pos-lista-mini:hover,
.pos-grafico-barras:hover {
  transform: translateY(-1px);
  box-shadow: var(--pos-v3-sombra-2);
}
.pos-kpi:hover { border-color: var(--pos-v3-linea-fuerte); }
@media (prefers-reduced-motion: reduce) {
  .pos-kpi, .pos-lista-mini, .pos-grafico-barras { transition: none; }
  .pos-kpi:hover, .pos-lista-mini:hover, .pos-grafico-barras:hover { transform: none; }
}

/* ═══════════════════════════════════════════════════════════════════════
   V4 · TARJETA DE PRODUCTO CON FOTO: FOTO ARRIBA, FRANJA BLANCA DEBAJO
   ───────────────────────────────────────────────────────────────────────
   Hallazgo del revisor de diseño (15/09/2026): nuestra tarjeta con foto
   usaba el patrón CONTRARIO al de las seis referencias — foto a sangre con
   un degradado negro encima y el texto sobre la imagen. En las referencias
   la foto ocupa la mitad de arriba y el nombre y el precio van en una
   franja blanca debajo, siempre legibles: el degradado depende de cómo sea
   la foto, y una tarta clara con texto crema encima no se lee.

   Hoy NINGÚN producto del catálogo real tiene foto (comprobado sobre el
   pull de verdad: 458 productos, 0 con `imagen_url`), así que esto no
   cambia nada de lo que se ve — pero el día que Joni suba las fotos, la
   tarjeta ya nace bien en vez de estrenarse con el patrón malo.
   ═══════════════════════════════════════════════════════════════════════ */
.pos-prod--foto {
  padding: 0;
  justify-content: flex-start;
  overflow: hidden;
  background: var(--pos-v3-carta);
}
/* La foto deja de estar a sangre: ocupa la mitad de arriba, en flujo. */
.pos-prod--foto .foto {
  position: relative;
  inset: auto;
  width: 100%;
  height: 56%;
  flex: 0 0 56%;
  object-fit: cover;
  display: block;
  z-index: 0;
}
/* Fuera el degradado: ya no hay texto encima de la imagen. */
.pos-prod--foto::after { content: none; }
.pos-prod--foto .n,
.pos-prod--foto .p {
  position: relative;
  z-index: 2;
  color: var(--pos-v3-texto);
  padding: 0 10px;
}
.pos-prod--foto .n { padding-top: 8px; }
.pos-prod--foto .p { padding-bottom: 8px; color: var(--mrj-orange); }
/* Con franja blanca ya hay sitio para la variante, que antes se ocultaba
   por no tener dónde ponerla sobre la foto. */
.pos-prod--foto .v { display: block; padding: 0 10px; color: var(--pos-v3-suave); }

/* ═══════════════════════════════════════════════════════════════════════
   MODO PRUEBAS — la banda que no se puede ignorar
   ───────────────────────────────────────────────────────────────────────
   Joni, 15/09/2026: la app se pone online «en modo pruebas para ir
   probando... es importante esto para que no se sumen ni ventas ni nada».

   El servidor ya corta de verdad lo que importa (no manda nada al OS ni a
   las impresoras). Esto es la otra mitad: que la persona que está delante
   del mostrador lo sepa de un vistazo, sin tener que acordarse de en qué
   dirección entró.

   Empuja la pantalla hacia abajo en vez de flotar encima: una banda que
   tapa algo es una banda que alguien acaba queriendo quitar. Y no lleva
   botón de cerrar a propósito.
   ═══════════════════════════════════════════════════════════════════════ */
.pos-banda-pruebas {
  display: flex;
  align-items: baseline;
  justify-content: center;
  flex-wrap: wrap;
  gap: 4px 12px;
  padding: 7px 16px;
  background: var(--pos-v3-alerta-tx, #B4401A);
  color: #FFF4EE;
  text-align: center;
  line-height: 1.35;
}
.pos-banda-pruebas b {
  font-family: var(--pos-font-display);
  font-weight: var(--pos-peso-titulo, 700);
  font-size: 13px;
  letter-spacing: .14em;
  text-transform: uppercase;
}
.pos-banda-pruebas span { font-size: 12.5px; opacity: .92; }
@media (max-width: 640px) {
  .pos-banda-pruebas span { display: none; } /* en el móvil basta el rótulo */
}
