/* public/css/app.css — TODO el estilo de la libreta.
 *
 * Salió del bloque <style> de public/index.html el 28-oct, tal cual:
 * ni una regla cambiada, ni una reordenada. Era mudanza, no rediseño.
 *
 * El orden IMPORTA y es el de siempre: la cascada manda, y hay capas al
 * final de este archivo que ganan a propósito por venir después (busca
 * "va al final"). Si algún día esto se parte en varios archivos, tienen
 * que enlazarse EN ESTE MISMO ORDEN.
 *
 * Lo que NO está aquí: el <style> que se arma dentro del JavaScript para
 * la ventana de impresión del recibo, y la hoja de variables del tema que
 * se inyecta desde app.js. Esos viven donde viven porque se generan.
 */
    /* ── Reset & Variables ─────────────────────────────────────────────── */
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

    /* ══════════════════════════════════════════════════════════════════
       LAS DOS ESCALAS

       Antes de esto había 121 tamaños de letra y 48 valores de espaciado:
       medio píxel de diferencia entre uno y otro, que nadie ve como una
       intención, y todos los enteros del 1 al 20 en los rellenos. Un
       7px no es una decisión, es lo que salió.

       La raíz es 18px (html{font-size:18px}), así que TODO se escribe en
       px: un rem aquí vale 18, y la mitad de los rem del proyecto estaban
       calculados contra 16 — el número decía una cosa y la pantalla otra.
       En px no hay nada que interpretar.
       ══════════════════════════════════════════════════════════════════ */
    :root {
      /* Texto: ocho escalones, de 10 a 36.

         Elegidos midiendo los 10.000 elementos de las 56 hojas, no a ojo.
         Con esta escala NINGUNO cambia de tamaño más de un 11%, y el peor
         caso es un rotulillo de 9px que pasa a 10.

         Con 12/14/16/18/22/30/48/92 se movían 461 elementos más de un 15%,
         por dos agujeros: no había peldaño abajo (los rotulillos de 9 y
         10px saltaban a 12, un 33% más grandes) y no había nada entre 30 y
         48 (las cifras grandes, que van de 34 a 40px, se partían en dos:
         unas bajaban a 30 y otras subían a 48 — 18px entre dos cosas que
         hoy se ven iguales). */
      --fs-micro:    10px;
      --fs-xs:       12px;
      --fs-s:        14px;
      --fs-m:        16px;
      --fs-base:     18px;
      --fs-l:        22px;
      --fs-xl:       28px;
      --fs-2xl:      36px;

      /* Interlineado: tres. "plano" es para lo que ocupa una línea sola
         (botones, iconos, insignias), donde cualquier otra cosa cambia la
         altura de la pieza. */
      --lh-plano:    1;
      --lh-titulo:   1.2;
      --lh-cuerpo:   1.45;

      /* Tracking: tres, en em, que es lo correcto — acompaña al tamaño. */
      --track-sutil:   0.02em;
      --track-etiqueta: 0.06em;
      --track-ancho:   0.12em;

      /* Espaciado: ocho escalones sobre base de 4. */
      --esp-1:       4px;
      --esp-2:       8px;
      --esp-3:       12px;
      --esp-4:       16px;
      --esp-5:       24px;
      --esp-6:       32px;
      --esp-7:       48px;
      --esp-8:       64px;
    }
    :root {
      /* ═══════════════════════════════════════════════════════════════
         EL ORDEN DE LAS CAPAS — quién tapa a quién, en un solo sitio
      ═══════════════════════════════════════════════════════════════════

         De abajo arriba. Cada número vive AQUÍ y sólo aquí: si una capa
         nueva necesita colarse entre dos, se le pone nombre en esta lista
         y no se improvisa un 9999 suelto en mitad de la hoja.

           480     un panel dentro de una pantalla (#bg-panel)
           500     las pantallas que se abren encima (los "overlay")
           520     pendientes de pago, que se abre desde varias
           550     historial de cambios
           560     cédula/cliente, que sale desde registrar
           600     pedir la contraseña
           700     pedir la contraseña dentro de registrar
          1000     la pantalla de entrada
          9000     el VISOR DE FOTOS: encima de cualquier pantalla, porque
                   se abre desde dentro de ellas
          9500     el globo de ayuda
          9800     el cartel del turno abierto
          9850     el cartel del cobro de WispHub
          9999     avisos que no tapan nada importante (toast, versión)
         10000     la portada de bienvenida
        100000     la sesión bloqueada — por encima de todo, siempre

         ── LA TRAMPA QUE COSTÓ UN FALLO ─────────────────────────────
         #app lleva `position:relative; z-index:1` (para quedar sobre el
         fondo de la página). Eso lo convierte en un MUNDO APARTE: un
         elemento de dentro, por mucho z-index que declare, nunca sube por
         encima de otro que viva fuera. El visor de fotos estaba dentro de
         #app con z-index 99999 y aun así se abría POR DETRÁS del detalle
         de una transacción, que es un overlay de 500 colgado del body.

         Regla, entonces: todo lo que tenga que taparlo TODO va colgado
         del <body>, nunca de #app. (Queda un caso vivo del mismo tipo:
         .disp-modal-backdrop declara 9999 dentro de #app, así que su 9999
         no vale fuera. No molesta porque no se abre junto a nada de
         fuera, pero si algún día hay que sacarlo, es por esto.)
      */
      --capa-panel:          480;
      --capa-modal:          500;
      --capa-modal-pend:     520;
      /* El comprobante que espera OK: por encima del detalle de una
         pendiente (se puede estar mirando esa ventana cuando sale el
         cartel) y por debajo del visor de fotos, para que la foto se
         pueda acercar sobre él. */
      --capa-modal-ok:       540;
      --capa-modal-hist:     550;
      --capa-modal-ci:       560;
      --capa-modal-clave:    600;
      --capa-login:         1000;
      --capa-chat:          8800;
      --capa-visor:         9000;
      --capa-ayuda:         9500;
      --capa-cartel-turno:  9800;
      --capa-cartel-cobro:  9850;
      --capa-aviso:         9999;
      --capa-intro:        10000;
      --capa-bloqueo:     100000;

      /* ── Paleta nueva (dark + oro) ───────────────────────────────── */
      --bg-base:        #0a0a0c;
      --bg-elevated:    #16161a;
      --bg-card:        #1a1a20;
      --bg-input:       #0f0f13;
      --bg-overlay:     rgba(10, 10, 12, 0.85);

      --bg-gold-start:  #2a1f0d;
      --bg-gold-end:    #3d2e10;
      --bg-gold-grad:   linear-gradient(135deg, #2a1f0d 0%, #3d2e10 100%);

      --border-subtle:  rgba(255, 255, 255, 0.06);
      --border-soft:    rgba(255, 255, 255, 0.10);
      --border-strong:  rgba(255, 255, 255, 0.18);
      --border-accent:  #3b6a96;
      --border-gold:    rgba(201, 169, 97, 0.35);

      --gold:           #c9a961;
      --gold-bright:    #e5c878;
      --gold-dim:       #8a7842;
      --rose:           #b88a8a;
      --rose-bg:        #2a1818;
      --cyan-accent:    #5a8fb8;

      --positive:       #5ab574;
      --positive-bg:    rgba(90, 181, 116, 0.12);
      --negative:       #d97474;
      --negative-bg:    rgba(217, 116, 116, 0.12);
      --warning:        #e5a05e;

      --text-primary:   #ffffff;
      --text-secondary: #c0c0c8;
      --text-muted:     #8a8a92;
      --text-dim:       #5a5a64;
      --text-on-gold:   #f5e8c8;

      --font-serif:     'Playfair Display', Georgia, serif;
      --font-sans:      'Inter', -apple-system, system-ui, sans-serif;

      --radius-sm:      8px;
      --radius-md:      14px;
      --radius-lg:      20px;
      --shadow-card:    0 1px 3px rgba(0,0,0,0.4), 0 4px 12px rgba(0,0,0,0.2);
      --shadow-gold:    0 4px 24px rgba(201, 169, 97, 0.12);

      /* ── Aliases heredados (legacy claritas) ─────────────────────────
         Las reglas viejas siguen usando estos nombres; aquí los re-mapeo
         al tema oscuro para evitar tocar ~100 referencias una por una. */
      --rose-light:  var(--bg-base);
      --rose-mid:    var(--bg-elevated);
      --rose-deep:   var(--gold);
      --rose-med:    var(--gold);
      --gold-light:  var(--gold-bright);
      --gold-dark:   var(--gold-dim);
      --text-dark:   var(--text-primary);
      --text-med:    var(--text-secondary);
      --white:       var(--bg-card);
      --border:      var(--border-subtle);
      --radius:      var(--radius-md);
      --shadow:      var(--shadow-card);
      --shadow-lg:   var(--shadow-card);

      /* Layout (sin cambios) */
      --topbar-h:    64px;
    }

    html, body { height: 100%; overflow: hidden; }

    body {
      font-family: var(--font-sans);
      background: var(--rose-light);
      color: var(--text-dark);
      font-size: var(--fs-s);
      display: flex;
    }

    /* ── Scrollbar ──────────────────────────────────────────────────────── */
    ::-webkit-scrollbar { width: 6px; height: 6px; }
    ::-webkit-scrollbar-track { background: transparent; }
    ::-webkit-scrollbar-thumb { background: var(--rose-deep); border-radius: 99px; }

    /* ═══════════════════════════════════════════════════════════════════
       SIDEBAR
    ══════════════════════════════════════════════════════════════════════ */

    .nav-item {
      display: flex;
      align-items: center;
      gap: var(--esp-2);
      padding: var(--esp-2) var(--esp-3);
      border-radius: var(--radius-sm);
      cursor: pointer;
      color: rgba(255,255,255,0.6);
      font-size: var(--fs-m);
      font-weight: 400;
      transition: all 0.18s;
      margin-bottom: var(--esp-1);
      user-select: none;
    }
    .nav-item:hover {
      background: rgba(232,160,176,0.1);
      color: rgba(255,255,255,0.9);
    }
    .nav-item.active {
      background: linear-gradient(135deg, rgba(232,160,176,0.25), rgba(201,169,110,0.15));
      color: var(--white);
      font-weight: 500;
    }
    .nav-item.active .nav-icon { color: var(--rose-deep); }
    .nav-icon {
      width: 18px;
      text-align: center;
      font-size: var(--fs-m);
      flex-shrink: 0;
      color: rgba(255,255,255,0.4);
      transition: color 0.18s;
    }

    /* Badge dorado para contador (pendientes por autorización). */
    .nav-badge {
      margin-left: auto;
      background: var(--bg-gold-grad);
      border: 1px solid var(--gold);
      color: var(--gold-bright);
      font-size: var(--fs-xs);
      font-weight: 700;
      padding: var(--esp-1) var(--esp-2);
      border-radius: 999px;
      min-width: 22px;
      text-align: center;
      box-shadow: 0 0 0 2px rgba(201,169,97,0.18);
    }

    /* ── Vista Disponibles (saldos por persona) ────────────────────── */
    .disp-persona-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
      gap: var(--esp-3);
      margin-bottom: var(--esp-5);
    }
    /* Etiqueta de grupo de cuentas por moneda (Bolívares / USDT) */
    .disp-group-label {
      font-size: var(--fs-xs);
      font-weight: 700;
      letter-spacing: var(--track-etiqueta);
      text-transform: uppercase;
      color: var(--text-muted);
      margin: var(--esp-1) 0 var(--esp-2);
    }
    .disp-persona-card.usdt { border-color: rgba(90,181,116,.45); }
    .disp-persona-card {
      background: var(--bg-card);
      border: 1px solid var(--border-gold);
      border-radius: var(--radius);
      padding: var(--esp-4) var(--esp-4);
      cursor: pointer;
      transition: transform .15s, border-color .15s, background .15s;
      position: relative;
    }
    .disp-persona-card:hover { transform: translateY(-1px); }
    /* Cuenta desactivada: se sigue viendo y se puede abrir (para consultarla
       o volver a activarla), pero apagada, para que no se confunda con las
       que se usan hoy. */
    .disp-persona-card.inactiva {
      opacity: .55;
      border-style: dashed;
      border-color: var(--linea);
    }
    .disp-persona-card.inactiva:hover { opacity: .8; }
    /* Y si se abre una desactivada para mirarla, se marca que está abierta
       sin encenderla: el dorado macizo de .selected la haría parecer la
       cuenta con la que se está trabajando, que es justo lo contrario. */
    /* Al abrirla se le quita el apagado y se la deja con el mismo realce que
       cualquier otra card abierta: el texto de una card seleccionada está
       pensado para ese fondo dorado, y sobre uno apagado se vuelve
       ilegible. Lo que dice que está fuera de uso es su rótulo
       "Desactivada", que se sigue leyendo. */
    .disp-persona-card.inactiva.selected { opacity: 1; }
    .disp-persona-card.selected {
      background: var(--oro-macizo);
      border-color: var(--gold);
      box-shadow: 0 0 0 1px var(--gold);
    }
    .disp-persona-card.principal {
      border-style: dashed;
      border-color: var(--gold-bright);
    }
    .disp-persona-card.principal.selected {
      border-style: solid;
    }
    .disp-persona-card-badge {
      display: inline-block;
      background: rgba(229,200,120,0.18);
      color: var(--gold-bright);
      font-size: var(--fs-xs);
      font-weight: 600;
      letter-spacing: var(--track-etiqueta);
      text-transform: uppercase;
      padding: var(--esp-1) var(--esp-2);
      border-radius: 999px;
      margin-bottom: var(--esp-1);
    }
    .disp-persona-card-sub {
      font-size: var(--fs-xs);
      color: var(--text-muted);
      text-transform: uppercase;
      letter-spacing: var(--track-etiqueta);
      margin-top: var(--esp-1);
    }
    .disp-persona-card-name {
      font-family: var(--font-serif);
      font-style: italic;
      color: var(--gold-bright);
      font-size: var(--fs-m);
      margin-bottom: var(--esp-1);
    }
    .disp-persona-card-saldo {
      font-family: var(--font-sans);
      font-weight: 700;
      color: var(--text-primary);
      font-size: var(--fs-base);
      letter-spacing: -0.01em;
    }
    .disp-persona-card-saldo small {
      font-size: var(--fs-xs);
      color: var(--text-muted);
      font-weight: 400;
      margin-right: var(--esp-1);
    }
    .disp-edit-btn {
      position: absolute;
      top: 8px;
      right: 8px;
      width: 24px;
      height: 24px;
      padding: 0;
      border: 1px solid var(--border-soft);
      background: var(--bg-elevated);
      color: var(--text-muted);
      border-radius: 6px;
      font-size: var(--fs-s);
      line-height: var(--lh-plano);
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    .disp-edit-btn:hover { color: var(--gold-bright); border-color: var(--border-gold); }
    .disp-del-btn {
      position: absolute;
      top: 8px;
      right: 38px;
      width: 24px;
      height: 24px;
      padding: 0;
      border: 1px solid var(--border-soft);
      background: var(--bg-elevated);
      color: var(--text-muted);
      border-radius: 6px;
      font-size: var(--fs-s);
      line-height: var(--lh-plano);
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    .disp-del-btn:hover { color: var(--negative); border-color: var(--negative); }
    .disp-persona-card-add {
      background: transparent;
      border: 1px dashed var(--border-soft);
      border-radius: var(--radius);
      padding: var(--esp-4) var(--esp-4);
      cursor: pointer;
      color: var(--text-muted);
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      gap: var(--esp-1);
      min-height: 84px;
      transition: color .15s, border-color .15s;
    }
    .disp-persona-card-add:hover { color: var(--gold-bright); border-color: var(--border-gold); }
    .disp-persona-card-add .add-plus { font-size: var(--fs-l); line-height: var(--lh-plano); }
    .disp-persona-card-add .add-label { font-size: var(--fs-s); }
    .disp-hero {
      background: var(--bg-gold-grad);
      border: 1px solid var(--border-gold);
      border-radius: var(--radius);
      padding: var(--esp-5) var(--esp-6);
      position: relative;
      display: flex;
      flex-direction: column;
      align-items: flex-start;
    }
    .disp-hero-label {
      font-family: var(--font-serif);
      font-style: italic;
      color: var(--gold);
      font-size: var(--fs-s);
      letter-spacing: var(--track-ancho);
      margin-bottom: var(--esp-2);
    }
    .disp-hero-value {
      font-family: var(--font-sans);
      font-weight: 700;
      color: var(--text-primary);
      font-size: var(--fs-2xl);
      line-height: var(--lh-plano);
      letter-spacing: -0.02em;
    }
    .disp-hero-name {
      font-family: var(--font-serif);
      font-style: italic;
      color: var(--gold-bright);
      font-size: var(--fs-m);
      margin-top: var(--esp-2);
    }
    .disp-hero-btn {
      position: absolute;
      top: 20px;
      right: 20px;
    }
    .disp-kpis {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: var(--esp-3);
      margin-top: var(--esp-4);
    }
    .disp-kpi {
      background: var(--bg-card);
      border: 1px solid var(--border-gold);
      border-radius: var(--radius);
      padding: var(--esp-3) var(--esp-4);
    }
    .disp-kpi-label {
      font-size: var(--fs-xs);
      color: var(--text-muted);
      text-transform: uppercase;
      letter-spacing: var(--track-etiqueta);
      margin-bottom: var(--esp-1);
    }
    .disp-kpi-value {
      font-family: var(--font-sans);
      font-weight: 700;
      font-size: var(--fs-base);
    }
    /* Modal recarga */
    .disp-modal-backdrop {
      position: fixed;
      inset: 0;
      background: rgba(0,0,0,0.6);
      display: flex;
      align-items: center;
      justify-content: center;
      z-index: var(--capa-aviso);          /* OJO: vive dentro de #app, así que este 9999 no vale fuera. Ver EL ORDEN DE LAS CAPAS. */
      padding: var(--esp-4);
    }
    .disp-modal {
      background: var(--bg-card);
      border: 1px solid var(--border-gold);
      border-radius: var(--radius);
      padding: var(--esp-5) var(--esp-5);
      width: 100%;
      max-width: 440px;
      box-shadow: 0 20px 60px rgba(0,0,0,0.5);
    }
    .disp-modal-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: var(--esp-4);
    }
    .disp-modal-title {
      font-family: var(--font-serif);
      font-style: italic;
      color: var(--gold-bright);
      font-size: var(--fs-base);
    }
    .disp-modal-title span { color: var(--text-primary); font-style: normal; }
    .disp-modal-close {
      background: transparent;
      border: none;
      color: var(--text-muted);
      cursor: pointer;
      font-size: var(--fs-base);
      line-height: var(--lh-plano);
    }
    .disp-modal-close:hover { color: var(--gold); }
    /* Registro de Ingresos — botones de categoría */
    .ing-cat-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
      gap: var(--esp-3);
      margin-top: var(--esp-2);
    }
    .ing-cat-btn {
      background: var(--bg-card);
      border: 1px solid var(--border-gold);
      border-radius: var(--radius);
      padding: var(--esp-5) var(--esp-4);
      cursor: pointer;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      gap: var(--esp-3);
      color: inherit;
      text-align: center;
      font-family: inherit;
      transition: transform .15s, border-color .15s, background .15s;
    }
    .ing-cat-btn:hover {
      transform: translateY(-1px);
      border-color: var(--gold);
    }
    .ing-cat-btn.selected {
      background: var(--bg-gold-grad);
      border-color: var(--gold);
      box-shadow: 0 0 0 1px var(--gold);
    }
    .ing-cat-icon {
      font-size: var(--fs-xl);
      color: var(--gold-bright);
      line-height: var(--lh-plano);
    }
    /* Categorías creadas desde la app: su ícono es un SVG del catálogo. */
    .ing-cat-icon svg {
      width: 30px;
      height: 30px;
      display: block;
      stroke: var(--gold-bright);
      color: var(--gold-bright);
    }
    .ing-cat-label {
      font-family: var(--font-serif);
      font-style: italic;
      color: var(--text-primary);
      font-size: var(--fs-base);
    }

    /* ── Pago Móvil (detalle dentro de Registro de Ingresos) ─────────── */
    .pm-header {
      display: flex;
      align-items: center;
      gap: var(--esp-4);
      margin-bottom: var(--esp-4);
    }
    .pm-header h3 {
      font-family: var(--font-serif);
      font-style: italic;
      color: var(--gold-bright);
      font-size: var(--fs-base);
      margin: 0;
    }
    .pm-total {
      margin-left: auto;
      padding: var(--esp-1) var(--esp-3);
      background: var(--bg-gold-grad);
      border: 1px solid var(--border-gold);
      border-radius: 999px;
      color: var(--gold-bright);
      font-weight: 600;
      font-size: var(--fs-m);
      white-space: nowrap;
    }
    .pm-resumen {
      display: flex;
      flex-wrap: wrap;
      gap: var(--esp-2) var(--esp-3);
      align-items: baseline;
      padding: var(--esp-2) var(--esp-3);
      margin-bottom: var(--esp-3);
      background: rgba(201,169,97,0.06);
      border: 1px solid var(--border-gold);
      border-left-width: 3px;
      border-radius: 8px;
      font-size: var(--fs-m);
    }
    .pm-resumen-item { display: inline-flex; gap: var(--esp-1); align-items: baseline; }
    .pm-resumen-k { color: var(--text-muted); font-weight: 600; }
    .pm-resumen-v { color: var(--text-primary); font-weight: 600; }
    .pm-resumen-v.gold {
      color: var(--gold-bright);
      font-family: var(--font-sans);
      font-weight: 700;
      font-size: var(--fs-m);
      letter-spacing: var(--track-sutil);
      font-variant-numeric: tabular-nums;
    }
    .pm-resumen-sep { color: var(--text-dim); }
    .pm-filters {
      display: flex;
      flex-wrap: wrap;
      align-items: end;
      gap: var(--esp-3);
      padding: var(--esp-3) var(--esp-4);
      background: var(--bg-card);
      border: 1px solid var(--border-gold);
      border-radius: var(--radius);
      margin-bottom: var(--esp-4);
    }
    .pm-filters .pm-field { display: flex; flex-direction: column; gap: var(--esp-1); }
    .pm-filters label {
      font-size: var(--fs-xs);
      color: var(--text-muted);
      text-transform: uppercase;
      letter-spacing: var(--track-etiqueta);
    }
    .pm-filters input[type="date"] {
      background: var(--bg-dark, #0d0d12);
      border: 1px solid var(--border-gold);
      color: var(--text-primary);
      padding: var(--esp-1) var(--esp-2);
      border-radius: 6px;
      font-family: var(--font-sans);
      font-size: var(--fs-m);
      color-scheme: dark;
    }
    .pm-filters input[type="date"]:focus { border-color: var(--gold); }

    /* Filtro de fecha: "Un día" o "Por rango", nunca los dos a la vez. Los
       botones son los MISMOS de Informes (.inf-modo), en chico: el operador
       ya conoce el gesto y no tiene que aprender otro. El lado que no manda
       queda atenuado y desactivado — no se puede rellenar por error ni dejar
       un valor viejo decidiendo por él. */
    .pm-filters .inf-modos { margin-bottom: 0; gap: var(--esp-1); }
    .pm-filters .inf-modo { font-size: var(--fs-xs); padding: var(--esp-1) var(--esp-3); }
    .pm-field.apagado { opacity: .45; }
    .pm-field.apagado input[type="date"] { cursor: not-allowed; }
    .pm-split {
      display: grid;
      grid-template-columns: 1fr 360px;
      gap: var(--esp-4);
      align-items: start;
    }
    @media (max-width: 900px) {
      .pm-split { grid-template-columns: 1fr; }
    }
    .pm-table-wrap {
      background: var(--bg-card);
      border: 1px solid var(--border-gold);
      border-radius: var(--radius);
      overflow: hidden;
    }
    .pm-table-wrap table {
      width: 100%;
      border-collapse: collapse;
    }
    .pm-table-wrap thead th {
      text-align: left;
      font-size: var(--fs-xs);
      color: var(--text-muted);
      text-transform: uppercase;
      letter-spacing: var(--track-etiqueta);
      padding: var(--esp-2) var(--esp-3);
      background: rgba(0,0,0,0.25);
      border-bottom: 1px solid var(--border-gold);
    }
    .pm-table-wrap tbody tr {
      border-bottom: 1px solid rgba(201,169,97,0.10);
      cursor: pointer;
      transition: background .15s, border-color .15s;
    }
    .pm-table-wrap tbody tr:hover { background: rgba(201,169,97,0.04); }
    .pm-table-wrap tbody tr.selected {
      background: rgba(201,169,97,0.08);
      box-shadow: inset 2px 0 0 var(--gold);
    }
    .pm-table-wrap td {
      padding: var(--esp-2) var(--esp-3);
      font-size: var(--fs-m);
      color: #e5e5e5;
    }
    .pm-panel {
      background: var(--bg-card);
      border: 1px solid var(--border-gold);
      border-radius: var(--radius);
      padding: var(--esp-4) var(--esp-4);
      position: sticky;
      top: 16px;
    }
    .pm-panel-empty {
      color: var(--text-muted);
      font-size: var(--fs-m);
      text-align: center;
      padding: var(--esp-5) var(--esp-3);
    }
    .pm-panel h4 {
      font-family: var(--font-serif);
      font-style: italic;
      color: var(--gold-bright);
      font-size: var(--fs-m);
      margin: 0 0 var(--esp-3) 0;
    }
    .pm-panel .pm-field-row {
      display: flex;
      justify-content: space-between;
      gap: var(--esp-3);
      padding: var(--esp-1) 0;
      border-bottom: 1px dashed rgba(201,169,97,0.12);
      font-size: var(--fs-m);
    }
    .pm-panel .pm-field-row:last-of-type { border-bottom: none; }
    .pm-panel .pm-field-label {
      color: var(--text-muted);
      text-transform: uppercase;
      letter-spacing: var(--track-etiqueta);
      font-size: var(--fs-xs);
    }
    .pm-panel .pm-field-value {
      color: var(--text-primary);
      font-family: var(--font-sans);
      text-align: right;
      word-break: break-word;
    }
    .pm-thumb {
      display: block;
      max-width: 100%;
      width: 120px;
      height: 90px;
      object-fit: cover;
      border-radius: 6px;
      border: 1px solid var(--border-gold);
      cursor: pointer;
      margin: 0 auto var(--esp-3) auto;
    }
    .pm-thumb:hover { border-color: var(--gold); }
    .pm-no-photo {
      text-align: center;
      color: var(--text-muted);
      font-size: var(--fs-s);
      padding: var(--esp-4);
      border: 1px dashed rgba(201,169,97,0.2);
      border-radius: 6px;
      margin-bottom: var(--esp-3);
    }
    .pm-edit-btn {
      margin-top: var(--esp-3);
      width: 100%;
      padding: var(--esp-2) var(--esp-3);
      background: transparent;
      border: 1px solid var(--border-gold);
      color: var(--gold-bright);
      border-radius: 6px;
      font-family: inherit;
      cursor: pointer;
    }
    .pm-edit-btn[disabled] {
      opacity: 0.5;
      cursor: not-allowed;
    }
    .pm-delete-btn {
      margin-top: var(--esp-2);
      width: 100%;
      padding: var(--esp-2) var(--esp-3);
      background: transparent;
      border: 1px solid var(--negative);
      color: var(--negative);
      border-radius: 6px;
      font-family: inherit;
      cursor: pointer;
      transition: background .12s;
    }
    .pm-delete-btn:hover {
      background: rgba(215,119,119,0.08);
    }
    .pm-edit-row {
      display: flex;
      flex-direction: column;
      gap: var(--esp-1);
      margin-top: var(--esp-3);
    }
    .pm-edit-row .pm-edit-btn { margin-top: 0; }
    .pm-history-link {
      display: inline-block;
      align-self: center;
      color: var(--text-secondary);
      text-decoration: underline;
      cursor: pointer;
      font-size: var(--fs-s);
    }
    .pm-history-link:hover { color: var(--gold-bright); }

    /* ── Lote B: modales de edición / autorización / historial ────────── */
    .edit-form-grid {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: var(--esp-3);
    }
    .edit-form-grid .form-group.full { grid-column: 1 / -1; }
    .edit-thumb-wrap {
      display: flex;
      gap: var(--esp-3);
      margin-bottom: var(--esp-3);
      flex-wrap: wrap;
    }
    .edit-thumb-wrap img {
      width: 90px;
      height: 90px;
      object-fit: cover;
      border-radius: 6px;
      border: 1px solid var(--border-soft);
    }
    .edit-thumb-note {
      font-size: var(--fs-xs);
      color: var(--text-muted);
      align-self: center;
    }
    #auth-overlay .modal {
      border-top: 3px solid var(--warning);
      max-width: 380px;
    }
    #auth-overlay .auth-note {
      color: var(--warning);
      font-size: var(--fs-s);
      margin-bottom: var(--esp-3);
    }
    .audit-table {
      width: 100%;
      border-collapse: collapse;
      font-size: var(--fs-s);
    }
    .audit-table th, .audit-table td {
      padding: var(--esp-2) var(--esp-2);
      border-bottom: 1px solid var(--border-subtle);
      text-align: left;
      vertical-align: top;
    }
    .audit-table th {
      color: var(--text-muted);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: var(--track-etiqueta);
      font-size: var(--fs-xs);
    }
    .audit-table .val-anterior { color: var(--negative); }
    .audit-table .val-nuevo    { color: var(--positive); }
    .audit-empty {
      padding: var(--esp-5);
      text-align: center;
      color: var(--text-muted);
    }
    .toast {
      position: fixed;
      bottom: 24px;
      left: 50%;
      transform: translateX(-50%);
      background: var(--bg-elevated);
      color: var(--positive);
      border: 1px solid var(--border-soft);
      border-radius: var(--radius-md);
      padding: var(--esp-2) var(--esp-4);
      box-shadow: var(--shadow-card);
      z-index: var(--capa-aviso);
      font-size: var(--fs-m);
      opacity: 0;
      transition: opacity .25s ease;
      pointer-events: none;
    }
    .toast.show { opacity: 1; }
    .toast.error { color: var(--negative); border-color: var(--negative-bg); }

    /* ── Lote C: admin-only + panel de precios + estado discrepancia ── */
    body:not(.is-admin) .admin-only { display: none !important; }
    body.is-admin .operator-only { display: none !important; }
    .acceso-restringido {
      max-width: 480px;
      margin: var(--esp-8) auto;
      padding: var(--esp-6);
      background: var(--bg-card);
      border: 1px solid var(--border-soft);
      border-radius: var(--radius-md);
      text-align: center;
    }
    .acceso-restringido h3 { color: var(--warning); margin: 0 0 var(--esp-3); }
    .acceso-restringido p  { color: var(--text-muted); margin: 0; }

    .precios-wrap { max-width: 920px; }
    .precios-wrap h3 { margin: 0 0 var(--esp-1); }
    .precios-wrap .precios-sub {
      color: var(--text-muted);
      font-size: var(--fs-m);
      margin-bottom: var(--esp-5);
    }

    /* ── Panel jerárquico de Precios (Sprint lunes 8 jun) ──────────────── */
    .precios-list {
      display: flex;
      flex-direction: column;
      gap: var(--esp-3);
    }
    .precios-card {
      background: var(--bg-card);
      border: 1px solid var(--border-subtle);
      border-left: 3px solid var(--gold-dim);
      border-radius: var(--radius-md);
      overflow: hidden;
    }
    .precios-card.expanded { border-left-color: var(--gold-bright); }
    .precios-empty-folder {
      padding: var(--esp-2) 0;
      color: var(--text-muted);
      font-size: var(--fs-m);
      font-style: italic;
    }
    .precios-card-head {
      display: flex;
      align-items: center;
      gap: var(--esp-3);
      padding: var(--esp-3) var(--esp-4);
      cursor: pointer;
      transition: background .15s;
    }
    .precios-card-head:hover { background: var(--bg-elevated); }
    .precios-card-caret {
      width: 18px;
      color: var(--gold-dim);
      font-size: var(--fs-s);
      transition: transform .15s;
      display: inline-block;
      text-align: center;
    }
    .precios-card.expanded .precios-card-caret { transform: rotate(90deg); }
    .precios-card-titles {
      flex: 1;
      min-width: 0;
    }
    .precios-card-title {
      font-size: var(--fs-base);
      font-weight: 600;
      color: var(--text-primary);
    }
    .precios-card-sub {
      font-size: var(--fs-s);
      color: var(--text-muted);
      margin-top: var(--esp-1);
    }
    .precios-card-actions {
      display: flex;
      gap: var(--esp-1);
    }
    .precios-card-actions .btn-mini {
      padding: var(--esp-1) var(--esp-2);
      font-size: var(--fs-s);
      background: var(--bg-elevated);
      border: 1px solid var(--border-soft);
      border-radius: 6px;
      color: var(--text-primary);
      cursor: pointer;
    }
    .precios-card-actions .btn-mini:hover { background: var(--bg-card); }
    .precios-card-actions .btn-mini.danger {
      color: var(--negative);
      border-color: rgba(215,119,119,0.35);
    }
    .precios-card-body {
      display: none;
      padding: var(--esp-1) var(--esp-4) var(--esp-3) var(--esp-7);
      border-top: 1px solid var(--border-subtle);
    }
    .precios-card.expanded .precios-card-body { display: block; }
    .precios-subitem-row {
      display: flex;
      align-items: center;
      gap: var(--esp-2);
      padding: var(--esp-2) 0;
      border-bottom: 1px dashed var(--border-subtle);
    }
    .precios-subitem-row:last-child { border-bottom: none; }
    .precios-subitem-name {
      flex: 1;
      font-size: var(--fs-m);
    }
    .precios-subitem-price {
      font-family: var(--font-serif);
      color: var(--gold-bright);
      font-size: var(--fs-base);
      min-width: 80px;
      text-align: right;
    }
    .precios-add-sub {
      margin-top: var(--esp-2);
      padding: var(--esp-1) var(--esp-3);
      font-size: var(--fs-s);
      background: transparent;
      border: 1px dashed var(--border-soft);
      border-radius: 6px;
      color: var(--gold-bright);
      cursor: pointer;
    }
    .precios-add-sub:hover { background: var(--bg-elevated); }

    .badge {
      display: inline-block;
      padding: var(--esp-1) var(--esp-2);
      border-radius: 999px;
      font-size: var(--fs-xs);
      font-weight: 600;
      letter-spacing: var(--track-sutil);
    }
    .badge-ok    { background: var(--positive-bg); color: var(--positive); }
    .badge-disc  { background: var(--negative-bg); color: var(--negative); }
    /* "Ponle el ojo": ni bien ni mal. Hoy lo usa la mensualidad que entró
       por el céntimo de tolerancia, y sólo lo ve el admin. */
    .badge-ojo   { background: rgba(229, 160, 94, 0.14); color: var(--warning);
                   cursor: help; }

    .sv-total-card {
      background: var(--bg-gold-grad);
      border: 1px solid var(--border-gold);
      border-radius: var(--radius-md);
      padding: var(--esp-4) var(--esp-5);
      margin-bottom: var(--esp-4);
      box-shadow: var(--shadow-gold);
    }
    .sv-total-card .sv-total-label {
      font-size: var(--fs-xs);
      letter-spacing: var(--track-ancho);
      text-transform: uppercase;
      color: var(--gold-bright);
      margin-bottom: var(--esp-1);
    }
    .sv-total-card .sv-total-value {
      font-family: var(--font-serif);
      font-size: var(--fs-xl);
      color: var(--gold-bright);
    }
    .sv-disc-alert {
      background: var(--negative-bg);
      border: 1px solid var(--negative);
      color: var(--negative);
      padding: var(--esp-2) var(--esp-3);
      border-radius: 6px;
      margin: var(--esp-3) 0;
      font-size: var(--fs-m);
    }
    /* Mismo cuadro, en ámbar: no es un descuadre, es un "míralo". */
    .sv-disc-alert.sv-disc-ojo {
      background: rgba(229, 160, 94, 0.12);
      border-color: var(--warning);
      color: var(--warning);
    }
    .pm-empty-state {
      padding: var(--esp-6) var(--esp-4);
      text-align: center;
      color: var(--text-muted);
    }
    .pm-empty-state .pm-empty-icon {
      font-size: var(--fs-xl);
      color: var(--gold-dim);
      margin-bottom: var(--esp-2);
    }

    /* Pago Listo: panel con DOS miniaturas (báscula + comprobante) */
    .pl-thumbs {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: var(--esp-2);
      margin-bottom: var(--esp-3);
    }
    .pl-thumb-cell {
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: var(--esp-1);
    }
    .pl-thumb-label {
      font-size: var(--fs-xs);
      color: var(--text-muted);
      text-transform: uppercase;
      letter-spacing: var(--track-etiqueta);
    }
    .pl-thumb-cell .pm-thumb,
    .pl-thumb-cell .pm-no-photo {
      margin: 0;
      width: 100%;
      height: 80px;
    }
    .pl-thumb-cell .pm-no-photo {
      display: flex;
      align-items: center;
      justify-content: center;
      padding: var(--esp-2);
      font-size: var(--fs-xs);
    }

    /* Placeholder "Próximamente" para categorías sin implementar */
    .ing-placeholder {
      text-align: center;
      padding: var(--esp-8) var(--esp-4);
      color: var(--text-muted);
      background: var(--bg-card);
      border: 1px solid var(--border-gold);
      border-radius: var(--radius);
    }
    .ing-placeholder .ing-placeholder-icon {
      font-size: var(--fs-2xl);
      color: var(--gold-dim);
      margin-bottom: var(--esp-3);
    }
    .ing-placeholder h3 {
      font-family: var(--font-serif);
      font-style: italic;
      color: var(--gold-bright);
      font-size: var(--fs-base);
      margin: 0 0 var(--esp-1);
    }
    .ing-placeholder p {
      font-size: var(--fs-m);
      margin: 0;
    }

    /* Lightbox para fotos.
       El hueco de arriba es para la ✕: antes el botón caía ENCIMA de la
       foto, y en una foto vertical de teléfono tapaba justo la esquina. */
    #pm-lightbox {
      position: fixed;
      inset: 0;
      background: rgba(0,0,0,0.92);
      display: flex;
      align-items: center;
      justify-content: center;
      z-index: var(--capa-visor);
      /* El 72 de arriba es maquetación (deja hueco a una barra fija): se
         queda. Los lados y el pie sí son espaciado, y van en la escala. */
      padding: 72px var(--esp-4) var(--esp-5);
    }
    /* 100% del hueco que deja el padding, no 90vw/90vh: así la foto entra
       ENTERA, que es lo que se viene a mirar. Sin object-fit que recorte y
       respetando la marca de orientación del archivo. */
    #pm-lightbox img {
      max-width: 100%;
      max-height: 100%;
      width: auto;
      height: auto;
      object-fit: contain;
      image-orientation: from-image;
      box-shadow: 0 20px 60px rgba(0,0,0,0.7);
      border-radius: 4px;
    }
    #pm-lightbox-fallo {
      color: #e8e2d8;
      font-size: var(--fs-base);
      text-align: center;
      padding: var(--esp-5) var(--esp-4);
      border: 1px dashed rgba(255,255,255,0.25);
      border-radius: 10px;
    }
    .pm-lightbox-close {
      position: absolute;
      top: 16px;
      right: 20px;
      z-index: 1;
      background: rgba(255,255,255,0.08);
      border: none;
      color: #fff;
      font-size: var(--fs-xl);
      line-height: var(--lh-plano);
      cursor: pointer;
      width: 44px;
      height: 44px;
      border-radius: 50%;
      transition: background .15s;
    }
    .pm-lightbox-close:hover { background: rgba(255,255,255,0.2); }
    /* Los mandos de la lupa. Arriba a la izquierda, lejos de la ✕. */
    .pm-lightbox-zoom {
      position: absolute;
      top: 16px;
      left: 20px;
      z-index: 2;
      display: flex;
      align-items: center;
      gap: var(--esp-1);
      background: rgba(255,255,255,0.08);
      border-radius: 99px;
      padding: var(--esp-1) var(--esp-1);
      color: #fff;
    }
    .pm-lightbox-zoom button {
      background: transparent; border: 0; color: #fff; cursor: pointer;
      font-size: var(--fs-base); line-height: var(--lh-plano); width: 36px; height: 36px; border-radius: 50%;
    }
    .pm-lightbox-zoom button:hover { background: rgba(255,255,255,0.18); }
    .pm-lightbox-zoom span { font-size: var(--fs-s); min-width: 46px; text-align: center; }
    /* La foto se mueve y se acerca con transform: sin transición, para que
       seguir el dedo o la rueda no vaya con retraso. */
    #pm-lightbox img { will-change: transform; touch-action: none; }
    /* Pantalla pequeña: menos margen para la foto y la ✕ bien a mano. */
    @media (max-width: 640px) {
      #pm-lightbox { padding: var(--esp-8) var(--esp-2) var(--esp-4); }
      .pm-lightbox-close { top: 10px; right: 10px; }
      .pm-lightbox-zoom { top: 10px; left: 10px; }
    }

    #disp-ledger-table .badge-recarga {
      background: var(--positive-bg);
      color: var(--positive);
      padding: var(--esp-1) var(--esp-2);
      border-radius: 99px;
      font-size: var(--fs-xs);
      font-weight: 600;
    }
    #disp-ledger-table .badge-debito {
      background: var(--negative-bg);
      color: var(--negative);
      padding: var(--esp-1) var(--esp-2);
      border-radius: 99px;
      font-size: var(--fs-xs);
      font-weight: 600;
    }

    /* ═══════════════════════════════════════════════════════════════════
       LOGIN SCREEN (full overlay, oculto por defecto)
    ══════════════════════════════════════════════════════════════════════ */
    #login-screen {
      display: none;
      position: fixed; inset: 0;
      background: var(--rose-light);
      z-index: var(--capa-login);
      align-items: center;
      justify-content: center;
      padding: var(--esp-5);
    }
    #login-screen.show { display: flex; }
    .login-card {
      background: var(--white);
      border: 1px solid var(--border);
      border-radius: var(--radius);
      box-shadow: var(--shadow-lg);
      padding: var(--esp-6) var(--esp-5);
      width: 100%;
      max-width: 380px;
    }
    .login-brand {
      font-size: var(--fs-l);
      font-weight: 600;
      color: var(--text-dark);
      margin-bottom: var(--esp-1);
    }
    .login-brand span { color: var(--rose-med); }
    .login-sub {
      font-size: var(--fs-s);
      color: var(--text-muted);
      letter-spacing: var(--track-etiqueta);
      text-transform: uppercase;
      margin-bottom: var(--esp-5);
    }
    #login-form .form-group { margin-bottom: var(--esp-3); }
    #login-error {
      display: none;
      margin-top: var(--esp-3);
      padding: var(--esp-2) var(--esp-3);
      background: var(--negative-bg);
      border: 1px solid color-mix(in srgb, var(--negative) 25%, transparent);
      color: var(--negative);
      border-radius: var(--radius-sm);
      font-size: var(--fs-s);
    }
    #login-error.show { display: block; }
    /* Usuario bloqueado: no es "te equivocaste", es "hoy no entras y hay que
       avisarle al administrador". Se ve más y se lee de lejos. */
    #login-error.bloqueado {
      font-size: var(--fs-m);
      font-weight: 600;
      padding: var(--esp-3) var(--esp-3);
      line-height: var(--lh-cuerpo);
    }

    /* Mientras no haya sesión, el resto de la app está oculto.
       Lo que vive DENTRO de #app se oculta solo; lo que vive fuera —las
       pantallas que se abren encima, y el visor de fotos— hay que nombrarlo
       aquí una por una. */
    body.unauthed #app,
    body.unauthed #edit-overlay,
    body.unauthed #destino-overlay,
    body.unauthed #auth-overlay,
    body.unauthed #history-overlay,
    body.unauthed #pm-lightbox,
    body.unauthed #persona-overlay { display: none !important; }
    body.unauthed #login-screen { display: flex; }

    /* ═══════════════════════════════════════════════════════════════════
       MAIN AREA
    ══════════════════════════════════════════════════════════════════════ */
    #app {
      flex: 1;
      display: flex;
      flex-direction: column;
      height: 100vh;
      overflow: hidden;
    }

    /* ── Botón principal "+ Registrar" ─────────────────────────────────
       Registrar es LA acción del día, así que va arriba del contenido,
       centrado y grande: es lo primero que se ve al entrar y no hay que
       buscarlo. Antes fue un flotante en la esquina inferior derecha;
       ahí tapaba la última fila de las tablas y obligaba a reservar
       espacio abajo en todas las vistas.

       Sólo se muestra en las secciones donde registrar tiene sentido
       (ver SECCIONES_CON_REGISTRAR en el JS de navegación). Vive dentro
       de #content, así que en la pantalla de login no aparece: body.unauthed
       ya oculta #app entero. */
    #registrar-bar {
      display: none;                  /* showSection decide cuándo se ve */
      justify-content: center;
      margin: 0 0 var(--esp-5);
    }
    #registrar-bar.visible { display: flex; }

    #btn-registrar-main {
      display: flex;
      align-items: center;
      justify-content: center;
      gap: var(--esp-3);
      width: 100%;
      max-width: 400px;
      min-height: 64px;
      padding: var(--esp-4) var(--esp-6);
      border: none;
      border-radius: var(--radius-md);
      background: var(--gold);
      color: #1d1608;                 /* texto oscuro sobre dorado sólido */
      font-family: var(--font-sans);
      font-size: var(--fs-base);
      font-weight: 700;
      letter-spacing: var(--track-sutil);
      cursor: pointer;
      box-shadow: 0 4px 16px rgba(0,0,0,0.35);
      transition: background 0.14s ease, transform 0.14s ease, box-shadow 0.14s ease;
    }
    #btn-registrar-main .reg-plus {
      font-size: var(--fs-xl);
      line-height: var(--lh-plano);
      font-weight: 400;
      margin-top: calc(-1 * var(--esp-1));
    }
    #btn-registrar-main:hover {
      background: var(--gold-bright);
      transform: translateY(-1px);
      box-shadow: 0 8px 22px rgba(0,0,0,0.42);
    }
    #btn-registrar-main:active { transform: translateY(0) scale(0.99); }
    #btn-registrar-main:focus-visible {
      outline: 3px solid var(--gold-bright);
      outline-offset: 3px;
    }

    @media print {
      #registrar-bar { display: none !important; }
    }

    /* ── Aviso de servidor desactualizado (ver verificarVersionServidor) ─
       Barra fija arriba. No captura clics (pointer-events:none) y, cuando
       está visible, el layout completo baja para que no tape la barra
       superior ni sus botones. */
    #version-banner {
      position: fixed; left: 0; right: 0; top: 0; z-index: var(--capa-aviso);
      padding: var(--esp-2) var(--esp-4);
      background: #7a2626; color: #fff;
      font-size: var(--fs-m); font-weight: 600; text-align: center;
      box-shadow: 0 2px 10px rgba(0,0,0,.4);
      pointer-events: none;
    }
    /* El botón de recargar SÍ se puede tocar: la barra no captura clics,
       pero lo que lleva dentro tiene que poder pulsarse. */
    #version-banner button {
      pointer-events: auto; margin-left: var(--esp-2); cursor: pointer;
      background: #fff; color: #7a2626; border: 0; border-radius: 6px;
      padding: var(--esp-1) var(--esp-3); font-size: var(--fs-m); font-weight: 700;
    }
    /* Varios avisos del sistema a la vez (página vieja + mantenimiento): uno
       debajo del otro, los dos legibles. */
    #version-banner .vb-linea + .vb-linea {
      margin-top: var(--esp-1); padding-top: var(--esp-1); border-top: 1px solid rgba(255,255,255,.35);
    }
    /* MODO MANTENIMIENTO. Ámbar y no rojo: no es un error, es una puerta
       cerrada a propósito — y tiene que distinguirse de un aviso de fallo. */
    #version-banner.mantenimiento { background: #8a5a00; }
    #version-banner.mantenimiento button { color: #8a5a00; }

    /* ── CHAT: LA VENTANITA FLOTANTE (estilo Facebook) ────────────────
       Abajo a la derecha, pegada al borde. Plegada es una barrita de 44px;
       desplegada, una ventana de 340×430 — no media pantalla. Por encima de
       las secciones y por debajo del visor de fotos, del cartel del turno y
       del bloqueo: un mensaje no tapa un aviso que tranca la caja.

       No estorba los botones de la página: ocupa la esquina, y en pantalla
       pequeña se queda igual de baja (no cubre la barra de arriba ni el
       botón de registrar, que va centrado). */
    #chat-flotante {
      position: fixed; right: 16px; bottom: 0; z-index: var(--capa-chat);
      width: min(340px, calc(100vw - 24px));
      display: flex; flex-direction: column;
      font-size: var(--fs-s);
    }
    /* LA BARRITA TIENE QUE VERSE. Era gris oscuro sobre fondo oscuro, con
       un borde al 10% de blanco, pegada al borde de abajo: estaba ahí, pero
       Rich no la encontraba — y con razón. Es la ÚNICA puerta al chat, así
       que ahora lleva un borde dorado tenue y su propio fondo, que la
       separan del fondo sin gritar. El dorado fuerte sigue reservado para
       cuando hay algo sin leer (.aviso), que es lo que debe destacar. */
    .chat-barra {
      display: flex; align-items: center; gap: var(--esp-2);
      width: 100%; padding: var(--esp-3) var(--esp-3); cursor: pointer;
      background: var(--bg-card); color: var(--text-primary);
      border: 1px solid var(--gold-dim); border-bottom: 0;
      border-radius: 10px 10px 0 0;
      box-shadow: 0 -6px 22px rgba(0,0,0,.55);
      font-family: inherit; font-size: var(--fs-s); text-align: left;
    }
    .chat-barra .chat-barra-titulo { color: var(--gold) }
    .chat-barra:hover { background: var(--bg-elevated) }
    .chat-barra.aviso .chat-barra-titulo { color: inherit }
    /* Con mensajes sin leer, la barrita se pone dorada y late: es el aviso,
       y no tapa nada de lo que el operador esté haciendo. */
    .chat-barra.aviso { background: var(--gold); color: #1b1403; border-color: var(--gold) }
    .chat-barra.aviso .chat-barra-titulo { font-weight: 800 }
    .chat-barra.late { animation: chat-late 1.8s ease-in-out infinite }
    @keyframes chat-late { 0%,100% { filter: brightness(1) } 50% { filter: brightness(1.18) } }
    .chat-barra-titulo { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
    .chat-barra-num {
      min-width: 21px; height: 21px; padding: 0 var(--esp-1); border-radius: 999px;
      background: #c0392b; color: #fff; font-size: var(--fs-xs); font-weight: 800;
      line-height: 21px; text-align: center;
    }
    .chat-barra-flecha { color: var(--text-muted); font-size: var(--fs-xs)}
    /* [hidden] gana sobre el display:flex. Sin esto la ventanita seguía
       ocupando sus 430 px plegada: no se veía, pero su caja invisible
       quedaba en la esquina y se comía los clics de lo que hubiera debajo. */
    .chat-ventana[hidden], .chat-barra[hidden], .chat-interruptor[hidden] { display: none }
    .chat-ventana {
      display: flex; flex-direction: column;
      height: min(430px, 68vh);
      background: var(--bg-card);
      border: 1px solid var(--border-soft); border-bottom: 0;
      border-radius: 10px 10px 0 0;
      box-shadow: 0 -6px 26px rgba(0,0,0,.45);
      overflow: hidden;
    }
    .chat-cab {
      display: flex; align-items: center; gap: var(--esp-1);
      padding: var(--esp-2) var(--esp-2) var(--esp-2) var(--esp-3); border-bottom: 1px solid var(--border-soft);
      background: var(--bg-elevated);
    }
    .chat-cab-titulo { font-weight: 800; font-size: var(--fs-m)}
    .chat-cab-sub { font-size: var(--fs-xs); color: var(--text-muted) }
    .chat-cab-btn {
      margin-left: auto; background: transparent; border: 0; color: var(--text-muted);
      font-size: var(--fs-m); cursor: pointer; line-height: var(--lh-plano); padding: var(--esp-1) var(--esp-1);
    }
    .chat-cab-btn + .chat-cab-btn { margin-left: 0 }
    /* Con el silenciador escondido (operador), el que queda primero es el
       que tiene que empujar los botones a la derecha. */
    .chat-cab-btn[hidden] { display: none }
    .chat-cab-btn[hidden] + .chat-cab-btn { margin-left: auto }
    .chat-cab-btn:hover { color: var(--text-primary) }
    .chat-interruptor {
      padding: var(--esp-2) var(--esp-3); border-bottom: 1px solid var(--border-soft);
      display: flex; flex-direction: column; gap: var(--esp-2); font-size: var(--fs-s);
    }
    .chat-cuerpo {
      flex: 1; overflow-y: auto; padding: var(--esp-2) var(--esp-3);
      display: flex; flex-direction: column; gap: var(--esp-2);
    }
    .chat-pie { padding: var(--esp-2) var(--esp-3) var(--esp-3); border-top: 1px solid var(--border-soft) }
    .chat-msg {
      max-width: 86%; padding: var(--esp-2) var(--esp-2); border-radius: 12px;
      font-size: var(--fs-s); line-height: var(--lh-cuerpo); overflow-wrap: anywhere;
    }
    /* La imagen, de miniatura dentro de la burbuja. Alto tope para que una
       captura alargada no empuje el resto de la conversación fuera de la
       ventanita; se abre en grande en el visor de siempre. */
    .chat-img {
      display: block; max-width: 100%; max-height: 180px; width: auto;
      border-radius: 8px; margin-bottom: var(--esp-1); cursor: zoom-in;
      border: 1px solid var(--border-soft); object-fit: cover;
    }
    .chat-clip {
      flex: none; width: 34px; min-height: 38px; padding: 0;
      background: transparent; border: 1px solid var(--border-soft);
      border-radius: 8px; color: var(--text-muted); font-size: var(--fs-m); cursor: pointer;
    }
    .chat-clip:hover { color: var(--text-primary); background: var(--bg-elevated) }
    /* La imagen pegada espera aquí hasta que se pulsa Enviar: se puede quitar
       y se puede acompañar de texto. */
    .chat-adjunta {
      display: flex; align-items: center; gap: var(--esp-2);
      padding: var(--esp-1) var(--esp-1); margin-bottom: var(--esp-1);
      border: 1px dashed var(--border-soft); border-radius: 8px;
    }
    .chat-adjunta[hidden] { display: none }
    .chat-adjunta img { width: 38px; height: 38px; object-fit: cover; border-radius: 5px; flex: none }
    .chat-msg.mio  { align-self: flex-end; background: rgba(201,169,97,.20); border: 1px solid rgba(201,169,97,.45) }
    .chat-msg.otro { align-self: flex-start; background: rgba(255,255,255,.07); border: 1px solid var(--border-soft) }
    .chat-msg-meta { font-size: var(--fs-micro); color: var(--text-muted); margin-top: var(--esp-1)}
    .chat-vacio { color: var(--text-muted); font-size: var(--fs-s); text-align: center; margin-top: var(--esp-3); line-height: var(--lh-cuerpo)}
    .chat-sistema {
      align-self: center; font-size: var(--fs-xs); color: var(--text-muted);
      text-align: center; padding: var(--esp-1) var(--esp-2);
    }
    /* Pantalla pequeña: ocupa el ancho menos los márgenes, y más baja. */
    @media (max-width: 720px) {
      #chat-flotante { right: 8px; left: 8px; width: auto }
      .chat-ventana { height: min(62vh, 400px) }
    }
    body.con-version-banner #app {
      margin-top: var(--version-banner-h, 42px);
      height: calc(100vh - var(--version-banner-h, 42px));
    }

    /* ── CARTEL DEL TURNO ─────────────────────────────────────────────
       Trece minutos antes de que se le acabe la franja al operador, y diez
       antes del cierre de caja. Grande, amarillo y SIN botón de cerrar: no
       se puede ocultar ni minimizar, y no se va hasta que el turno quede
       cerrado. Esa es toda la intención — el cartel que se puede quitar es
       el cartel que se quita sin leerlo.

       Va fijo arriba, igual que el aviso de servidor viejo, y empuja la
       página hacia abajo para no tapar la barra ni sus botones. A
       diferencia de aquél, ÉSTE sí captura clics: lleva el botón que
       lleva directo a cerrar el turno. */
    #cartel-turno {
      position: fixed; left: 0; right: 0; top: 0; z-index: var(--capa-cartel-turno);
      display: none;
      padding: var(--esp-3) var(--esp-4);
      background: #f6c945;
      color: #241c00;
      border-bottom: 3px solid #a8790b;
      box-shadow: 0 4px 18px rgba(0,0,0,.35);
      text-align: center;
      animation: cartel-turno-latido 2.4s ease-in-out infinite;
    }
    body.con-cartel-turno #cartel-turno { display: block }
    #cartel-turno .ct-titulo {
      font-size: var(--fs-base); font-weight: 800; letter-spacing: var(--track-sutil);
      text-transform: uppercase; margin-bottom: var(--esp-1);
    }
    #cartel-turno .ct-texto {
      font-size: var(--fs-m); font-weight: 600; line-height: var(--lh-cuerpo);
      max-width: 980px; margin: 0 auto;
    }
    #cartel-turno .ct-acciones { margin-top: var(--esp-2)}
    #cartel-turno .ct-btn {
      display: inline-block; cursor: pointer;
      background: #241c00; color: #f6c945;
      border: 0; border-radius: 8px;
      padding: var(--esp-2) var(--esp-4); font-size: var(--fs-m); font-weight: 700;
      font-family: inherit;
    }
    #cartel-turno .ct-btn:hover { background: #000 }
    /* Un cartel por cada cosa: si coinciden el fin de turno y el cierre de
       caja, se leen los dos, uno debajo del otro. */
    #cartel-turno .ct-uno + .ct-uno {
      margin-top: var(--esp-2); padding-top: var(--esp-2);
      border-top: 2px solid rgba(36,28,0,.25);
    }
    @keyframes cartel-turno-latido {
      0%, 100% { background: #f6c945 }
      50%      { background: #ffd96b }
    }
    /* Cuando también está el aviso de servidor viejo, el del turno se pone
       debajo: los dos se tienen que poder leer. */
    body.con-version-banner #cartel-turno { top: var(--version-banner-h, 42px) }
    body.con-cartel-turno #app { margin-top: var(--cartel-turno-h, 96px) }
    body.con-cartel-turno.con-version-banner #app {
      margin-top: calc(var(--version-banner-h, 42px) + var(--cartel-turno-h, 96px));
    }
    @media (max-width: 720px) {
      #cartel-turno { padding: var(--esp-3) var(--esp-3)}
      #cartel-turno .ct-titulo { font-size: var(--fs-m)}
      #cartel-turno .ct-texto  { font-size: var(--fs-s)}
    }

    /* ── CARTEL DEL COBRO QUE NO ENTRÓ EN WISPHUB ────────────────────
       El cobro se guarda y se responde ANTES de hablar con WispHub, así que
       el mostrador no puede esperar el resultado. Durante el 14-oct-2026 el
       resultado no se le decía NUNCA: la pantalla ponía "Mensualidad
       completa" mientras en WispHub la factura seguía abierta.

       Este cartel sale sólo cuando algo quedó mal, y por eso se puede leer:
       un aviso que sale en cada cobro se deja de mirar al tercero. Rojo,
       grande y arriba; se quita con su botón —nunca solo y nunca con un
       clic en cualquier parte—, porque el operador tiene que reconocer que
       lo leyó antes de que el cliente se vaya.

       ── Y TIENE DOS VOCES, DESDE EL 15-OCT-2026 ──────────────────────
       El rojo se gastó: salió sobre un cobro que había entrado perfecto en
       WispHub, porque la pantalla preguntaba a los 1,5 s y el trabajo real
       tardaba 5. Rich lo creyó, borró el cobro y volvió a cobrar.
       El rojo (sin clase) es para lo que FALLÓ, y sólo para eso. El
       `.cw-espera` es gris y sereno: "esto sigue en marcha". Un cobro
       tardando no es un cobro fallado, y la pantalla no puede confundirlos:
       es lo único que el operador tiene para decidir si deja ir al cliente. */
    #cartel-cobro-wh {
      position: fixed; left: 0; right: 0; top: 0; z-index: var(--capa-cartel-cobro);
      display: none;
      padding: var(--esp-3) var(--esp-4);
      background: #7a2626; color: #fff;
      border-bottom: 3px solid #4a1414;
      box-shadow: 0 4px 18px rgba(0,0,0,.45);
      text-align: center;
    }
    body.con-cartel-cobro-wh #cartel-cobro-wh { display: block }
    #cartel-cobro-wh .cw-titulo {
      font-size: var(--fs-base); font-weight: 800; text-transform: uppercase; margin-bottom: var(--esp-1);
    }
    #cartel-cobro-wh .cw-texto {
      font-size: var(--fs-m); font-weight: 600; line-height: var(--lh-cuerpo);
      max-width: 900px; margin: 0 auto;
    }
    #cartel-cobro-wh .cw-btn {
      margin-top: var(--esp-2); cursor: pointer;
      background: #fff; color: #7a2626; border: 0; border-radius: 8px;
      padding: var(--esp-2) var(--esp-4); font-size: var(--fs-s); font-weight: 800; font-family: inherit;
    }
    /* La voz serena: nada rojo, nada en mayúsculas, sin botón que reconocer
       mientras el trabajo sigue — el cartel se quita solo en cuanto hay
       respuesta. Gris de la casa, no de alarma. */
    #cartel-cobro-wh.cw-espera {
      background: #31343c; color: #e8e8ea; border-bottom-color: #1d1f24;
      box-shadow: 0 4px 14px rgba(0,0,0,.3);
    }
    #cartel-cobro-wh.cw-espera .cw-titulo {
      text-transform: none; font-weight: 700; font-size: var(--fs-m); color: #fff;
    }
    #cartel-cobro-wh.cw-espera .cw-texto { font-weight: 500; color: #c9cace }
    #cartel-cobro-wh.cw-espera .cw-btn   { background: #e8e8ea; color: #31343c }
    /* Tres puntos que respiran, para que se vea que no está congelado. */
    #cartel-cobro-wh.cw-espera .cw-titulo::after {
      content: '…'; animation: cw-latido 1.4s ease-in-out infinite;
    }
    @keyframes cw-latido { 0%,100% { opacity: .25 } 50% { opacity: 1 } }
    /* Si coincide con los otros carteles, cada uno debajo del anterior: los
       tres se tienen que poder leer. */
    body.con-version-banner #cartel-cobro-wh { top: var(--version-banner-h, 42px) }
    body.con-cartel-turno #cartel-cobro-wh {
      top: calc(var(--cartel-turno-h, 96px));
    }
    body.con-cartel-turno.con-version-banner #cartel-cobro-wh {
      top: calc(var(--version-banner-h, 42px) + var(--cartel-turno-h, 96px));
    }
    /* Y empuja la página hacia abajo mientras está puesto, para no tapar la
       barra de arriba ni sus botones. La altura la mide el JS al enseñarlo,
       porque depende de cuánto texto traiga el aviso. */
    body.con-cartel-cobro-wh #app {
      margin-top: var(--cartel-cobro-wh-h, 150px);
      height: calc(100vh - var(--cartel-cobro-wh-h, 150px));
    }
    body.con-cartel-cobro-wh.con-version-banner #app {
      margin-top: calc(var(--version-banner-h, 42px) + var(--cartel-cobro-wh-h, 150px));
      height: calc(100vh - var(--version-banner-h, 42px) - var(--cartel-cobro-wh-h, 150px));
    }
    body.con-cartel-cobro-wh.con-cartel-turno #app {
      margin-top: calc(var(--cartel-turno-h, 96px) + var(--cartel-cobro-wh-h, 150px));
    }
    body.con-cartel-cobro-wh.con-cartel-turno.con-version-banner #app {
      margin-top: calc(var(--version-banner-h, 42px) + var(--cartel-turno-h, 96px)
                       + var(--cartel-cobro-wh-h, 150px));
    }
    @media (max-width: 720px) {
      #cartel-cobro-wh { padding: var(--esp-3) var(--esp-3)}
      #cartel-cobro-wh .cw-titulo { font-size: var(--fs-m)}
      #cartel-cobro-wh .cw-texto  { font-size: var(--fs-s)}
    }

    /* ── Búsqueda global ─────────────────────────────────────────────
       Vive en el encabezado y está siempre a la vista: encontrar un pago
       no debería obligar a acordarse primero de en qué categoría estaba.
       Los resultados caen debajo, encima de todo, sin sacar a nadie de
       la pantalla en la que está trabajando. */
    #bg-wrap {
      position: relative;
      flex: 0 1 340px;
      min-width: 150px;
    }
    #bg-input {
      width: 100%;
      padding: var(--esp-2) var(--esp-6) var(--esp-2) var(--esp-6);
      border-radius: 99px;
      border: 1px solid var(--border);
      background: var(--white);
      font-family: var(--font-sans);
      font-size: var(--fs-m);
      color: var(--text-dark);
      /* El anillo de foco lo pone la regla única del final. */
      transition: border-color .18s, box-shadow .18s;
    }
    #bg-input:focus {
      border-color: var(--gold);
      box-shadow: 0 0 0 3px rgba(191,155,74,0.14);
    }
    #bg-input::placeholder { color: var(--text-muted); }
    #bg-icon {
      position: absolute; left: 12px; top: 50%;
      transform: translateY(-50%);
      width: 14px; height: 14px;
      pointer-events: none;
      color: var(--text-muted);
    }
    #bg-clear {
      position: absolute; right: 8px; top: 50%;
      transform: translateY(-50%);
      border: none; background: none; cursor: pointer;
      color: var(--text-muted); font-size: var(--fs-m); line-height: var(--lh-plano);
      padding: var(--esp-1); display: none;
    }
    #bg-wrap.has-text #bg-clear { display: block; }
    #bg-panel {
      position: absolute;
      top: calc(100% + 8px); right: 0;
      width: min(560px, 92vw);
      max-height: min(70vh, 560px);
      overflow-y: auto;
      background: var(--white);
      border: 1px solid var(--border);
      border-radius: var(--radius);
      box-shadow: var(--shadow-lg);
      z-index: var(--capa-panel);
      display: none;
    }
    #bg-panel.open { display: block; }
    .bg-head {
      padding: var(--esp-2) var(--esp-3);
      font-size: var(--fs-xs);
      text-transform: uppercase;
      letter-spacing: var(--track-etiqueta);
      color: var(--text-muted);
      border-bottom: 1px solid var(--border-soft);
      position: sticky; top: 0;
      background: var(--white);
    }
    .bg-row {
      display: block; width: 100%;
      text-align: left;
      padding: var(--esp-2) var(--esp-3);
      border: none; background: none; cursor: pointer;
      border-bottom: 1px solid var(--border-soft);
      transition: background .12s;
    }
    .bg-row:hover, .bg-row:focus { background: var(--rose-light); }
    .bg-row-top {
      display: flex; justify-content: space-between;
      align-items: baseline; gap: var(--esp-2);
    }
    .bg-row-cliente {
      font-weight: 600; font-size: var(--fs-m); color: var(--text-dark);
      overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    .bg-row-monto {
      font-family: var(--font-serif);
      font-size: var(--fs-m); white-space: nowrap; color: var(--text-dark);
    }
    .bg-row-sub {
      display: flex; flex-wrap: wrap; gap: var(--esp-1);
      font-size: var(--fs-s); color: var(--text-muted); margin-top: var(--esp-1);
    }
    .bg-row-sub span:not(:last-child)::after { content: ' ·'; }
    .bg-tag {
      display: inline-block;
      font-size: var(--fs-xs); font-weight: 700;
      text-transform: uppercase; letter-spacing: var(--track-sutil);
      padding: var(--esp-1) var(--esp-1); border-radius: 4px;
      background: var(--rose-light); color: var(--text-muted);
    }
    /* Una transacción anulada se sigue encontrando —puede que se busque
       justo para comprobar que lo está— pero no se puede confundir con
       una viva. */
    .bg-row.anulada .bg-row-cliente,
    .bg-row.anulada .bg-row-monto { text-decoration: line-through; opacity: .6; }
    .bg-tag.anulada { background: rgba(198,64,64,.12); color: var(--negative); }
    .bg-vacio, .bg-cargando {
      padding: var(--esp-5) var(--esp-3); text-align: center;
      font-size: var(--fs-m); color: var(--text-muted);
    }
    .bg-pie {
      padding: var(--esp-2) var(--esp-3); font-size: var(--fs-xs);
      color: var(--text-muted); text-align: center;
      border-top: 1px solid var(--border-soft);
    }
    @media (max-width: 860px) {
      #bg-wrap { flex: 1 1 auto; min-width: 0; }
      #bg-panel { width: min(560px, 96vw); }
    }

    /* Nota: el botón "+ Registrar" del encabezado se eliminó; la única
       forma de registrar es el botón grande de arriba del contenido
       (#btn-registrar-main). */

    /* ── Content ────────────────────────────────────────────────────── */

    .section { display: none; }
    .section.active { display: block; }

    /* ═══════════════════════════════════════════════════════════════════
       COMPONENTES REUTILIZABLES
    ══════════════════════════════════════════════════════════════════════ */

    /* Título de sección */
    .section-title {
      font-family: var(--font-sans);
      font-size: var(--fs-xl);
      font-weight: 600;
      color: var(--text-dark);
      margin-bottom: var(--esp-1);
    }
    .section-sub {
      font-size: var(--fs-s);
      color: var(--text-muted);
      margin-bottom: var(--esp-5);
    }

    /* Divider dorado */
    .gold-line {
      height: 2px;
      background: linear-gradient(90deg, var(--gold), transparent);
      margin: 0 0 var(--esp-5);
      border-radius: 99px;
      width: 80px;
    }

    /* ── KPI Cards ──────────────────────────────────────────────────── */
    .kpi-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
      gap: var(--esp-4);
      margin-bottom: var(--esp-5);
    }
    .kpi-card {
      background: var(--white);
      border: 1px solid var(--border);
      border-radius: var(--radius);
      padding: var(--esp-4) var(--esp-5);
      box-shadow: var(--shadow);
      position: relative;
      overflow: hidden;
      transition: transform 0.18s, box-shadow 0.18s;
    }
    .kpi-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-lg); }
    .kpi-card::before {
      content: '';
      position: absolute;
      top: 0; left: 0; right: 0;
      height: 3px;
    }
    .kpi-card.gold::before   { background: linear-gradient(90deg, var(--gold), var(--gold-light)); }
    .kpi-card.rose::before   { background: linear-gradient(90deg, var(--rose-deep), var(--rose-med)); }
    .kpi-card.teal::before   { background: linear-gradient(90deg, #6bbfb5, #9dddd6); }

    .kpi-icon {
      width: 40px; height: 40px;
      border-radius: 10px;
      display: flex; align-items: center; justify-content: center;
      font-size: var(--fs-base);
      margin-bottom: var(--esp-3);
    }
    .kpi-card.gold   .kpi-icon { background: rgba(201,169,110,0.15); color: var(--gold-dark); }
    .kpi-card.rose   .kpi-icon { background: rgba(232,160,176,0.15); color: var(--rose-med); }
    .kpi-card.teal   .kpi-icon { background: rgba(107,191,181,0.15); color: #27a89a; }

    .kpi-label {
      font-size: var(--fs-s);
      color: var(--text-muted);
      text-transform: uppercase;
      letter-spacing: var(--track-etiqueta);
      font-weight: 500;
      margin-bottom: var(--esp-1);
    }
    .kpi-value {
      font-family: var(--font-sans);
      font-size: var(--fs-xl);
      font-weight: 800;
      color: #3d2535;
      line-height: var(--lh-plano);
      margin-bottom: var(--esp-1);
    }

    /* ── Card genérica ──────────────────────────────────────────────── */
    .card {
      background: var(--white);
      border: 1px solid var(--border);
      border-radius: var(--radius);
      box-shadow: var(--shadow);
      overflow: hidden;
      margin-bottom: var(--esp-4);
    }
    .card-header {
      padding: var(--esp-4) var(--esp-4);
      border-bottom: 1px solid var(--border);
      display: flex;
      align-items: center;
      justify-content: space-between;
    }
    .card-title {
      font-family: var(--font-sans);
      font-size: var(--fs-base);
      font-weight: 600;
      color: var(--text-dark);
    }
    .card-body { padding: var(--esp-4); }

    /* ── Tabla ──────────────────────────────────────────────────────── */
    .table-wrap { overflow-x: auto; }
    table { width: 100%; border-collapse: collapse; font-size: var(--fs-m); }
    thead tr { background: var(--rose-light); }
    th {
      padding: var(--esp-3) var(--esp-3);
      text-align: left;
      font-size: var(--fs-xs);
      font-weight: 600;
      color: var(--text-muted);
      text-transform: uppercase;
      letter-spacing: var(--track-etiqueta);
      border-bottom: 1px solid var(--border);
      white-space: nowrap;
    }
    td {
      padding: var(--esp-3) var(--esp-3);
      border-bottom: 1px solid rgba(240,213,220,0.5);
      color: var(--text-dark);
      vertical-align: middle;
    }
    tbody tr:last-child td { border-bottom: none; }
    tbody tr:hover { background: var(--rose-light); }

    /* Badge */
    .badge {
      display: inline-flex;
      align-items: center;
      gap: var(--esp-1);
      padding: var(--esp-1) var(--esp-2);
      border-radius: 99px;
      font-size: var(--fs-s);
      font-weight: 600;
      letter-spacing: var(--track-sutil);
    }
    .badge-green  { background: var(--positive-bg); color: var(--positive); }

    /* ── Botones ────────────────────────────────────────────────────── */
    .btn {
      display: inline-flex;
      align-items: center;
      gap: var(--esp-1);
      padding: var(--esp-2) var(--esp-4);
      border-radius: 99px;
      border: none;
      cursor: pointer;
      font-family: var(--font-sans);
      font-size: var(--fs-m);
      font-weight: 500;
      transition: all 0.18s;
    }
    .btn-primary   { background: var(--text-dark); color: var(--white); }
    .btn-primary:hover { background: #5c3a4a; }
    .btn-gold      { background: var(--gold); color: var(--text-dark); }
    .btn-gold:hover { background: var(--gold-dark); color: var(--white); }
    .btn-outline   { background: transparent; color: var(--text-dark); border: 1px solid var(--border); }
    .btn-outline:hover { background: var(--rose-light); }
    .btn-danger    { background: transparent; color: var(--negative); border: 1px solid color-mix(in srgb, var(--negative) 30%, transparent); }
    .btn-danger:hover { background: var(--negative-bg); }
    .btn-sm { padding: var(--esp-1) var(--esp-3); font-size: var(--fs-s); }
    .btn-icon { width: 32px; height: 32px; padding: 0; justify-content: center; border-radius: 8px; }
    /* Botón de guardar/registrar/confirmar deshabilitado hasta que todos los
       campos obligatorios estén completos (regla global de UX). Se ve
       claramente inactivo y no responde al click. */
    .btn:disabled, .btn[disabled], .btn.is-disabled {
      opacity: 0.4;
      cursor: not-allowed;
      filter: grayscale(0.5);
      box-shadow: none;
    }

    .btn-row { display: flex; gap: var(--esp-2); align-items: center; flex-wrap: wrap; }

    /* ── Formularios ────────────────────────────────────────────────── */
    .form-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
      gap: var(--esp-4);
    }
    .form-group { display: flex; flex-direction: column; gap: var(--esp-1); }
    .form-group.span2 { grid-column: span 2; }
    .form-group.full { grid-column: 1 / -1; }
    /* Asterisco dorado en campos obligatorios */
    .form-group label .req { color: var(--gold); margin-left: var(--esp-1); font-weight: 700; }
    /* Validación visual en vivo (modal Nuevo cliente internet y otros) */
    .form-error {
      display: none;
      font-size: var(--fs-s);
      color: var(--negative);
      margin-top: var(--esp-1);
      line-height: var(--lh-titulo);
    }
    .form-group.error > input,
    .form-group.error > select,
    .form-group.error > textarea {
      border-color: var(--negative) !important;
      box-shadow: 0 0 0 3px rgba(225, 60, 60, 0.12) !important;
      background: #fff5f5;
    }
    .form-group.error .form-error { display: block; }
    label {
      font-size: var(--fs-s);
      font-weight: 500;
      color: var(--text-muted);
      text-transform: uppercase;
      letter-spacing: var(--track-etiqueta);
    }
    input, select, textarea {
      background: var(--rose-light);
      border: 1px solid var(--border);
      border-radius: var(--radius-sm);
      color: var(--text-dark);
      padding: var(--esp-2) var(--esp-3);
      font-family: var(--font-sans);
      font-size: var(--fs-m);
      width: 100%;
      transition: border-color 0.18s, box-shadow 0.18s;
    }
    input:focus, select:focus, textarea:focus {
      /* El anillo de foco lo pone la regla única del final. */
      border-color: var(--gold);
      box-shadow: 0 0 0 3px rgba(201,169,110,0.15);
      background: var(--white);
    }
    textarea { resize: vertical; min-height: 80px; }

    /* Alert */
    .alert {
      padding: var(--esp-2) var(--esp-4);
      border-radius: var(--radius-sm);
      font-size: var(--fs-m);
      margin-bottom: var(--esp-4);
    }
    .alert-success { background: var(--positive-bg); border: 1px solid color-mix(in srgb, var(--positive) 30%, transparent); color: var(--positive); }
    .alert-error   { background: var(--negative-bg); border: 1px solid color-mix(in srgb, var(--negative) 25%, transparent); color: var(--negative); }

    /* Empty state */
    .empty {
      padding: var(--esp-7) var(--esp-4);
      text-align: center;
      color: var(--text-muted);
      font-size: var(--fs-m);
    }
    .empty-icon { font-size: var(--fs-2xl); margin-bottom: var(--esp-3); opacity: 0.4; }

    /* ═══════════════════════════════════════════════════════════════════
       GRÁFICAS
    ══════════════════════════════════════════════════════════════════════ */
    .chart-container {
      position: relative;
      height: 280px;
      padding: var(--esp-1);
    }

    /* ── La hoja de Gráficas ────────────────────────────────────────
       Mismo papel que el resto de la libreta: nada de cuadros de otro
       sistema. Los colores salen todos de variables, así que el tema
       claro y el oscuro se pintan solos. */
    /* ── Los tres pasos ─────────────────────────────────────────────
       Numerados y en fila de arriba abajo: quien los usa no lee
       instrucciones, sigue los números. */
    .inf-paso {
      border: 1px solid var(--linea); border-radius: 14px;
      background: var(--n2); margin-bottom: var(--esp-3); overflow: hidden;
    }
    .inf-paso-cab {
      display: flex; align-items: center; gap: var(--esp-3);
      padding: var(--esp-3) var(--esp-4); border-bottom: 1px solid var(--linea);
      background: var(--glow-suave);
    }
    .inf-paso-n {
      flex: 0 0 auto; width: 29px; height: 29px; border-radius: 50%;
      display: flex; align-items: center; justify-content: center;
      background: var(--oro); color: #141001;
      font-family: var(--font-sans); font-size: var(--fs-s); font-weight: bold;
    }
    [data-tema="claro"] .inf-paso-n { background: var(--oro-claro); color: var(--text-on-gold); }
    .inf-paso-tit { font-family: var(--font-serif); font-size: var(--fs-base); font-weight: 700; color: var(--texto); }
    .inf-paso-sub { font-size: var(--fs-xs); color: var(--gris-2); font-style: italic; margin-top: var(--esp-1); }
    .inf-paso-cuerpo { padding: var(--esp-4) var(--esp-4); }
    /* El paso 3 se distingue: es el que hace algo. */
    .inf-paso-generar .inf-paso-cab { background: transparent; }
    .inf-paso-generar .inf-paso-cuerpo { display: flex; align-items: center; gap: var(--esp-4); flex-wrap: wrap; }
    .inf-paso-generar .btn { font-size: var(--fs-m); padding: var(--esp-3) var(--esp-6); }

    /* Paso 1 — las casillas de categoría */
    .inf-chk {
      display: inline-flex; align-items: center; gap: var(--esp-2); cursor: pointer;
      padding: var(--esp-2) var(--esp-3); border-radius: 10px; border: 1px solid var(--linea);
      font-size: var(--fs-s); color: var(--texto); transition: all .18s;
      /* Son nombres de categoría, no rótulos de campo: la regla global de
         <label> los ponía en versalitas y a 12px, y catorce nombres en
         mayúscula gritan y se leen peor. */
      text-transform: none; letter-spacing: 0;
    }
    .inf-chk:hover { border-color: var(--oro-med); background: var(--glow-suave); }
    .inf-chk input { width: auto; margin: 0; accent-color: var(--oro); }
    .inf-chk-todo { margin-bottom: var(--esp-3); font-weight: 700; }
    .inf-chk-todo.activo { border-color: var(--oro-med); background: var(--glow-suave); color: var(--oro); }
    .inf-cats { display: flex; flex-wrap: wrap; gap: var(--esp-2); transition: opacity .18s; }
    /* Con "Todo el negocio" puesto la lista sigue a la vista —para que se
       entienda qué hay— pero apagada: no está eligiendo nada. */
    .inf-cats.apagado { opacity: .45; }

    /* Paso 2 — los cuatro modos, excluyentes */
    .inf-modos { display: flex; flex-wrap: wrap; gap: var(--esp-2); margin-bottom: var(--esp-3); }
    .inf-modo {
      font-family: var(--font-sans); font-size: var(--fs-s); cursor: pointer;
      padding: var(--esp-2) var(--esp-5); border-radius: 999px;
      border: 1px solid var(--oro-dim); background: transparent; color: var(--gris);
      transition: all .18s;
    }
    .inf-modo:hover { color: var(--oro); border-color: var(--oro-med); }
    /* El activo se ve encendido y los otros tres apagados: de un vistazo se
       sabe cuál manda, que es justo lo que un grupo de casillas no deja ver. */
    .inf-modo.activo {
      background: var(--oro); border-color: var(--oro); color: #141001; font-weight: bold;
    }
    /* Ver la nota de contraste del tema claro, más abajo. */
    [data-tema="claro"] .inf-modo.activo {
      background: var(--oro-claro); border-color: var(--oro-claro); color: var(--text-on-gold);
    }
    .inf-campos { display: flex; gap: var(--esp-3); align-items: flex-end; flex-wrap: wrap; }
    /* Sin esto, los cuatro periodos se ven a la vez: el display:flex de
       arriba le gana al [hidden]{display:none} del navegador, y el JS puede
       poner hidden=true todo lo que quiera que el campo sigue en pantalla. */
    .inf-campos[hidden] { display: none; }
    .inf-nota { font-size: var(--fs-xs); color: var(--gris-2); font-style: italic; align-self: center; }
    .gf-campo { display: flex; flex-direction: column; gap: var(--esp-1); }
    .gf-campo label { font-size: var(--fs-xs); letter-spacing: var(--track-etiqueta); color: var(--gris); text-transform: uppercase; }
    .gf-campo input { width: 185px; }

    /* El aviso de que el periodo no cuadra, pegado al botón que no funcionó. */
    .inf-error {
      color: var(--rojo); font-size: var(--fs-s); font-weight: 700; line-height: var(--lh-cuerpo);
    }

    /* Encabezado del resultado: qué se generó, y los botones de llevárselo. */
    .inf-cabecera {
      display: flex; justify-content: space-between; align-items: flex-start;
      gap: var(--esp-3); flex-wrap: wrap;
      margin: var(--esp-5) var(--esp-1) var(--esp-4); padding-top: var(--esp-4); border-top: 2px solid var(--oro-dim);
    }
    .inf-cabecera-tit {
      font-family: var(--font-serif); font-size: var(--fs-l); font-weight: 700; color: var(--oro);
    }
    .gf-leyenda-rango { font-size: var(--fs-s); color: var(--text-muted); font-style: italic; }
    .gf-exportar { display: flex; gap: var(--esp-2); align-items: center; flex-wrap: wrap; }
    /* Por qué los tres botones están apagados. Va a su lado, no en un aviso
       aparte: la pregunta nace mirándolos. */
    .gf-sin-datos {
      font-size: var(--fs-s);
      color: var(--text-muted);
      font-style: italic;
    }
    .gf-exportar .btn[disabled] { opacity: .5; cursor: not-allowed; }

    /* La fila del total cierra la tabla del informe: se busca antes que
       ninguna otra, así que se lee más fuerte y con una línea de oro. */
    .gf-tabla tfoot .gf-total td {
      border-top: 2px solid var(--oro-dim); border-bottom: none;
      font-weight: 700; color: var(--oro); background: var(--glow-suave);
    }

    /* Franja de totales del rango. Es lo primero que se lee. */
    .gf-kpis {
      display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
      gap: var(--esp-3); margin-bottom: var(--esp-4);
    }
    .gf-kpi {
      border: 1px solid var(--linea); border-radius: 12px; padding: var(--esp-3) var(--esp-4);
      background: var(--n2);
    }
    .gf-kpi-label {
      font-size: var(--fs-xs); letter-spacing: var(--track-etiqueta); text-transform: uppercase;
      color: var(--gris); margin-bottom: var(--esp-1);
    }
    .gf-kpi-val { font-size: var(--fs-l); font-weight: 700; color: var(--texto); line-height: var(--lh-titulo); }
    .gf-kpi-val.oro { color: var(--oro) }
    .gf-kpi-val.verde { color: var(--verde) }
    .gf-kpi-val.rojo { color: var(--rojo) }
    .gf-kpi-sub { font-size: var(--fs-xs); color: var(--gris-2); margin-top: var(--esp-1); }

    /* Cada gráfico y su tabla de cifras viven en la misma tarjeta. */
    .gf-card {
      border: 1px solid var(--linea); border-radius: 14px;
      background: var(--n2); margin-bottom: var(--esp-4); overflow: hidden;
    }
    .gf-card-head {
      padding: var(--esp-3) var(--esp-4); border-bottom: 1px solid var(--linea);
      display: flex; justify-content: space-between; align-items: baseline; gap: var(--esp-3); flex-wrap: wrap;
    }
    .gf-card-title { font-family: var(--font-serif); font-size: var(--fs-base); color: var(--texto); font-weight: 700; }
    .gf-card-nota { font-size: var(--fs-xs); color: var(--gris-2); font-style: italic; }
    .gf-card-body { padding: var(--esp-4) var(--esp-4); }
    .gf-chart { position: relative; height: 260px; }
    .gf-chart.alto { height: 320px; }

    /* Cifras al lado del gráfico. */
    .gf-cifras { display: flex; flex-wrap: wrap; gap: var(--esp-2) var(--esp-5); margin-top: var(--esp-3);
                 padding-top: var(--esp-3); border-top: 1px solid var(--linea); }
    .gf-cifra-label { font-size: var(--fs-xs); letter-spacing: var(--track-etiqueta); text-transform: uppercase; color: var(--gris) }
    .gf-cifra-val { font-size: var(--fs-m); font-weight: 700; color: var(--texto) }

    .gf-tabla { width: 100%; border-collapse: collapse; font-size: var(--fs-s)}
    .gf-tabla th {
      text-align: left; padding: var(--esp-2) var(--esp-3); font-size: var(--fs-xs); letter-spacing: var(--track-etiqueta);
      text-transform: uppercase; color: var(--gris); border-bottom: 1px solid var(--linea);
      font-weight: 700; white-space: nowrap;
    }
    .gf-tabla td { padding: var(--esp-2) var(--esp-3); border-bottom: 1px solid var(--linea); color: var(--texto) }
    .gf-tabla tr:last-child td { border-bottom: none }
    .gf-tabla .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap }
    .gf-tabla .cat { font-weight: 700 }
    .gf-tabla tbody tr:hover td { background: var(--glow-suave) }
    .gf-tabla .prod td { font-size: var(--fs-s); color: var(--gris); padding-left: var(--esp-5)}
    .gf-tabla .prod td:first-child::before { content: '· '; color: var(--oro-dim) }

    .gf-vacio {
      padding: var(--esp-5) var(--esp-4); text-align: center; color: var(--gris);
      font-style: italic; font-size: var(--fs-s);
    }
    @media (max-width: 700px) {
      .inf-campos { flex-direction: column; align-items: stretch }
      .gf-campo input { width: 100% }
      .inf-modo { flex: 1 1 44%; }
      .gf-chart { height: 220px }
    }

    /* ── Categorías creadas desde la app (panel Precios) ─────────────── */
    .pcat-hint {
      font-size: var(--fs-s);
      color: var(--text-muted);
      margin-top: var(--esp-1);
      line-height: var(--lh-cuerpo);
    }
    .pcat-iconos {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(58px, 1fr));
      gap: var(--esp-2);
      margin-top: var(--esp-1);
    }
    .pcat-icono-btn {
      background: var(--bg-card);
      border: 1px solid var(--border-soft);
      border-radius: 10px;
      padding: var(--esp-2) var(--esp-1) var(--esp-1);
      cursor: pointer;
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: var(--esp-1);
      font-family: inherit;
    }
    .pcat-icono-btn svg {
      width: 26px; height: 26px; display: block;
      stroke: var(--gold-bright); color: var(--gold-bright);
    }
    .pcat-icono-btn span {
      font-size: var(--fs-xs);
      color: var(--text-muted);
      text-align: center;
      line-height: var(--lh-titulo);
    }
    .pcat-icono-btn.selected {
      border-color: var(--gold);
      box-shadow: 0 0 0 1px var(--gold);
    }
    /* Ya lo usa otra categoría: se avisa, no se prohíbe. */
    .pcat-icono-btn.en-uso { opacity: 0.72; }
    .pcat-icono-btn.en-uso svg { stroke: var(--text-muted); color: var(--text-muted); }
    .pcat-icono-btn.selected.en-uso { opacity: 1; }
    .pcat-icono-btn.selected.en-uso svg { stroke: var(--gold-bright); color: var(--gold-bright); }
    .pcat-icono-uso {
      font-size: var(--fs-micro);
      font-style: normal;
      text-transform: uppercase;
      letter-spacing: var(--track-sutil);
      color: var(--warning);
      line-height: var(--lh-plano);
    }
    .pcat-iconos {
      max-height: 260px;
      overflow-y: auto;
    }
    .pcat-monedas { display: flex; gap: var(--esp-2); margin-top: var(--esp-1); }
    .pcat-moneda-btn.selected {
      border-color: var(--gold);
      box-shadow: 0 0 0 1px var(--gold);
    }
    .precios-cat-icono svg {
      width: 22px; height: 22px; display: block;
      stroke: var(--gold-bright); color: var(--gold-bright);
    }
    .precios-badge {
      font-size: var(--fs-xs);
      text-transform: uppercase;
      letter-spacing: var(--track-sutil);
      border: 1px solid var(--border-gold);
      color: var(--gold-dark);
      border-radius: 999px;
      padding: var(--esp-1) var(--esp-2);
      margin-left: var(--esp-1);
      white-space: nowrap;
    }
    .precios-group-title {
      font-size: var(--fs-s);
      text-transform: uppercase;
      letter-spacing: var(--track-etiqueta);
      color: var(--text-muted);
      margin: var(--esp-4) 0 var(--esp-2);
    }

    /* ═══════════════════════════════════════════════════════════════════
       MODAL DE REGISTRO
    ══════════════════════════════════════════════════════════════════════ */
    #edit-overlay,
    #destino-overlay,
    #auth-overlay,
    #history-overlay,
    #persona-overlay,
    #psub-overlay,
    #pcat-overlay,
    #inv-carga-overlay,
    #inv-ajuste-overlay,
    #ci-overlay,
    #spl-abono-overlay,
    #spl-edit-overlay,
    #disp-edit-overlay,
    #ver-detail-overlay,
    #nomina-modal-overlay,
    #usuarios-modal-overlay,
    #recibo-overlay,
    #bg-detalle-overlay,
    #pend-overlay,
    #vok-overlay,
    #cierre-manual-overlay{
      position: fixed; inset: 0;
      background: rgba(61,37,53,0.45);
      backdrop-filter: blur(4px);
      z-index: var(--capa-modal);
      display: none;
      align-items: center;
      justify-content: center;
      padding: var(--esp-4);
    }
    #auth-overlay    { z-index: var(--capa-modal-clave); }
    #history-overlay { z-index: var(--capa-modal-hist); }
    /* ci-overlay se abre desde el flujo de registrar, que ahora vive en la
       hoja: se queda por encima del resto de modales de esa pantalla. */
    #ci-overlay      { z-index: var(--capa-modal-ci); }
    #edit-overlay.open,
    #destino-overlay.open,
    #auth-overlay.open,
    #history-overlay.open,
    #persona-overlay.open,
    #psub-overlay.open,
    #pcat-overlay.open,
    #inv-carga-overlay.open,
    #inv-ajuste-overlay.open,
    #ci-overlay.open,
    #spl-abono-overlay.open,
    #spl-edit-overlay.open,
    #disp-edit-overlay.open,
    #ver-detail-overlay.open,
    #nomina-modal-overlay.open,
    #usuarios-modal-overlay.open,
    #recibo-overlay.open,
    #bg-detalle-overlay.open,
    #pend-overlay.open,
    #vok-overlay.open,
    #cierre-manual-overlay.open{ display: flex; }
    /* El comprobante que espera OK: se abre desde el cartel de arriba, que
       sale esté abierta la pantalla que esté — también encima del detalle de
       una pendiente, que es desde donde se puede estar mirando. */
    #vok-overlay { z-index: var(--capa-modal-ok); }
    /* Pendientes de pago: se abre también desde Inicio y desde Historial. */
    #pend-overlay { z-index: var(--capa-modal-pend); }
    .modal {
      background: var(--white);
      border-radius: var(--radius);
      box-shadow: var(--shadow-lg);
      width: 100%;
      max-width: 560px;
      max-height: 90vh;
      overflow-y: auto;
      animation: modal-in 0.25s;
    }
    @keyframes modal-in {
      from { transform: scale(0.95) translateY(10px); opacity: 0; }
      to   { transform: scale(1) translateY(0); opacity: 1; }
    }
    .modal-header {
      padding: var(--esp-4) var(--esp-5) var(--esp-4);
      border-bottom: 1px solid var(--border);
      display: flex;
      align-items: center;
      justify-content: space-between;
    }
    .modal-title {
      font-family: var(--font-sans);
      font-size: var(--fs-l);
      font-weight: 600;
      color: var(--text-dark);
    }
    .modal-close {
      width: 32px; height: 32px;
      border-radius: 50%;
      border: none;
      background: var(--rose-light);
      color: var(--text-muted);
      cursor: pointer;
      font-size: var(--fs-m);
      display: flex; align-items: center; justify-content: center;
      transition: background 0.18s;
    }
    .modal-close:hover { background: var(--rose-mid); color: var(--text-dark); }
    .modal-body  { padding: var(--esp-4) var(--esp-5); }
    .modal-footer {
      padding: var(--esp-4) var(--esp-5);
      border-top: 1px solid var(--border);
      display: flex;
      gap: var(--esp-2);
      justify-content: flex-end;
    }

    /* ═══════════════════════════════════════════════════════════════════
       MODAL DE CAPTURA MANUAL (Día 2)
    ══════════════════════════════════════════════════════════════════════ */
    .cap-step-hint {
      font-size: var(--fs-m);
      color: var(--text-muted);
      margin-bottom: var(--esp-4);
    }
    .cap-tipos-grid {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
      gap: var(--esp-3);
    }
    .cap-tipo-card {
      background: var(--rose-light);
      border: 1px solid var(--border);
      border-radius: var(--radius-md);
      padding: var(--esp-4) var(--esp-3);
      cursor: pointer;
      transition: all 0.15s;
      display: flex;
      flex-direction: column;
      gap: var(--esp-1);
      min-height: 76px;
      text-align: left;
      font-family: inherit;
      color: var(--text-dark);
      width: 100%;
    }
    .cap-tipo-card:hover {
      border-color: var(--gold);
      transform: translateY(-2px);
      box-shadow: 0 4px 14px rgba(201,169,97,0.15);
    }
    .cap-tipo-card .cap-tipo-icon {
      font-size: var(--fs-l);
      color: var(--gold-dark);
      line-height: var(--lh-plano);
      display: inline-flex;
      align-items: center;
      justify-content: center;
      min-height: 26px;
    }
    .cap-tipo-card .cap-tipo-icon svg {
      width: 26px;
      height: 26px;
      display: block;
      stroke: var(--gold-bright);
      color: var(--gold-bright);
    }
    .cap-tipo-card .cap-tipo-name {
      font-weight: 600;
      font-size: var(--fs-m);
    }
    .cap-tipo-card .cap-tipo-sub {
      font-size: var(--fs-s);
      color: var(--text-muted);
    }

    .cap-back {
      display: flex;
      align-items: center;
      gap: var(--esp-3);
      margin-bottom: var(--esp-4);
      padding-bottom: var(--esp-3);
      border-bottom: 1px solid var(--border);
    }
    .cap-current-tipo {
      font-weight: 600;
      color: var(--text-dark);
      font-size: var(--fs-base);
    }

    #cap-form .form-grid {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: var(--esp-3);
    }
    #cap-form .form-group { display: flex; flex-direction: column; gap: var(--esp-1); }
    #cap-form .form-group.full { grid-column: 1 / -1; }
    #cap-form .form-group label {
      font-size: var(--fs-s);
      color: var(--text-muted);
      font-weight: 500;
    }
    #cap-form .form-group label .req { color: var(--negative); margin-left: var(--esp-1); }
    #cap-form input,
    #cap-form select,
    #cap-form textarea {
      width: 100%;
      padding: var(--esp-2) var(--esp-3);
      border: 1px solid var(--border-soft);
      border-radius: 8px;
      font-size: var(--fs-m);
      font-family: inherit;
      background: var(--bg-input);
      color: var(--text-primary);
      min-height: 40px;
      transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;
    }
    #cap-form textarea { resize: vertical; min-height: 60px; }
    #cap-form input::placeholder,
    #cap-form textarea::placeholder { color: var(--text-dim); }
    #cap-form input:focus,
    #cap-form select:focus,
    #cap-form textarea:focus {
      /* El anillo de foco lo pone la regla única del final. */
      border-color: var(--gold);
      box-shadow: 0 0 0 3px rgba(201,169,97,0.18);
      background: var(--bg-elevated);
    }
    #cap-form select option { background: var(--bg-card); color: var(--text-primary); }

    /* File picker: input nativo OCULTO + <label> visible que actúa como
       botón. Esto evita el bug del input[type=file] estilizado que en
       algunos casos quedaba con el ::file-selector-button no clickable
       (efecto colateral de display:flex + overflow:hidden + padding:0
       sobre el control nativo, agravado por overlays del modal). El
       patrón label[for] es bulletproof: clic en label dispara input.click()
       internamente, sin depender del render del pseudo-elemento. */
    #section-registrar input[type="file"].cap-photo-input {
      position: absolute;
      width: 1px;
      height: 1px;
      opacity: 0;
      overflow: hidden;
      clip: rect(0 0 0 0);
      clip-path: inset(50%);
      white-space: nowrap;
      border: 0;
      padding: 0;
      margin: calc(-1 * var(--esp-1));
      pointer-events: none;
    }
    .cap-photo-picker {
      display: flex;
      align-items: center;
      gap: var(--esp-2);
      flex-wrap: wrap;
      min-height: 48px;
      padding: var(--esp-2) var(--esp-3);
      border: 1px dashed var(--border-soft);
      border-radius: 10px;
      background: var(--bg-input);
      transition: border-color 0.15s, background 0.15s;
    }
    .cap-photo-picker:hover {
      border-color: var(--border-gold);
      background: var(--bg-elevated);
    }
    .cap-photo-picker.has-file {
      border-style: solid;
      border-color: var(--border-gold);
    }
    .cap-photo-btn {
      display: inline-flex;
      align-items: center;
      gap: var(--esp-2);
      padding: var(--esp-2) var(--esp-4);
      background: var(--bg-gold-grad);
      color: var(--gold-bright);
      border: 1px solid var(--border-soft);
      border-radius: 8px;
      font-family: inherit;
      font-weight: 600;
      font-size: var(--fs-s);
      cursor: pointer;
      transition: background 0.15s, color 0.15s;
      user-select: none;
      line-height: var(--lh-plano);
    }
    .cap-photo-btn:hover {
      background: linear-gradient(135deg, #3a2c12 0%, #4d3a14 100%);
      color: #fff;
    }
    .cap-photo-btn:focus-within,
    .cap-photo-input.cap-photo-input:focus + .cap-photo-btn {
      outline: 2px solid var(--gold);
      outline-offset: 2px;
    }
    .cap-photo-btn svg {
      width: 16px;
      height: 16px;
      stroke: currentColor;
      fill: none;
    }
    .cap-photo-filename {
      font-size: var(--fs-s);
      color: var(--text-muted);
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      max-width: 248px;
    }
    .cap-photo-picker.has-file .cap-photo-filename {
      color: var(--positive);
    }
    /* "Quitar foto": a la vista, pero sin competir con el botón de la
       cámara. Sólo aparece cuando hay una foto puesta. */
    .cap-photo-quitar {
      background: transparent;
      border: 1px solid var(--border-soft);
      color: var(--text-muted);
      border-radius: 8px;
      padding: var(--esp-2) var(--esp-3);
      font-family: inherit;
      font-size: var(--fs-s);
      line-height: var(--lh-plano);
      cursor: pointer;
    }
    .cap-photo-quitar:hover { color: var(--negative); border-color: var(--negative); }
    .cap-photo-quitar[hidden] { display: none; }

    /* Botón "← Volver" del paso 2 — más prominente que el viejo
       "‹ Cambiar tipo" para que el operador NO crea que la única
       salida es la X (que cierra todo el modal). */
    .cap-back-btn {
      display: inline-flex;
      align-items: center;
      gap: var(--esp-1);
      padding: var(--esp-2) var(--esp-3);
      background: transparent;
      border: 1px solid var(--border);
      border-radius: 8px;
      color: var(--text-muted);
      font-family: inherit;
      font-size: var(--fs-m);
      font-weight: 500;
      cursor: pointer;
      transition: all 0.15s;
    }
    .cap-back-btn:hover {
      border-color: var(--gold);
      color: var(--gold-bright);
      background: rgba(201,169,97,0.06);
    }
    .cap-back-btn svg {
      width: 16px;
      height: 16px;
      stroke: currentColor;
      fill: none;
    }

    /* Bloque de foto + IA */
    .cap-photo-input {
      font-size: var(--fs-m);
      min-height: 44px;
    }
    .cap-photo-preview {
      display: none;
      max-width: 240px;
      max-height: 160px;
      border-radius: 8px;
      object-fit: cover;
      border: 1px solid var(--border);
    }
    .cap-photo-preview.show { display: block; }
    .cap-photo-status {
      font-size: var(--fs-s);
      color: var(--text-muted);
      min-height: 1.1em;
    }
    .cap-photo-status.ok    { color: var(--positive); }
    .cap-photo-status.error { color: var(--negative); }

    /* Discrepancia (servicios oro) */
    .cap-discrep {
      font-size: var(--fs-s);
      padding: var(--esp-2) var(--esp-3);
      border-radius: 8px;
      margin-top: var(--esp-1);
      font-weight: 500;
    }
    .cap-discrep.ok {
      background: rgba(90, 181, 116, 0.12);
      color: var(--positive);
      border: 1px solid rgba(90, 181, 116, 0.3);
    }
    .cap-discrep.bad {
      background: rgba(217, 116, 116, 0.12);
      color: var(--negative);
      border: 1px solid rgba(217, 116, 116, 0.3);
    }

    /* Autocomplete cliente */
    .cap-ac { position: relative; }
    .cap-ac-list {
      position: absolute;
      top: 100%; left: 0; right: 0;
      background: var(--bg-card);
      border: 1px solid var(--border-soft);
      border-radius: 8px;
      box-shadow: 0 8px 32px rgba(0,0,0,0.5);
      max-height: 220px;
      overflow-y: auto;
      z-index: 20;
      display: none;
    }
    .cap-ac-list.open { display: block; }
    /* La lista abierta con la flecha enseña TODOS los clientes, así que tiene
       que poder recorrerse: alto de unas siete filas y scroll propio. El
       `overscroll-behavior: contain` es lo que evita que, al llegar al final
       con la rueda o con el dedo, el empujón siga y arrastre el modal de
       detrás — en tablet eso se siente como que la pantalla se va sola. */
    .cap-ac-list {
      max-height: 320px;
      overscroll-behavior: contain;
      -webkit-overflow-scrolling: touch;
    }
    /* La flecha vive dentro del campo, pegada a la derecha. Va en su propio
       envoltorio y no en `.cap-ac` a secas, porque debajo del campo puede
       haber más cosas (la nota de "créalo primero en Clientes de internet")
       y entonces "pegado abajo" no sería el borde del campo. */
    .cap-ac-campo { position: relative; }
    .cap-ac-campo > input[type="text"] { padding-right: var(--esp-7); width: 100%; }
    .cap-ac-toggle {
      position: absolute;
      right: 1px; top: 50%; transform: translateY(-50%);
      width: 40px; height: calc(100% - 2px);
      /* El campo mide 40 px y el dedo necesita 44: la flecha se los toma
         centrada, asomando un par de píxeles por arriba y por abajo. Como no
         tiene fondo propio, eso no se ve — sólo se nota al tocarla, que es
         de lo que se trata. */
      min-height: 44px;
      display: flex; align-items: center; justify-content: center;
      background: none; border: 0; padding: 0;
      cursor: pointer; color: var(--text-muted);
      border-radius: 0 8px 8px 0;
    }
    /* El realce sólo donde hay ratón: en táctil pintaría un rectángulo más
       alto que el campo. */
    @media (hover: hover) {
      .cap-ac-toggle:hover { color: var(--gold-bright); background: var(--bg-elevated); }
    }
    .cap-ac-toggle svg { width: 18px; height: 18px; transition: transform .15s ease; }
    .cap-ac-toggle[aria-expanded="true"] svg { transform: rotate(180deg); }
    /* El dedo necesita blanco al que apuntar: en táctil la flecha se ensancha. */
    @media (hover: none) and (pointer: coarse) {
      .cap-ac-toggle { width: 48px; }
      .cap-ac-campo > input[type="text"] { padding-right: var(--esp-7); }
    }
    .cap-ac-item {
      padding: var(--esp-2) var(--esp-3);
      cursor: pointer;
      font-size: var(--fs-m);
      color: var(--text-primary);
      border-bottom: 1px solid var(--border-subtle);
    }
    .cap-ac-item:last-child { border-bottom: none; }
    .cap-ac-item:hover, .cap-ac-item.active { background: var(--bg-elevated); }
    .cap-ac-item .cap-ac-sub {
      font-size: var(--fs-s);
      color: var(--text-muted);
      margin-top: var(--esp-1);
    }
    .cap-ac-item.create { color: var(--gold-bright); font-weight: 600; }

    /* Spinner pequeño dentro del modal */
    .cap-mini-spinner {
      display: inline-block;
      width: 12px; height: 12px;
      border: 2px solid var(--border);
      border-top-color: var(--gold);
      border-radius: 50%;
      animation: spin 0.7s linear infinite;
      vertical-align: -2px;
      margin-right: var(--esp-1);
    }

    /* Grupos del paso 1 (LADO A / LADO B / Otros) */
    .cap-tipos-section { margin-bottom: var(--esp-5); }
    .cap-tipos-section-title {
      font-size: var(--fs-xs);
      font-weight: 600;
      color: var(--text-muted);
      text-transform: uppercase;
      letter-spacing: var(--track-etiqueta);
      margin-bottom: var(--esp-2);
    }

    /* Sub-pasos del paso 2 */
    .cap-substep {
      border: 1px solid var(--border);
      border-radius: 10px;
      padding: var(--esp-3);
      margin-bottom: var(--esp-3);
      background: var(--white);
    }
    .cap-substep.done {
      background: rgba(90, 181, 116, 0.04);
      border-color: rgba(90, 181, 116, 0.3);
    }
    .cap-substep-title {
      display: flex;
      align-items: center;
      gap: var(--esp-2);
      font-weight: 600;
      font-size: var(--fs-m);
      color: var(--text-dark);
      margin-bottom: var(--esp-3);
    }
    /* ── Transiciones del modal de registro ───────────────────────────
       El formulario se re-arma ENTERO en cada elección (tipo de envío,
       subcategoría, medio de pago, volver atrás). Sin transición eso se ve
       como un parpadeo: desaparece una cosa y aparece otra de golpe.

       Cómo se anima sin hacer esperar al operador: el DOM real se reemplaza
       de forma SÍNCRONA —quien llama sigue encontrando los campos en el
       acto— y lo que se anima es un FANTASMA, una copia del contenido
       saliente superpuesta y sin capturar clics, que se desvanece por
       encima mientras el nuevo contenido entra por debajo. Nadie espera a
       que termine ninguna animación.

       Sólo transform y opacity: nada que provoque reflow. */
    #cap-body, #cap-form { position: relative; }

    .cap-ghost {
      position: absolute;
      top: 0; left: 0; right: 0;
      z-index: 3;
      pointer-events: none;   /* no se puede pulsar lo que ya no existe */
      animation: cap-sale 0.19s cubic-bezier(0.2, 0.85, 0.4, 1) both;
    }
    @keyframes cap-sale {
      from { opacity: 1; transform: translate3d(0, 0, 0); }
      to   { opacity: 0; transform: translate3d(0, -8px, 0); }
    }
    /* El fantasma es una FOTO de lo que había, no una escena que se vuelve a
       reproducir. La copia se toma del HTML tal cual está, y ese HTML todavía
       lleva las clases de entrada del repintado anterior: dentro del fantasma
       esas animaciones arrancan otra vez desde cero, así que el contenido que
       se va APARECE encima del que entra y luego se desvanece. Eso era el
       parpadeo de superposición. El JS quita esas marcas al copiar; esta regla
       lo garantiza aunque algo se escape (un fantasma anidado, una animación
       de un widget). */
    .cap-ghost * { animation: none !important; transition: none !important; }
    /* El botón que el operador acaba de elegir NO se desvanece con los
       demás: se va volando a su nueva posición como título, así que en el
       fantasma se oculta para que no se vea dos veces. */
    .cap-ghost .cap-elegido-fantasma { opacity: 0; }

    /* Entrada retrasada 80 ms: el saliente y el entrante NO pueden ser
       legibles a la vez. Antes se cruzaban —el que salía aún al 65 % cuando el
       que entraba ya iba por el 85 %— y dos capas legibles superpuestas se
       leen como un defecto, no como una transición. Ahora el saliente cae
       rápido y el entrante empieza cuando ya casi no está. */
    .cap-entra {
      animation: cap-entra 0.26s cubic-bezier(0.22, 0.8, 0.3, 1) both;
      animation-delay: 80ms;
    }
    @keyframes cap-entra {
      from { opacity: 0; transform: translate3d(0, 12px, 0); }
      to   { opacity: 1; transform: translate3d(0, 0, 0); }
    }

    /* El título que sustituye al botón elegido arranca donde estaba el
       botón y con parte de su tamaño, y viaja hasta su sitio: se ve cómo
       uno se convierte en el otro. El desplazamiento y la escala los
       calcula el JS al vuelo (no se pueden saber de antemano) y los pasa
       por estas variables. */
    .cap-morph { animation: cap-morph 0.32s cubic-bezier(0.22, 0.8, 0.3, 1) both; }
    @keyframes cap-morph {
      from {
        opacity: 0.5;
        transform: translate3d(var(--mx, 0px), var(--my, 0px), 0) scale(var(--ms, 1));
      }
      to { opacity: 1; transform: translate3d(0, 0, 0) scale(1); }
    }

    /* Nada de esto es información: si se pide menos movimiento, el cambio
       de paso es instantáneo y ya. */
    @media (prefers-reduced-motion: reduce) {
      .cap-ghost { display: none !important; }
      .cap-entra, .cap-morph { animation: none !important; }
    }

    /* Tipo de envío ya elegido: deja de ser un botón y pasa a ser el
       encabezado del formulario. Sin borde ni fondo de tarjeta a propósito
       — si siguiera pareciendo un botón, el operador volvería a pulsarlo. */
    .cap-pmtipo-elegido {
      display: flex;
      align-items: baseline;
      flex-wrap: wrap;
      gap: var(--esp-1) var(--esp-3);
      margin-bottom: var(--esp-4);
      padding-bottom: var(--esp-3);
      border-bottom: 2px solid rgba(255, 209, 0, 0.55);
    }
    .cap-pmtipo-titulo {
      font-size: var(--fs-base);
      font-weight: 700;
      letter-spacing: var(--track-etiqueta);
      color: var(--gold);
      line-height: var(--lh-titulo);
    }
    .cap-pmtipo-sub {
      font-size: var(--fs-s);
      color: var(--text-muted);
      flex: 1;
    }
    /* Discreto y a un lado: es una salida por si se equivocó, no una acción
       que haya que ofrecerle con el mismo peso que el formulario. */
    .cap-pmtipo-cambiar {
      background: none;
      border: none;
      padding: var(--esp-1) var(--esp-1);
      font-family: inherit;
      font-size: var(--fs-s);
      color: var(--text-muted);
      text-decoration: underline;
      cursor: pointer;
    }
    .cap-pmtipo-cambiar:hover { color: var(--cyan-accent); }

    .cap-substep-num {
      width: 22px; height: 22px;
      border-radius: 50%;
      background: var(--gold);
      color: #1a1208;
      font-size: var(--fs-s);
      font-weight: 700;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      flex-shrink: 0;
    }
    .cap-substep.done .cap-substep-num { background: var(--positive); color: #fff; }

    /* Componente VisionConfirmation */
    .cap-vc {
      background: linear-gradient(135deg, rgba(201,169,97,0.08), rgba(201,169,97,0.03));
      border: 1px solid var(--border-gold);
      border-radius: 10px;
      padding: var(--esp-3);
      margin-top: var(--esp-2);
    }
    .cap-vc-title {
      font-size: var(--fs-s);
      font-weight: 600;
      color: var(--text-dark);
      margin-bottom: var(--esp-2);
    }
    .cap-vc-data {
      display: grid;
      grid-template-columns: auto 1fr;
      gap: var(--esp-1) var(--esp-3);
      font-size: var(--fs-m);
      margin-bottom: var(--esp-3);
    }
    .cap-vc-key { color: var(--text-muted); }
    .cap-vc-val { color: var(--text-dark); font-weight: 600; }
    .cap-vc-val.null { color: var(--text-muted); font-weight: 400; font-style: italic; }
    .cap-vc-actions { display: flex; gap: var(--esp-2); flex-wrap: wrap; }
    .cap-vc-counter {
      font-size: var(--fs-s);
      color: var(--text-muted);
      margin-left: auto;
      align-self: center;
    }
    .cap-vc-counter.warn { color: var(--warning); font-weight: 600; }

    /* ── Los datos del comprobante, en líneas ──────────────────────────
       Cinco líneas pegadas a la izquierda, una por dato, todas del mismo
       tamaño. Antes eran cinco campos abiertos en dos columnas con título y
       párrafo: ocupaba media pantalla y el operador tenía que desplazarse
       para llegar al botón de guardar. Un dato que ya está bien no necesita
       una casilla abierta — necesita verse y poder tocarse. */
    .cap-cl { margin-top: var(--esp-2); display: flex; flex-direction: column; gap: var(--esp-1)}
    .cap-cl-linea {
      display: flex; align-items: center; gap: var(--esp-2);
      padding: var(--esp-1) var(--esp-1); border-radius: 6px; cursor: pointer;
      font-size: var(--fs-m); line-height: var(--lh-cuerpo); text-align: left;
      background: transparent; border: 0; width: 100%; font-family: inherit;
    }
    .cap-cl-linea:hover { background: rgba(255,255,255,.05) }
    /* La línea que leyó la IA no invita a tocarse: ni cursor de mano ni
       realce al pasar por encima. */
    .cap-cl-linea.fija { cursor: default }
    .cap-cl-linea.fija:hover { background: transparent }
    .cap-cl-k { color: var(--text-muted); min-width: 74px }
    /* Lo que leyó la IA va en verde. Vacío o escrito a mano se distingue sin
       gritar: el verde dice "esto lo puso la foto", no "esto está bien". */
    .cap-cl-v { color: var(--verde); font-weight: 600; flex: 1; min-width: 0 }
    .cap-cl-v.manual { color: var(--text-dark); font-weight: 600 }
    .cap-cl-v.vacio  { color: var(--text-muted); font-weight: 400; font-style: italic }
    .cap-cl-chip {
      font-size: var(--fs-xs); letter-spacing: var(--track-etiqueta); text-transform: uppercase;
      padding: var(--esp-1) var(--esp-1); border-radius: 999px; border: 1px solid currentColor;
      opacity: .55; flex: none;
    }
    .cap-cl-chip.ia     { color: var(--verde) }
    .cap-cl-chip.manual { color: var(--text-muted) }
    .cap-cl-lapiz { color: var(--text-muted); opacity: .45; font-size: var(--fs-s); flex: none }
    .cap-cl-linea:hover .cap-cl-lapiz { opacity: .9 }
    /* En edición la línea no se mueve de sitio: la casilla ocupa el hueco
       del valor y nada salta. */
    /* [hidden] no gana sobre display:flex: sin esto, la casilla cerrada
       seguía ocupando sus 30 px en cada línea — invisible, pero el bloque
       pesaba casi lo mismo que el recuadro que vino a sustituir. */
    .cap-cl-edit[hidden] { display: none }
    .cap-cl-edit { flex: 1; min-width: 0; display: flex; gap: var(--esp-1); align-items: center }
    .cap-cl-edit input { width: 100%; min-height: 30px; padding: var(--esp-1) var(--esp-2); font-size: var(--fs-m)}
    @media (max-width: 520px) {
      .cap-cl-k { min-width: 64px; font-size: var(--fs-m)}
      .cap-cl-linea { font-size: var(--fs-m)}
    }

    /* Panel "Promedio del oro" (sólo Pago Móvil — vive bajo la zona de
       carga de comprobante en el modal de captura). Bs/g calculado en
       vivo desde amount_bs / gramos. */
    .cap-promedio-oro {
      margin-top: var(--esp-3);
      padding: var(--esp-2) var(--esp-3);
      background: rgba(201,169,97,0.10);
      border: 1px solid var(--border-gold);
      border-left-width: 3px;
      border-radius: 8px;
      display: flex;
      align-items: baseline;
      gap: var(--esp-2);
      flex-wrap: wrap;
    }
    .cap-promedio-oro-label {
      font-size: var(--fs-s);
      color: var(--text-muted);
      font-weight: 600;
    }
    .cap-promedio-oro-value {
      font-family: var(--font-sans);
      color: var(--gold-bright);
      font-weight: 700;
      font-size: var(--fs-base);
      letter-spacing: var(--track-sutil);
      font-variant-numeric: tabular-nums;
    }

    /* Indicador de confianza de Vision (báscula) */
    .cap-conf-badge {
      display: inline-block;
      font-size: var(--fs-xs);
      font-weight: 700;
      letter-spacing: var(--track-sutil);
      padding: var(--esp-1) var(--esp-2);
      border-radius: 999px;
      margin-left: var(--esp-1);
      vertical-align: middle;
      text-transform: uppercase;
    }
    .cap-conf-badge.baja  { background: #c0392b; color: #fff; }

    /* Estado bloqueado (campos llenados por Vision) */
    .cap-locked-label::after {
      content: ' 🔒';
      font-size: var(--fs-s);
      color: var(--positive);
    }
    .cap-locked-label.override::after {
      content: ' ✏ (manual)';
      font-size: var(--fs-xs);
      color: var(--warning);
      font-style: italic;
    }

    /* Medio de pago selector */
    .cap-medio-grid {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      gap: var(--esp-2);
    }
    .cap-medio-btn {
      position: relative;
      padding: var(--esp-4) var(--esp-2) var(--esp-3);
      border: 1px solid var(--border-soft);
      border-radius: 12px;
      background: var(--bg-card);
      cursor: pointer;
      font-size: var(--fs-m);
      font-weight: 500;
      color: var(--text-secondary);
      text-align: center;
      transition: border-color 0.18s, background 0.18s, color 0.18s, transform 0.15s, box-shadow 0.18s;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      gap: var(--esp-2);
      min-height: 92px;
    }
    .cap-medio-btn .cap-medio-icon {
      width: 28px;
      height: 28px;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      color: var(--gold-dim);
      transition: color 0.18s, transform 0.18s;
    }
    .cap-medio-btn .cap-medio-icon svg { width: 100%; height: 100%; display: block; }
    .cap-medio-btn .cap-medio-label {
      font-size: var(--fs-m);
      line-height: var(--lh-titulo);
      letter-spacing: var(--track-sutil);
    }
    .cap-medio-btn:hover {
      border-color: var(--gold);
      background: var(--bg-elevated);
      color: var(--text-primary);
      transform: translateY(-1px);
      box-shadow: 0 6px 18px rgba(201,169,97,0.18);
    }
    .cap-medio-btn:hover .cap-medio-icon { color: var(--gold); }
    .cap-medio-btn.selected {
      background: var(--bg-gold-grad);
      color: var(--gold-bright);
      border-color: var(--gold);
      font-weight: 700;
      box-shadow: 0 0 0 1px var(--gold), 0 8px 28px rgba(201,169,97,0.28);
    }
    .cap-medio-btn.selected .cap-medio-icon {
      color: var(--gold-bright);
      transform: scale(1.08);
    }
    @media (max-width: 540px) {
      .cap-medio-grid { grid-template-columns: 1fr; }
      .cap-medio-btn { min-height: 72px; flex-direction: row; gap: var(--esp-3); padding: var(--esp-3) var(--esp-3); }
      .cap-medio-btn .cap-medio-icon { width: 24px; height: 24px; }
    }

    /* ── ¿Qué trae el cliente? Oro o billetes ─────────────────────────
       Pago Móvil pagado con dólares en efectivo. Reusa .cap-medio-btn (los
       mismos botones de "medio de pago", con su estado .selected dorado);
       lo único propio es que son DOS, no tres. */
    /* Un campo de dinero no se estira a lo ancho de la hoja: un monto son
       pocos dígitos y un campo de 900px invita a mirar donde no hay nada. */
    .u-ancho-320 { max-width: 320px }

    /* ── EL TRABAJO DEL MOSTRADOR CON LOS DEPÓSITOS ───────────────────
       El cartel del bloque de avisos: comprobantes esperando OK y depósitos
       que volvieron por datos incorrectos. Los colores significan:
       ámbar = alguien está esperando · rojo = algo no cuadra. */
    .muro-aviso { margin-bottom: var(--esp-3) }
    .muro-aviso.espera  { border: 1px solid rgba(214,137,16,.55) }
    .muro-aviso.urgente { border: 1px solid rgba(224,90,90,.55) }
    .muro-mal { color: var(--negative); font-weight: 600 }
    .muro-filas {
      margin-top: var(--esp-2);
      display: flex;
      flex-direction: column;
      gap: var(--esp-2);
    }
    .muro-fila {
      display: flex;
      align-items: flex-start;
      gap: var(--esp-3);
      padding-top: var(--esp-2);
      border-top: 1px solid var(--border-soft);
    }
    .muro-filas > .muro-fila:first-child { border-top: 0; padding-top: 0 }
    .muro-fila-datos { flex: 1; min-width: 0 }
    .muro-fila-botones {
      display: flex;
      flex-direction: column;
      gap: var(--esp-2);
      flex-shrink: 0;
    }
    /* La miniatura del comprobante: se toca y se abre el visor con zoom. */
    .muro-mini {
      width: 56px; height: 56px;
      object-fit: cover;
      border-radius: 8px;
      border: 1px solid var(--border-soft);
      cursor: pointer;
      flex-shrink: 0;
      background: var(--bg-elevated);
    }
    .muro-mini:hover { border-color: var(--gold) }
    /* En el teléfono la fila se apila: con 390px de ancho, el texto y dos
       botones al lado se quedan en tres letras por renglón. */
    @media (max-width: 640px) {
      .muro-fila { flex-wrap: wrap }
      .muro-fila-datos { flex-basis: 100%; order: 2 }
      .muro-fila-botones { flex-direction: row; flex-wrap: wrap; order: 3; flex-basis: 100% }
      .muro-fila-botones .btn { flex: 1; min-height: 44px }
    }
    /* La salida al camino secundario, separada por una línea de puntos para
       que no se confunda con los campos del paso. */
    .cap-volver-buzon {
      margin-top: var(--esp-3);
      padding-top: var(--esp-3);
      border-top: 1px dashed var(--border-soft);
    }
    .cap-pagacon-grid {
      display: grid;
      grid-template-columns: repeat(2, 1fr);
      gap: var(--esp-2);
    }
    @media (max-width: 540px) {
      .cap-pagacon-grid { grid-template-columns: 1fr }
    }
    /* El lingote isométrico del botón de oro. Las tres caras son el mismo
       dorado a tres luces: es lo que lo hace leerse como un lingote y no como
       un polígono. Los colores son los de la marca, no decoración. */
    .cap-oro-cara-lado   { fill: var(--gold-dim);    stroke: var(--gold-dim); stroke-width: 0.5 }
    .cap-oro-cara-frente { fill: var(--gold);        stroke: var(--gold-dim); stroke-width: 0.5 }
    .cap-oro-cara-arriba { fill: var(--gold-bright); stroke: var(--gold-dim); stroke-width: 0.5 }

    /* ── El recuadro de una cuenta hecha a la vista ───────────────────
       "El cliente debe entregar $X". La cifra es grande a propósito: es el
       número que el operador le dice en voz alta al cliente. */
    .cap-calc-box {
      background: var(--bg-elevated);
      border: 1px solid var(--border-soft);
      border-radius: 8px;
      padding: var(--esp-3);
    }
    .cap-calc-cifra {
      font-size: var(--fs-xl);
      font-weight: 700;
      color: var(--gold-bright);
      line-height: var(--lh-titulo);
    }
    /* La diferencia entre lo calculado y lo contado. No bloquea nada: es un
       hecho que el admin tiene que ver, no un error que esconder. Rojo falta,
       ámbar sobra, verde cuadra — los mismos colores de siempre. */
    .cap-calc-aviso {
      margin-top: var(--esp-2);
      font-size: var(--fs-m);
      font-weight: 600;
    }
    .cap-calc-aviso.falta  { color: var(--negative) }
    .cap-calc-aviso.sobra  { color: var(--warning) }
    .cap-calc-aviso.cuadra { color: var(--positive) }

    /* ═══════════════════════════════════════════════════════════════════
       RESPONSIVE
    ══════════════════════════════════════════════════════════════════════ */
    /* Tablet — columnas más angostas, tarjetas más chicas */
    @media (max-width: 1024px) and (min-width: 769px) {
      #content { padding: var(--esp-5); }
      .kpi-grid { grid-template-columns: repeat(2, 1fr); }
    }

    /* Móvil — una sola columna */
    @media (max-width: 768px) {
      #topbar-title { font-size: var(--fs-m); }
      #topbar-date { display: none; }

      #content { padding: var(--esp-3); }

      /* Registrar en móvil: ocupa todo el ancho disponible (el max-width
         de 400px sigue mandando en pantallas medianas). */
      #registrar-bar { margin-bottom: var(--esp-4); }
      #btn-registrar-main {
        max-width: none;
        min-height: 60px;
        padding: var(--esp-4) var(--esp-5);
        font-size: var(--fs-m);
      }
      #btn-registrar-main .reg-plus { font-size: var(--fs-xl); }

      .kpi-grid     { grid-template-columns: 1fr; }
      .disp-persona-grid,
      .cap-tipos-grid{ grid-template-columns: 1fr; }
      .form-grid    { grid-template-columns: 1fr; }
      .form-group.span2 { grid-column: span 1; }
      #cap-form .form-grid { grid-template-columns: 1fr; }

      /* Botones touch-friendly */
      .btn, button.btn, .nav-item,
      .cap-tipo-card, .cap-ac-item{ min-height: 44px; }

      /* Modales — casi pantalla completa */
      #edit-overlay, #destino-overlay, #auth-overlay,
      #history-overlay, #persona-overlay {
        padding: var(--esp-2);
      }
      .modal {
        width: 95vw;
        max-width: 95vw;
        max-height: 95vh;
      }
      .modal-body { padding: var(--esp-4); }
      .modal-header { padding: var(--esp-3) var(--esp-4) var(--esp-3); }
      .modal-footer { padding: var(--esp-3) var(--esp-4); }

      /* Tablas grandes con scroll horizontal */
      .table-wrap, .pm-table-wrap,
      .cu-table-wrap{
        -webkit-overflow-scrolling: touch;
        overflow-x: auto;
      }
      table { font-size: var(--fs-s); }

    }

    @media (max-width: 420px) {
      .kpi-grid { grid-template-columns: 1fr; }
    }

    /* ── Panel de respaldos ────────────────────────────────────────
       Va ARRIBA del todo en Configuración general y a propósito: es lo
       único de esta pantalla que, si falla, se lleva el negocio entero.
       En el camino feliz no pide nada — dice cuándo fue el último y ya. */
    .resp-panel {
      background: var(--bg-card);
      border: 1px solid var(--border-subtle);
      border-radius: var(--radius);
      padding: var(--esp-4);
      margin-top: var(--esp-4);
    }
    /* Sin respaldo o con más de 48 h: el borde entero se pone rojo. Un
       respaldo que falla en silencio es igual a no tener respaldo, así que
       esto no puede parecer un detalle más de la lista. */
    .resp-panel.alerta { border-color: rgba(217,116,116,0.55); }

    .resp-encabezado {
      display: flex; align-items: center; justify-content: space-between;
      gap: var(--esp-3); flex-wrap: wrap; margin-bottom: var(--esp-3);
    }
    .resp-titulo { font-size: var(--fs-base); font-weight: 700; color: var(--text-primary); }
    .resp-sub { font-size: var(--fs-s); color: var(--text-muted); margin-top: var(--esp-1); }

    .resp-banner {
      display: flex; align-items: flex-start; gap: var(--esp-2);
      padding: var(--esp-3) var(--esp-3); border-radius: var(--radius-sm);
      background: var(--negative-bg);
      border: 1px solid rgba(217,116,116,0.35);
      color: var(--negative);
      font-size: var(--fs-m); line-height: var(--lh-cuerpo);
      margin-bottom: var(--esp-3);
    }
    .resp-banner-icono { font-size: var(--fs-base); line-height: var(--lh-titulo); }

    .resp-estado {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
      gap: var(--esp-3);
      margin-bottom: var(--esp-3);
    }
    .resp-dato {
      background: var(--bg-input);
      border: 1px solid var(--border-subtle);
      border-radius: var(--radius-sm);
      padding: var(--esp-2) var(--esp-3);
      min-width: 0;
    }
    .resp-dato-label {
      font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: var(--track-etiqueta);
      color: var(--text-muted); margin-bottom: var(--esp-1);
    }
    .resp-dato-valor {
      font-size: var(--fs-m); font-weight: 600; color: var(--text-primary);
      overflow-wrap: anywhere;
    }
    .resp-dato-valor.ok   { color: var(--positive); }
    .resp-dato-valor.mal  { color: var(--negative); }

    .resp-lista { margin-top: var(--esp-1); }
    .resp-lista-titulo {
      font-size: var(--fs-s); text-transform: uppercase; letter-spacing: var(--track-etiqueta);
      color: var(--text-muted); margin: var(--esp-4) 0 var(--esp-2);
    }
    .resp-fila {
      display: flex; align-items: center; gap: var(--esp-2);
      padding: var(--esp-2) 0;
      border-bottom: 1px solid var(--border-subtle);
      font-size: var(--fs-s);
    }
    .resp-fila:last-child { border-bottom: none; }
    .resp-fila-nombre { flex: 1; min-width: 0; overflow-wrap: anywhere; font-family: ui-monospace, monospace; font-size: var(--fs-s); }
    .resp-fila-peso { color: var(--text-muted); white-space: nowrap; }
    .resp-fila-tag {
      font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: var(--track-etiqueta);
      padding: var(--esp-1) var(--esp-1); border-radius: 4px; white-space: nowrap;
    }
    .resp-fila-tag.ok  { background: rgba(90,181,116,0.15); color: var(--positive); }
    .resp-fila-tag.mal { background: rgba(217,116,116,0.15); color: var(--negative); }
    .resp-error-txt { color: var(--negative); font-size: var(--fs-s); overflow-wrap: anywhere; }

    /* Listas plegadas: a la vista sólo la más reciente. La lista entera
       estiraba el panel hasta dejar el resto de Configuración fuera de
       pantalla. Lo de dentro son exactamente las mismas filas. */
    .resp-vermas {
      background: none; border: none; cursor: pointer;
      padding: var(--esp-2) 0 var(--esp-1); margin: 0;
      font: inherit; font-size: var(--fs-s);
      color: var(--gold); text-align: left;
    }
    .resp-vermas:hover { text-decoration: underline; }
    .resp-plegado[hidden] { display: none; }

    /* ── Purga de datos antiguos ────────────────────────────────────
       Vive en Configuración general, debajo del respaldo y a propósito:
       lo primero que se ve encima es la copia de seguridad. El panel va
       con borde rojo permanente —no como aviso puntual— porque lo que
       hace no se deshace. */
    .purga-panel { border-color: rgba(217,116,116,0.38); }
    .purga-paso {
      margin-top: var(--esp-4); padding-top: var(--esp-3);
      border-top: 1px solid var(--border-subtle);
    }
    .purga-paso:first-of-type { border-top: none; padding-top: 0; }
    .purga-campos {
      display: flex; flex-wrap: wrap; gap: var(--esp-3); align-items: flex-end;
      margin-top: var(--esp-2);
    }
    .purga-campo { display: flex; flex-direction: column; gap: var(--esp-1); min-width: 160px; }
    .purga-campo label {
      font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: var(--track-etiqueta);
      color: var(--text-muted);
    }
    /* Las dos listas del "antes": qué se va y qué se queda, una al lado de
       la otra. Puestas juntas no hace falta creerle a nadie: se leen. */
    .purga-listas {
      display: grid; grid-template-columns: repeat(auto-fit, minmax(270px, 1fr));
      gap: var(--esp-3); margin-top: var(--esp-3);
    }
    .purga-lista {
      background: var(--bg-input);
      border: 1px solid var(--border-subtle);
      border-radius: var(--radius-sm);
      padding: var(--esp-3) var(--esp-3);
    }
    .purga-lista.borra    { border-color: rgba(217,116,116,0.42); }
    .purga-lista.conserva { border-color: rgba(90,181,116,0.38); }
    .purga-lista-tit {
      font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: var(--track-etiqueta);
      margin-bottom: var(--esp-2); font-weight: 700;
    }
    .purga-lista.borra    .purga-lista-tit { color: var(--negative); }
    .purga-lista.conserva .purga-lista-tit { color: var(--positive); }
    .purga-lista ul {
      margin: 0; padding-left: var(--esp-4);
      font-size: var(--fs-s); line-height: var(--lh-cuerpo); color: var(--text-secondary);
    }
    .purga-lista li { margin-bottom: var(--esp-1); }
    .purga-frase {
      font-family: ui-monospace, monospace; font-weight: 700;
      color: var(--negative); letter-spacing: var(--track-sutil);
    }

    /* ── Los tres cuadros que se leen de lejos ──────────────────────────
       "Fotos leídas por la IA", "Cuentas USDT" y "Borrar datos antiguos"
       no se rellenan como un formulario: se consultan, muchas veces de
       pie y de un vistazo. Van dos puntos por encima del resto de la app
       — la misma letra y los mismos colores de La Libreta, sólo más
       grandes. Las medidas van todas juntas aquí, con el id del panel
       delante: las clases son compartidas, y sin el id esto le subiría la
       letra al panel de respaldos y a media app de paso. */
    #vision-uso-panel .card-title,
    #cuentas-usdt-panel .card-title  { font-size: var(--fs-base); }
    #vision-uso-panel table,
    #vision-uso-panel td             { font-size: var(--fs-m); }
    #vision-uso-panel th             { font-size: var(--fs-xs); }
    #vision-uso-panel .pm-history-link { font-size: var(--fs-m); }

    #cuentas-usdt-panel .cfg-card-title { font-size: var(--fs-base); }
    #cuentas-usdt-panel .cfg-card-desc  { font-size: var(--fs-m); }
    #cuentas-usdt-panel .ci-empty       { font-size: var(--fs-base); }
    #cuentas-usdt-panel .badge          { font-size: var(--fs-xs); }

    #purga-panel .resp-titulo        { font-size: var(--fs-base); }
    #purga-panel .resp-sub           { font-size: var(--fs-m); }
    #purga-panel .resp-dato-label    { font-size: var(--fs-s); }
    #purga-panel .resp-dato-valor    { font-size: var(--fs-base); }
    #purga-panel .resp-lista-titulo  { font-size: var(--fs-m); }
    #purga-panel .resp-error-txt     { font-size: var(--fs-m); }
    #purga-panel .purga-campo label  { font-size: var(--fs-s); }
    #purga-panel .purga-lista-tit    { font-size: var(--fs-s); }
    #purga-panel .purga-lista ul     { font-size: var(--fs-m); }
    #purga-panel input,
    #purga-panel select              { font-size: var(--fs-m); }

    /* Los botones, después de lo demás: .btn-sm tiene que ganarle a .btn
       en los elementos que llevan las dos clases. */
    #purga-panel .btn                { font-size: var(--fs-s); }
    #vision-uso-panel .btn-sm,
    #cuentas-usdt-panel .btn-sm,
    #purga-panel .btn-sm             { font-size: var(--fs-s); }

    /* ── Mi Nómina — la pantalla del operador ───────────────────────────
       Es donde alguien mira cuánto va a cobrar y por qué le descuentan.
       Se lee de pie, con prisa y a veces desde el teléfono, así que va un
       punto por encima del resto de la app y lo que más importa —la cifra
       y el motivo de cada multa— es lo más grande de su bloque. Los
       colores y las formas son los de La Libreta: mismas tarjetas, mismo
       oro, misma serif en cursiva para los nombres. */
    .mn-card .card-header {
      display: flex; align-items: center; justify-content: space-between;
      gap: var(--esp-3); flex-wrap: wrap;
    }
    /* El rótulo que deja claro que aquí no se toca nada. */
    .mn-solo-lectura {
      font-size: var(--fs-s); color: var(--text-muted);
      border: 1px solid var(--border-subtle); border-radius: 999px;
      padding: var(--esp-1) var(--esp-3); letter-spacing: var(--track-etiqueta); text-transform: uppercase;
      white-space: nowrap;
    }
    .mn-encabezado { margin-bottom: var(--esp-4); }
    .mn-nombre {
      font-family: var(--font-serif); font-style: italic;
      font-size: var(--fs-l); font-weight: bold; color: var(--text-primary);
    }
    .mn-cargo { font-size: var(--fs-base); color: var(--text-muted); font-style: italic; margin-top: var(--esp-1); }

    /* La cifra: lo primero y lo más grande de la pantalla. */
    .mn-cobro {
      text-align: center; padding: var(--esp-4) var(--esp-3); margin-bottom: var(--esp-4);
      border: 1px solid var(--border-subtle); border-radius: 14px;
      background: var(--bg-input);
    }
    .mn-cobro-rotulo {
      font-size: var(--fs-s); text-transform: uppercase; letter-spacing: var(--track-ancho);
      color: var(--text-muted);
    }
    .mn-cobro-cifra { font-size: var(--fs-2xl); line-height: var(--lh-plano); margin: var(--esp-2) 0 var(--esp-1); font-weight: bold; }
    .mn-cobro-sub { font-size: var(--fs-base); color: var(--text-secondary); font-style: italic; }

    /* La cuenta, escrita como se escribiría en un papel: el salario arriba,
       las restas debajo y la raya antes del total. Es la respuesta a "¿por
       qué cobro esto?" sin que haya que hacer ninguna cuenta de cabeza. */
    .mn-cuenta {
      border: 1px solid var(--border-subtle); border-radius: 12px;
      padding: var(--esp-1) var(--esp-4); margin-bottom: var(--esp-4);
    }
    .mn-cuenta-fila {
      display: flex; align-items: baseline; justify-content: space-between;
      gap: var(--esp-4); padding: var(--esp-2) 0; font-size: var(--fs-base); color: var(--text-secondary);
    }
    .mn-cuenta-fila + .mn-cuenta-fila { border-top: 1px solid var(--border-subtle); }
    .mn-cuenta-monto { font-weight: 600; color: var(--text-primary); white-space: nowrap; }
    .mn-cuenta-fila.resta .mn-cuenta-monto { color: var(--negative); }
    .mn-cuenta-fila.total {
      border-top: 2px solid var(--border-subtle);
      font-size: var(--fs-base); font-weight: 700; color: var(--text-primary);
    }
    .mn-cuenta-fila.total .mn-cuenta-monto { font-size: var(--fs-l); }

    .mn-datos {
      display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: var(--esp-3);
    }
    .mn-dato {
      background: var(--bg-input); border: 1px solid var(--border-subtle);
      border-radius: 10px; padding: var(--esp-3) var(--esp-3); min-width: 0;
    }
    .mn-dato-rotulo {
      font-size: var(--fs-s); text-transform: uppercase; letter-spacing: var(--track-etiqueta);
      color: var(--text-muted); margin-bottom: var(--esp-1);
    }
    .mn-dato-valor { font-size: var(--fs-base); font-weight: 600; color: var(--text-primary); overflow-wrap: anywhere; }

    /* Las multas. El motivo va grande y arriba: es la respuesta a la única
       pregunta que se hace quien ve un descuento en su pago. */
    .mn-multas-total { font-size: var(--fs-base); font-weight: 700; color: var(--negative); white-space: nowrap; }
    .mn-multas-nota { font-size: var(--fs-m); color: var(--text-muted); line-height: var(--lh-cuerpo); margin-bottom: var(--esp-3); }
    .mn-multa {
      /* El 140 de la derecha es maquetación: deja sitio a lo que va encima.
         El resto es espaciado y va en la escala. */
      position: relative; margin-bottom: var(--esp-2); padding: var(--esp-3) 140px var(--esp-3) var(--esp-3);
      border: 1px solid var(--border-subtle); border-left: 3px solid var(--negative);
      border-radius: 10px;
    }
    .mn-multa-motivo { font-size: var(--fs-base); color: var(--text-primary); line-height: var(--lh-cuerpo); }
    .mn-multa-pie {
      font-size: var(--fs-m); color: var(--text-muted); margin-top: var(--esp-1);
      display: flex; gap: var(--esp-3); flex-wrap: wrap;
    }
    .mn-multa-monto {
      position: absolute; right: 14px; top: 12px;
      font-size: var(--fs-l); font-weight: 700; color: var(--negative); white-space: nowrap;
    }
    .mn-sin-multas { font-size: var(--fs-base); color: var(--text-muted); }

    /* Las solicitudes de adelanto que hizo él. No son descuentos —todavía no
       son nada— así que NO se pintan de rojo como una multa: la que espera va
       en el ámbar de "falta algo por pasar", la aprobada en verde y la negada
       en gris. El color es la primera respuesta: en qué quedó lo que pidió. */
    .mn-sol {
      margin-top: var(--esp-2); padding: var(--esp-3) var(--esp-3); border-radius: 10px;
      border: 1px solid var(--border-subtle); border-left: 3px solid var(--text-muted);
    }
    .mn-sol.pendiente { border-left-color: var(--warning); }
    .mn-sol.aprobada  { border-left-color: var(--positive); }
    .mn-sol-titulo { font-size: var(--fs-base); color: var(--text-primary); line-height: var(--lh-cuerpo); }
    .mn-sol-pie    { font-size: var(--fs-m); color: var(--text-muted); margin-top: var(--esp-1); line-height: var(--lh-cuerpo); }
    .mn-sol-sep {
      margin: var(--esp-4) 0 0; padding-top: var(--esp-3); border-top: 1px solid var(--border-subtle);
    }
    .mn-sol-sep-titulo {
      font-size: var(--fs-s); text-transform: uppercase; letter-spacing: var(--track-etiqueta);
      color: var(--text-muted);
    }

    /* ── Lo que ya cobraste, mes por mes ──────────────────────────────
       Era una lista corrida. Al año son cincuenta y pico de renglones que
       nadie termina de leer; plegada por mes son doce, y el que quiere
       saber cuánto cobró en agosto lo tiene escrito en el renglón sin
       abrir nada. */
    .mn-mes { border-top: 1px solid var(--border-subtle); }
    .mn-mes:first-child { border-top: none; }

    /* El renglón es un BOTÓN de verdad: en la tablet se toca con el dedo y
       con el teclado se llega con el tabulador. 56px de alto es el piso por
       debajo del cual un dedo empieza a fallar, y el área que se puede
       tocar es el renglón entero, no la flechita. */
    .mn-mes-cab {
      width: 100%; min-height: 56px; display: flex; align-items: center; gap: var(--esp-3);
      padding: var(--esp-3) var(--esp-4); background: none; border: none; cursor: pointer;
      text-align: left; color: inherit; font: inherit;
      -webkit-tap-highlight-color: transparent;
    }
    .mn-mes-cab:hover  { background: rgba(255, 255, 255, 0.03); }
    .mn-mes-cab:active { background: rgba(255, 255, 255, 0.06); }
    .mn-mes-cab:focus-visible { outline: 2px solid var(--oro); outline-offset: -2px; }
    .mn-mes-flecha {
      flex: none; width: 18px; color: var(--text-muted); font-size: var(--fs-m);
      transition: transform 0.18s ease;
    }
    .mn-mes[data-abierto="1"] .mn-mes-flecha { transform: rotate(90deg); }
    .mn-mes-nombre  { flex: 1; font-size: var(--fs-base); font-weight: 600; min-width: 0; }
    .mn-mes-cuantos { font-size: var(--fs-m); color: var(--text-muted); white-space: nowrap; }
    .mn-mes-total   { font-size: var(--fs-base); font-weight: 700; white-space: nowrap; }

    /* El despliegue se abre suave sin que nadie tenga que medir alturas a
       mano: la rejilla pasa de 0fr a 1fr y el navegador anima hasta el alto
       que de verdad ocupa el contenido. */
    .mn-mes-panel {
      display: grid; grid-template-rows: 0fr;
      transition: grid-template-rows 0.25s ease;
    }
    .mn-mes[data-abierto="1"] .mn-mes-panel { grid-template-rows: 1fr; }
    .mn-mes-panel > div { overflow: hidden; }
    @media (prefers-reduced-motion: reduce) {
      .mn-mes-panel, .mn-mes-flecha { transition: none; }
    }

    .mn-cobro-item {
      margin: 0 var(--esp-4) var(--esp-2); padding: var(--esp-3) var(--esp-3);
      border: 1px solid var(--border-subtle); border-left: 3px solid var(--positive);
      border-radius: 10px;
    }
    .mn-cobro-item:first-child { margin-top: var(--esp-1); }
    .mn-cobro-cab {
      display: flex; justify-content: space-between; gap: var(--esp-2); flex-wrap: wrap;
      margin-bottom: var(--esp-2);
    }
    .mn-cobro-per   { font-size: var(--fs-base); color: var(--text-primary); }
    .mn-cobro-fecha { font-size: var(--fs-m); color: var(--text-muted); white-space: nowrap; }
    .mn-cobro-linea {
      display: flex; justify-content: space-between; gap: var(--esp-3);
      font-size: var(--fs-m); padding: var(--esp-1) 0; color: var(--text-secondary);
    }
    .mn-cobro-linea.recibiste {
      margin-top: var(--esp-1); padding-top: var(--esp-2); border-top: 1px solid var(--border-subtle);
      color: var(--text-primary); font-weight: 700;
    }
    .mn-cobro-linea .mn-cobro-cifra { white-space: nowrap; }

    @media (max-width: 640px) {
      /* En pantalla angosta el "cuántos cobros" sobra antes que el total:
         el número es lo que se viene a ver. */
      .mn-mes-cuantos { display: none; }
      .mn-mes-cab     { padding: var(--esp-3) var(--esp-3); }
      .mn-cobro-item  { margin-left: var(--esp-3); margin-right: var(--esp-3); }
    }

    /* Las dos pantallas del módulo se leen como el resto de Configuración:
       la tabla, dos puntos por encima de la de la app, y desplazándose
       dentro de su tarjeta antes que empujar la página de lado. */
    #section-mi_nomina table,
    #section-nomina table    { font-size: var(--fs-m); }
    #section-mi_nomina th,
    #section-nomina th       { font-size: var(--fs-xs); }
    #section-mi_nomina .card-body,
    #section-nomina .card-body { overflow-x: auto; }

    @media (max-width: 640px) {
      /* En el teléfono el monto se va debajo del motivo: al lado no cabe
         sin partir la frase en una palabra por línea. */
      .mn-multa { padding: var(--esp-3) var(--esp-3); }
      .mn-multa-monto { position: static; display: block; margin-top: var(--esp-2); }
      .mn-cobro-cifra { font-size: var(--fs-2xl); }
      #section-mi_nomina th, #section-nomina th { padding-left: var(--esp-2); padding-right: var(--esp-2); }
      #section-mi_nomina td, #section-nomina td { padding-left: var(--esp-2); padding-right: var(--esp-2); }
    }

    /* Con la letra más grande, la tabla de cuatro columnas del contador de
       la IA no cabe en un teléfono. Que se desplace dentro de su cuadro es
       mejor que volver a encogerla o que empuje la página entera de lado. */
    #vision-uso-panel .card-body,
    #cuentas-usdt-panel .card-body   { overflow-x: auto; }
    /* Y en el teléfono se le quita aire a las celdas, para que haya que
       desplazar lo menos posible. */
    @media (max-width: 640px) {
      #vision-uso-panel th,
      #vision-uso-panel td { padding-left: var(--esp-2); padding-right: var(--esp-2); }
      #vision-uso-panel .card-body,
      #cuentas-usdt-panel .card-body { padding: var(--esp-4) var(--esp-3); }
    }

    /* ── Configuración general (Día 1 cierre) ─────────────────────── */
    .cfg-list {
      display: flex;
      flex-direction: column;
      gap: var(--esp-3);
      margin-top: var(--esp-4);
    }
    .cfg-card {
      background: var(--white);
      border: 1px solid var(--border);
      border-radius: 12px;
      padding: var(--esp-4) var(--esp-4);
      display: flex;
      align-items: center;
      gap: var(--esp-3);
    }
    .cfg-card-info { flex: 1; min-width: 0; }
    .cfg-card-title {
      font-weight: 600;
      color: var(--text-dark);
      font-size: var(--fs-base);
      margin-bottom: var(--esp-1);
    }
    .cfg-card-desc {
      font-size: var(--fs-s);
      color: var(--text-muted);
      line-height: var(--lh-cuerpo);
    }
    .cfg-card-meta {
      font-size: var(--fs-xs);
      color: var(--text-muted);
      margin-top: var(--esp-1);
    }
    .cfg-toggle {
      position: relative;
      width: 46px; height: 26px;
      flex-shrink: 0;
      display: inline-block;
    }
    .cfg-toggle input { opacity: 0; width: 0; height: 0; }
    .cfg-toggle .cfg-slider {
      position: absolute; inset: 0;
      background: #d4c3cd;
      border-radius: 99px;
      cursor: pointer;
      transition: background 0.2s;
    }
    .cfg-toggle .cfg-slider::before {
      content: '';
      position: absolute;
      width: 20px; height: 20px;
      left: 3px; top: 3px;
      background: #fff;
      border-radius: 50%;
      transition: transform 0.2s;
      box-shadow: 0 1px 3px rgba(0,0,0,0.18);
    }
    .cfg-toggle input:checked + .cfg-slider { background: #2d6a2d; }
    .cfg-toggle input:checked + .cfg-slider::before { transform: translateX(20px); }
    .cfg-toggle input:disabled + .cfg-slider { opacity: 0.55; cursor: not-allowed; }

    /* ── Sub-pestañas de Configuración general ─────────────────────────
       Las pestañas grandes de La Libreta, en pequeño y dentro de la hoja:
       misma letra y el mismo lenguaje de color (se reusan los siete colores
       de las hojas, --c-inicio … --c-admin). La elegida se pinta LLENA con
       su color y el texto oscuro encima; las demás quedan con el fondo de
       la hoja y su borde de color, que es lo que las distingue de un botón.

       Los siete colores son los mismos en tema claro y oscuro, así que el
       texto oscuro sobre el color llena contrasta igual en los dos. */
    .cfg-subtabs {
      display: flex;
      flex-wrap: wrap;
      gap: var(--esp-2);
      margin: var(--esp-4) 0 var(--esp-4);
    }
    .cfg-subtab {
      font-family: var(--font-sans);
      font-size: var(--fs-base);
      letter-spacing: var(--track-sutil);
      padding: var(--esp-2) var(--esp-4);
      border-radius: 11px 11px 4px 4px;
      cursor: pointer;
      color: var(--texto);
      background: var(--pagina2);
      border: 1px solid color-mix(in srgb, var(--ct) 65%, transparent);
      border-bottom: 3px solid var(--ct);
      transition: background .2s, color .2s, box-shadow .2s, transform .2s;
    }
    .cfg-subtab:hover {
      transform: translateY(-2px);
      box-shadow: 0 7px 18px -12px var(--ct);
    }
    .cfg-subtab:focus-visible { outline: 2px solid var(--ct); outline-offset: 2px; }
    .cfg-subtab.on {
      background: var(--ct);
      /* Oscuro fijo: encima del color lleno es lo único que se lee igual de
         bien con la luz encendida y apagada. */
      color: #17130a;
      font-weight: bold;
      border-color: var(--ct);
      box-shadow: 0 9px 22px -14px var(--ct);
      transform: none;
    }
    /* El panel que no toca queda fuera de la vista, pero YA CARGADO: sus
       datos se piden igual al entrar a Configuración, como antes. */
    .cfg-pane[hidden] { display: none; }
    @media (max-width: 640px) {
      .cfg-subtab { font-size: var(--fs-m); padding: var(--esp-2) var(--esp-3); }
    }

    /* ── Spinner ───────────────────────────────────────────────────── */
    .spinner {
      width: 24px; height: 24px;
      border: 3px solid var(--border);
      border-top-color: var(--gold);
      border-radius: 50%;
      animation: spin 0.7s linear infinite;
      margin: var(--esp-6) auto;
    }
    @keyframes spin { to { transform: rotate(360deg); } }

    /* Highlight gold text */
    .text-gold   { color: var(--gold-dark); font-weight: 600; }
    .text-green  { color: #2d6a2d; }
    .text-red    { color: #8b2020; }
    .text-muted  { color: var(--text-muted); }
    .fw-serif    { font-family: var(--font-sans); font-size: var(--fs-s); font-weight: 600; color: #3d2535; }

    /* ═══════════════════════════════════════════════════════════════════
       REGISTRO-APP — DARK THEME OVERRIDES
       Aplica la paleta del mockup móvil sobre el chasis heredado.
       Por cascada gana sobre las reglas claritas de arriba.
    ══════════════════════════════════════════════════════════════════════ */

    body {
      font-family: var(--font-sans);
      background: var(--bg-base);
      color: var(--text-primary);
    }

    /* ── Tipografía serif para encabezados ─────────────────────────── */
    h1, h2, h3,
    .section-title,
    .card-title,
    .modal-title,
    .login-brand,
    .login-brand span,
    .login-sub{
      font-family: var(--font-sans);
      font-style: normal;
      color: var(--text-muted);
      font-weight: 500;
      letter-spacing: var(--track-ancho);
    }

    /* ── Scrollbar discreta ────────────────────────────────────────── */
    ::-webkit-scrollbar { width: 6px; height: 6px; }
    ::-webkit-scrollbar-track { background: transparent; }
    ::-webkit-scrollbar-thumb { background: var(--border-soft); border-radius: 99px; }
    ::-webkit-scrollbar-thumb:hover { background: var(--border-strong); }

    /* ── Sidebar ───────────────────────────────────────────────────── */
    .nav-item {
      color: var(--text-secondary);
    }
    .nav-item:hover {
      background: rgba(201,169,97,0.08);
      color: var(--gold);
    }
    .nav-item.active {
      background: rgba(201,169,97,0.12);
      color: var(--gold);
      font-weight: 500;
      border: 1px solid var(--border-gold);
    }
    .nav-item.active .nav-icon,
    .nav-item:hover .nav-icon { color: var(--gold); }
    .nav-icon { color: var(--text-muted); }

    /* ── Cards ─────────────────────────────────────────────────────── */
    .card {
      background: var(--bg-card);
      border: 1px solid var(--border-subtle);
      border-radius: var(--radius-md);
      box-shadow: var(--shadow-card);
    }
    .card-header {
      border-bottom: 1px solid var(--border-subtle);
    }
    .card-title { color: var(--text-primary); }

    /* ── KPI cards ─────────────────────────────────────────────────── */
    .kpi-card {
      background: var(--bg-card);
      border: 1px solid var(--border-subtle);
      border-radius: var(--radius-md);
      box-shadow: var(--shadow-card);
    }
    .kpi-card.gold {
      background: var(--bg-gold-grad);
      border-color: var(--border-gold);
      box-shadow: var(--shadow-gold);
    }
    .kpi-card.gold .kpi-label  { color: var(--text-on-gold); opacity: 0.7; }
    .kpi-card.gold .kpi-value  { color: var(--text-primary); }
    .kpi-card.gold .kpi-icon   { background: rgba(0,0,0,0.25); color: var(--gold-bright); }

    .kpi-card.rose::before,
    .kpi-card.teal::before { background: linear-gradient(90deg, var(--gold-dim), var(--gold)); }

    .kpi-card.rose   .kpi-icon { background: rgba(201,169,97,0.10); color: var(--gold); }
    .kpi-card.teal   .kpi-icon { background: rgba(201,169,97,0.10); color: var(--gold); }

    .kpi-label { color: var(--text-muted); }
    .kpi-value {
      font-family: var(--font-sans);
      font-weight: 700;
      color: var(--text-primary);
    }

    /* ── Section titles + gold line ────────────────────────────────── */
    .section-title { color: var(--text-primary); font-size: var(--fs-xl); }
    .section-sub   { color: var(--text-muted); }
    .gold-line { background: linear-gradient(90deg, var(--gold), transparent); }

    /* ── Tablas ────────────────────────────────────────────────────── */
    thead tr { background: var(--bg-elevated); }
    th {
      color: var(--text-muted);
      border-bottom: 1px solid var(--border-subtle);
    }
    td {
      color: var(--text-secondary);
      border-bottom: 1px solid var(--border-subtle);
    }
    tbody tr:hover { background: rgba(255,255,255,0.02); }

    /* ── Badges ────────────────────────────────────────────────────── */
    .badge-green  { background: var(--positive-bg);     color: var(--positive); }

    /* ── Botones ───────────────────────────────────────────────────── */
    .btn-primary {
      background: var(--bg-gold-grad);
      color: var(--text-on-gold);
      border: 1px solid var(--border-gold);
    }
    .btn-primary:hover {
      background: linear-gradient(135deg, #34270f 0%, #4a3914 100%);
      color: var(--text-primary);
    }
    .btn-gold {
      background: var(--gold);
      color: #1a1408;
      border: 1px solid var(--border-gold);
    }
    .btn-gold:hover {
      background: var(--gold-bright);
      color: #1a1408;
    }
    .btn-outline {
      background: transparent;
      color: var(--text-primary);
      border: 1px solid var(--border-soft);
    }
    .btn-outline:hover {
      background: rgba(255,255,255,0.04);
      border-color: var(--border-strong);
    }
    .btn-danger {
      background: transparent;
      color: var(--negative);
      border: 1px solid color-mix(in srgb, var(--negative) 30%, transparent);
    }
    .btn-danger:hover {
      background: var(--negative-bg);
    }

    /* ── Formularios ───────────────────────────────────────────────── */
    label { color: var(--text-muted); }
    input, select, textarea {
      background: var(--bg-input);
      border: 1px solid var(--border-soft);
      color: var(--text-primary);
      color-scheme: dark;
    }
    input::placeholder, textarea::placeholder { color: var(--text-dim); }
    input:focus, select:focus, textarea:focus {
      /* El anillo de foco lo pone la regla única del final. */
      border-color: var(--gold);
      box-shadow: 0 0 0 3px rgba(201,169,97,0.18);
      background: var(--bg-elevated);
    }
    select option { background: var(--bg-card); color: var(--text-primary); }

    /* ── Alertas ───────────────────────────────────────────────────── */
    .alert-success {
      background: var(--positive-bg);
      border: 1px solid rgba(90,181,116,0.3);
      color: var(--positive);
    }
    .alert-error {
      background: var(--negative-bg);
      border: 1px solid rgba(217,116,116,0.3);
      color: var(--negative);
    }

    /* ── Empty state ───────────────────────────────────────────────── */
    .empty { color: var(--text-muted); }
    .empty-icon { color: var(--gold-dim); opacity: 0.5; }

    /* ── Text helpers ──────────────────────────────────────────────── */
    .text-gold  { color: var(--gold); font-weight: 600; }
    .text-green { color: var(--positive); }
    .text-red   { color: var(--negative); }
    .text-muted { color: var(--text-muted); }
    .fw-serif   { font-family: var(--font-sans); font-weight: 600; color: var(--text-primary); }

    /* ── Modal overlay + modal card ────────────────────────────────── */
    .modal {
      background: var(--bg-card);
      border: 1px solid var(--border-subtle);
      box-shadow: 0 8px 40px rgba(0,0,0,0.5);
    }
    .modal-header, .modal-footer {
      border-color: var(--border-subtle);
    }
    .modal-title { color: var(--text-primary); }
    .modal-close {
      background: var(--bg-elevated);
      color: var(--text-muted);
    }
    .modal-close:hover {
      background: rgba(255,255,255,0.05);
      color: var(--text-primary);
    }

    /* ── Login screen ──────────────────────────────────────────────── */
    #login-screen { background: var(--bg-base); }
    .login-card {
      background: var(--bg-card);
      border: 1px solid var(--border-subtle);
      box-shadow: 0 8px 40px rgba(0,0,0,0.5), var(--shadow-gold);
    }
    .login-brand { color: var(--text-primary); }
    #login-error {
      background: var(--negative-bg);
      border: 1px solid color-mix(in srgb, var(--negative) 30%, transparent);
      color: var(--negative);
    }

    /* ═══════════════════════════════════════════════════════════════════
       MURO DE ACTIVIDAD + PENDIENTES POR AUTORIZACIÓN
    ══════════════════════════════════════════════════════════════════════ */
    /* Si la franja de KPIs no se pobló (kpis-hoy falló o devolvió null),
       colapsarla para que no quede un cuadro vacío "flotando" sobre el
       título "Actividad reciente". */

    #muro-content { display: flex; flex-direction: column; gap: var(--esp-2); }
    .muro-day-sep {
      display: flex;
      align-items: center;
      gap: var(--esp-3);
      color: var(--text-muted);
      font-size: var(--fs-s);
      letter-spacing: var(--track-etiqueta);
      text-transform: uppercase;
      font-weight: 600;
      margin: var(--esp-3) 0 var(--esp-1);
    }
    .muro-day-sep::before,
    .muro-day-sep::after {
      content: '';
      flex: 1;
      height: 1px;
      background: var(--border-subtle);
    }
    /* Lista de Actividad reciente — solo lectura (no clickeable).
       Para abrir el detalle de una transacción, el usuario va a
       Registro de Ingresos / Resumen. */
    .muro-card {
      display: flex;
      align-items: center;
      gap: var(--esp-3);
      padding: var(--esp-3) var(--esp-3);
      background: var(--bg-card);
      border: 1px solid var(--border-subtle);
      border-left: 4px solid var(--text-dim);
      border-radius: var(--radius-sm);
      cursor: default;
    }
    /* Una tarjeta que abre el detalle se comporta como lo que es: se puede
       pulsar, y se nota antes de pulsarla. Las que no tienen transacción
       detrás (un cierre, un aviso) se quedan como estaban. */
    .muro-card.abrible { cursor: pointer; }
    .muro-card.abrible:focus-visible {
      outline: 2px solid var(--gold);
      outline-offset: 2px;
    }
    .muro-card.pendiente {
      border-color: var(--border-gold);
      box-shadow: 0 0 0 1px rgba(201,169,97,0.18);
    }
    .muro-card-icon {
      width: 32px;
      height: 32px;
      border-radius: 8px;
      background: var(--bg-elevated);
      display: flex;
      align-items: center;
      justify-content: center;
      flex-shrink: 0;
      color: var(--text-secondary);
      font-size: var(--fs-base);
    }
    .muro-card-body { flex: 1; min-width: 0; }
    .muro-card-top {
      display: flex;
      align-items: baseline;
      gap: var(--esp-2);
      flex-wrap: wrap;
    }
    .muro-card-titulo {
      color: var(--text-primary);
      font-weight: 600;
      font-size: var(--fs-m);
    }
    .muro-card-actor {
      color: var(--text-muted);
      font-size: var(--fs-s);
    }
    .muro-card-resumen {
      color: var(--text-secondary);
      font-size: var(--fs-s);
      margin-top: var(--esp-1);
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    .muro-card-tiempo {
      color: var(--text-muted);
      font-size: var(--fs-s);
      flex-shrink: 0;
      margin-left: var(--esp-2);
    }
    .muro-card-badge {
      display: inline-block;
      background: rgba(229,160,94,0.18);
      border: 1px solid rgba(229,160,94,0.45);
      color: var(--warning);
      font-size: var(--fs-xs);
      font-weight: 600;
      padding: var(--esp-1) var(--esp-2);
      border-radius: 999px;
      margin-left: var(--esp-1);
    }
    .muro-empty {
      text-align: center;
      padding: var(--esp-8) var(--esp-4);
      color: var(--text-muted);
    }
    .muro-empty-icon {
      font-size: var(--fs-2xl);
      color: var(--gold-dim);
      opacity: 0.5;
      margin-bottom: var(--esp-2);
    }

    /* ── Pendientes por autorización ─────────────────────────────────── */
    .pa-btn {
      padding: var(--esp-1) var(--esp-3);
      border-radius: var(--radius-sm);
      font-weight: 600;
      font-size: var(--fs-s);
      border: 1px solid transparent;
      cursor: pointer;
      transition: all 0.15s;
    }
    .pa-btn-aprobar {
      background: linear-gradient(135deg, #166534 0%, #15803d 100%);
      color: #d1fae5;
      border-color: #14532d;
      box-shadow: 0 4px 14px rgba(22,101,52,0.3);
    }
    .pa-btn-aprobar:hover {
      transform: translateY(-1px);
      box-shadow: 0 6px 18px rgba(22,101,52,0.4);
    }
    .pa-btn-rechazar {
      background: linear-gradient(135deg, #7f1d1d 0%, #991b1b 100%);
      color: #fee2e2;
      border-color: #7f1d1d;
    }
    .pa-btn-rechazar:hover {
      transform: translateY(-1px);
    }

    /* ── Clientes internet (Sprint martes 9 jun) ──────────────────────── */
    .ci-header {
      display: flex;
      justify-content: space-between;
      align-items: flex-start;
      gap: var(--esp-4);
      flex-wrap: wrap;
    }
    .ci-toolbar {
      display: flex;
      justify-content: space-between;
      align-items: center;
      gap: var(--esp-3);
      margin: var(--esp-3) 0;
      flex-wrap: wrap;
    }
    /* El contenedor apila SUS DOS FILAS, una encima de otra. Era
       `display:flex` en fila con wrap: como sus hijos son las dos líneas
       rotuladas, en una pantalla ancha cabían las dos al lado y se veían como
       una sola tira de cinco pestañas — que es justo lo que los rótulos
       vinieron a evitar (Rich, 23-oct). */
    .ci-tabs { display: flex; flex-direction: column; align-items: stretch; gap: var(--esp-1); }
    .ci-tab {
      padding: var(--esp-1) var(--esp-3); border-radius: 999px;
      background: var(--bg-elevated); border: 1px solid var(--border-subtle);
      color: var(--text-secondary); font-size: var(--fs-s); cursor: pointer;
      transition: all .15s;
    }
    .ci-tab:hover { border-color: var(--gold-dim); }
    .ci-tab.active {
      background: var(--bg-card); border-color: var(--gold-bright);
      color: var(--gold-bright);
    }
    .ci-tab-count { margin-left: var(--esp-1); font-size: var(--fs-s); color: var(--text-muted); }
    /* Las pestañas van en dos líneas rotuladas: arriba la cobranza (lo
       nuestro), abajo el servicio que manda WispHub. Sin el rótulo, las
       cinco parecían la misma pregunta y sus números parecían descuadrar. */
    .ci-tabs-fila {
      display: flex; align-items: center; gap: var(--esp-2); flex-wrap: wrap; margin-bottom: var(--esp-1);
    }
    .ci-tabs-rotulo {
      font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: var(--track-etiqueta);
      color: var(--text-muted); min-width: 112px;
    }
    .ci-tabs-rotulo.wh { color: var(--warning); }
    .ci-tabs-nota {
      font-size: var(--fs-s); color: var(--text-muted); line-height: var(--lh-cuerpo); margin: var(--esp-1) 0 var(--esp-1);
    }
    /* El estado que manda WispHub. "Activo" se pinta apagado porque es lo
       normal; lo que no es activo se ve de lejos. */
    .ci-badge-wh {
      display: inline-block; padding: var(--esp-1) var(--esp-2); border-radius: 999px;
      font-size: var(--fs-xs); font-weight: 600; letter-spacing: var(--track-sutil);
      border: 1px solid var(--border-subtle); color: var(--text-muted);
    }
    .ci-badge-wh.ok       { color: var(--text-muted); }
    .ci-badge-wh.malo     { color: var(--warning); border-color: var(--warning); font-weight: 700; }
    .ci-badge-wh.sin-dato { color: var(--text-muted); font-style: italic; }
    #ci-busqueda {
      min-width: 280px; padding: var(--esp-2) var(--esp-3);
      background: var(--bg-input); border: 1px solid var(--border-soft);
      border-radius: 8px; color: var(--text-primary); font-family: inherit;
    }
    .ci-table {
      width: 100%; border-collapse: collapse;
      background: var(--bg-card); border: 1px solid var(--border-subtle);
      border-radius: var(--radius-md); overflow: hidden;
    }
    .ci-table th, .ci-table td {
      padding: var(--esp-2) var(--esp-3); text-align: left;
      border-bottom: 1px solid var(--border-subtle);
      font-size: var(--fs-m); vertical-align: middle;
    }
    .ci-table thead th {
      color: var(--text-muted); font-size: var(--fs-xs);
      text-transform: uppercase; letter-spacing: var(--track-etiqueta);
      background: var(--bg-elevated);
    }
    .ci-table tbody tr { cursor: pointer; transition: background .12s; }
    .ci-table tbody tr:hover { background: var(--bg-elevated); }
    .ci-badge {
      display: inline-block; padding: var(--esp-1) var(--esp-2); border-radius: 999px;
      font-size: var(--fs-xs); font-weight: 600; letter-spacing: var(--track-sutil);
    }
    .ci-badge.activo     { background: rgba(90,181,116,0.15);  color: #5ab574; }
    .ci-badge.moroso     { background: rgba(229,160,94,0.18);  color: var(--warning); }
    .ci-badge.suspendido { background: rgba(150,150,150,0.18); color: var(--text-muted); }
    .ci-badge.cancelado  { background: rgba(215,119,119,0.18); color: #d77; }
    .ci-empty {
      padding: var(--esp-8) var(--esp-4); text-align: center; color: var(--text-muted);
      border: 1px dashed var(--border-subtle); border-radius: var(--radius-md);
    }
    /* El buscador manda: ocupa toda la línea y es lo primero que se ve. */
    .ci-buscador { margin: var(--esp-3) 0 var(--esp-2); }
    .ci-buscador input[type="search"] {
      width: 100%; padding: var(--esp-3) var(--esp-4); font-size: var(--fs-m);
      background: var(--bg-elevated); color: var(--text-primary);
      border: 1px solid var(--border-soft); border-radius: var(--radius-md);
    }
    .ci-buscador input[type="search"]:focus {
      border-color: var(--gold); background: var(--bg-surface);
    }
    /* La pantalla de reposo: lo que se ve al entrar, sin los 234 encima. */
    .ci-reposo { margin-top: var(--esp-1); }
    .ci-reposo-pista {
      padding: var(--esp-5) var(--esp-4); text-align: center; color: var(--text-muted);
      border: 1px dashed var(--border-subtle); border-radius: var(--radius-md);
      line-height: var(--lh-cuerpo);
    }
    .ci-marcados {
      border: 1px solid var(--warning); border-left-width: 4px;
      border-radius: var(--radius-md); background: var(--bg-elevated);
      padding: var(--esp-3) var(--esp-3); margin-bottom: var(--esp-3); line-height: var(--lh-cuerpo);
    }
    .ci-marcados-fila {
      padding: var(--esp-2) 0; border-top: 1px solid var(--border-subtle);
      cursor: pointer; font-size: var(--fs-m);
    }
    .ci-marcados-fila:first-of-type { border-top: none; }
    .ci-marcados-fila:hover { color: var(--gold-bright); }
    .ci-conteo {
      display: flex; justify-content: space-between; align-items: center;
      gap: var(--esp-3); flex-wrap: wrap; margin-top: var(--esp-3);
      color: var(--text-muted); font-size: var(--fs-m);
    }
    /* ── LA CAJA DE LA LISTA ───────────────────────────────────────────
       Alto fijo y recorrido propio: la ruedita mueve los clientes de dentro
       y la página de atrás se queda quieta (`overscroll-behavior: contain`,
       que además impide que al llegar al final el movimiento se contagie a
       la página). Mide lo mismo con 20 clientes que con 236.

       El alto NO es un número elegido a dedo: lo mide ciAjustarCaja() al
       pintar, quedándose con lo que sobra de pantalla desde donde empieza
       la caja. Así la sección abierta cabe en una pantalla en el monitor
       del local, en un portátil y en el teléfono, que es lo que pedía el
       encargo; lo de abajo es sólo el valor de arranque. */
    .ci-caja {
      /* Valor de arranque. El definitivo lo pone ciAjustarCaja() al pintar:
         la caja se queda con lo que sobra de pantalla, para que la sección
         abierta quepa entera sin tener que bajar. */
      max-height: clamp(200px, 46vh, 620px);
      overflow-y: auto; overflow-x: auto;
      overscroll-behavior: contain;
      -webkit-overflow-scrolling: touch;
      border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
      background: var(--bg-card);
    }
    /* `border-collapse: separate` NO es un capricho: con `collapse`, que es
       lo que usa la tabla por defecto, Chrome no respeta el `sticky` de la
       cabecera y los títulos de las columnas se van con el recorrido. */
    .ci-caja .ci-table {
      border: none; border-radius: 0;
      border-collapse: separate; border-spacing: 0;
      /* El `overflow: hidden` que trae .ci-table (para redondearle las
         esquinas) convierte a la tabla en su propio recuadro de recorte, y
         entonces la cabecera pegada se queda pegada A LA TABLA — o sea, se
         va con las filas. Dentro de la caja, la que recorta es la caja. */
      overflow: visible;
    }
    /* La cabecera se queda arriba mientras se recorre: sin esto, a la fila
       veinte ya nadie sabe qué columna está mirando. */
    .ci-caja .ci-table thead th {
      position: sticky; top: 0; z-index: 2;
      background: var(--bg-elevated);
      border-bottom: 1px solid var(--border-subtle);
    }
    /* Una línea por cliente, y apretada: es lo que hace que quepan unos
       veinte a la vez en una caja que todavía cabe en la pantalla. */
    .ci-caja .ci-table td {
      white-space: nowrap; padding: var(--esp-1) var(--esp-3); font-size: var(--fs-s);
      line-height: var(--lh-titulo);
      border-bottom: 1px solid var(--border-subtle);
    }
    .ci-caja .ci-table thead th { padding: var(--esp-1) var(--esp-3); }
    .ci-caja .ci-table td span { font-size: 0.95em; }
    .ci-caja .ci-table .ci-badge { padding: var(--esp-1) var(--esp-2); }
    /* ── EL DESPLEGABLE ────────────────────────────────────────────────
       Cerrado es una línea. Es un botón, pero no parece un botón: parece el
       renglón de una libreta que se abre. */
    .ci-desplegable {
      display: flex; align-items: center; gap: var(--esp-2); width: 100%;
      margin-top: var(--esp-3); padding: var(--esp-2) var(--esp-3); text-align: left;
      background: var(--bg-elevated); color: var(--text-secondary);
      border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
      font-family: inherit; font-size: var(--fs-m); cursor: pointer;
    }
    .ci-desplegable:hover { border-color: var(--gold-dim); color: var(--text-primary); }
    .ci-desplegable.abierta {
      border-color: var(--gold-dim); color: var(--text-primary);
      border-bottom-left-radius: 0; border-bottom-right-radius: 0;
      margin-bottom: 0;
    }
    .ci-desplegable.abierta + .ci-caja {
      border-top-left-radius: 0; border-top-right-radius: 0; border-top: none;
    }
    .ci-desplegable-flecha { color: var(--gold-bright); font-size: var(--fs-s); }
    .ci-desplegable-texto { font-weight: 600; }
    .ci-desplegable-pista {
      margin-left: auto; color: var(--text-muted); font-size: var(--fs-s);
    }
    /* La salida a WispHub cuando el cliente no está en la lista. */
    .ci-wh-zona {
      margin-top: var(--esp-3); padding: var(--esp-3) var(--esp-3); line-height: var(--lh-cuerpo);
      border: 1px solid var(--border-soft); border-radius: var(--radius-md);
      background: var(--bg-elevated);
    }
    .ci-wh-texto {
      font-size: var(--fs-m); color: var(--text-secondary); margin-bottom: var(--esp-2);
    }
    @media (max-width: 640px) {
      .ci-buscador input[type="search"] { font-size: var(--fs-m); padding: var(--esp-3) var(--esp-3); }
      .ci-conteo { justify-content: center; text-align: center; }
      /* En pantalla chica sobran las columnas menos urgentes: el próximo
         corte se deduce del mes pagado, y las dos están en la ficha. */
      .ci-table .ci-col-corte { display: none; }
      .ci-table th, .ci-table td { padding: var(--esp-2) var(--esp-2); font-size: var(--fs-s); }
      .ci-caja .ci-table td { padding: var(--esp-1) var(--esp-2); font-size: var(--fs-s); }
      /* En el teléfono la caja se queda algo más baja: con el teclado
         abierto, media pantalla es todo lo que hay. */
      .ci-caja { max-height: clamp(180px, 42vh, 420px); }
      /* La pista del desplegable se va a su propia línea en vez de apretar
         el conteo contra el borde. */
      .ci-desplegable { flex-wrap: wrap; }
      .ci-desplegable-pista { margin-left: var(--esp-5); }
    }

    /* Ficha de cliente */
    .ci-ficha-header {
      display: flex; justify-content: space-between; align-items: center;
      gap: var(--esp-3); margin-bottom: var(--esp-4); flex-wrap: wrap;
    }
    .ci-ficha-name {
      font-family: var(--font-serif);
      font-size: var(--fs-l); color: var(--gold-bright);
    }
    .ci-ficha-num { color: var(--text-muted); font-size: var(--fs-m); margin-left: var(--esp-2); }
    .ci-ficha-grid {
      display: grid; grid-template-columns: 1fr 1fr; gap: var(--esp-4);
    }
    @media (max-width: 800px) { .ci-ficha-grid { grid-template-columns: 1fr; } }
    .ci-ficha-card {
      background: var(--bg-card); border: 1px solid var(--border-subtle);
      border-radius: var(--radius-md); padding: var(--esp-3) var(--esp-4);
    }
    .ci-ficha-card h4 {
      margin: 0 0 var(--esp-2); font-size: var(--fs-s);
      color: var(--gold-dim); text-transform: uppercase; letter-spacing: var(--track-etiqueta);
    }
    .ci-ficha-row {
      display: flex; justify-content: space-between;
      padding: var(--esp-1) 0; font-size: var(--fs-m);
      border-bottom: 1px solid var(--border-subtle);
    }
    .ci-ficha-row:last-of-type { border-bottom: none; }
    .ci-ficha-row .lbl { color: var(--text-muted); }
    .ci-ficha-row .val { color: var(--text-primary); font-weight: 500; text-align: right; }
    .ci-ficha-actions {
      display: flex; gap: var(--esp-2); margin-top: var(--esp-4); flex-wrap: wrap;
    }

    .pa-empty {
      text-align: center;
      padding: 80px var(--esp-4);
      color: var(--text-muted);
    }
    .pa-empty-icon {
      font-size: 48px;
      color: var(--gold-dim);
      opacity: 0.5;
      margin-bottom: var(--esp-3);
    }
    .pa-counter {
      color: var(--gold);
      font-weight: 600;
      font-size: var(--fs-m);
      margin-left: var(--esp-2);
    }

    /* ── Saldo Pago Listo ───────────────────────────────────────────── */
    .spl-hero {
      display: flex;
      align-items: center;
      gap: var(--esp-4);
      padding: var(--esp-5) var(--esp-5);
      background: linear-gradient(135deg, rgba(201,169,97,0.10), rgba(201,169,97,0.02));
      border: 1px solid var(--border-gold);
      border-left-width: 4px;
      border-radius: var(--radius-md);
      margin-bottom: var(--esp-4);
      flex-wrap: wrap;
    }
    .spl-hero.low {
      background: linear-gradient(135deg, rgba(229,160,94,0.15), rgba(229,160,94,0.03));
      border-color: var(--warning);
      border-left-color: var(--warning);
    }
    .spl-hero.red {
      background: linear-gradient(135deg, rgba(215,119,119,0.18), rgba(215,119,119,0.04));
      border-color: var(--negative);
      border-left-color: var(--negative);
    }
    .spl-hero-info { flex: 1; min-width: 220px; }
    .spl-hero-label {
      color: var(--text-muted);
      font-size: var(--fs-s);
      font-weight: 600;
      text-transform: uppercase;
      letter-spacing: var(--track-ancho);
      margin-bottom: var(--esp-1);
    }
    .spl-hero-value {
      font-family: var(--font-sans);
      font-variant-numeric: tabular-nums;
      color: var(--gold-bright);
      font-weight: 700;
      font-size: var(--fs-2xl);
      letter-spacing: var(--track-sutil);
      line-height: var(--lh-plano);
    }
    .spl-hero.low .spl-hero-value { color: var(--warning); }
    .spl-hero.red .spl-hero-value { color: var(--negative); }
    .spl-hero-meta {
      color: var(--text-muted);
      font-size: var(--fs-s);
      margin-top: var(--esp-1);
    }
    .spl-hero-warning {
      display: inline-block;
      margin-top: var(--esp-2);
      padding: var(--esp-1) var(--esp-2);
      border-radius: 999px;
      font-size: var(--fs-s);
      font-weight: 600;
      background: rgba(229,160,94,0.18);
      color: var(--warning);
    }
    .spl-hero.red .spl-hero-warning {
      background: rgba(215,119,119,0.22);
      color: var(--negative);
    }
    /* Campo de hora de 24 horas (inputHora24): angosto y con cifras de igual
       ancho, para que 07:30 y 17:45 queden alineados en la tabla de horarios. */
    input.hora24 { font-variant-numeric: tabular-nums; letter-spacing: var(--track-sutil); }
    .hor-tabla input.hora24 { width: 76px; text-align: center; padding: var(--esp-1) var(--esp-1); }
    .spl-mov-table {
      width: 100%;
      border-collapse: collapse;
      background: var(--bg-card);
      border: 1px solid var(--border-subtle);
      border-radius: var(--radius-md);
      overflow: hidden;
      font-size: var(--fs-m);
    }
    .spl-mov-table th, .spl-mov-table td {
      padding: var(--esp-2) var(--esp-3);
      text-align: left;
      border-bottom: 1px solid var(--border-subtle);
      vertical-align: middle;
    }
    .spl-mov-table thead th {
      color: var(--text-muted);
      font-size: var(--fs-xs);
      text-transform: uppercase;
      letter-spacing: var(--track-etiqueta);
      background: var(--bg-elevated);
      font-weight: 600;
    }
    .spl-mov-table tbody tr:last-child td { border-bottom: none; }
    .spl-mov-table tbody tr:hover { background: var(--bg-elevated); }
    .spl-badge {
      display: inline-block;
      padding: var(--esp-1) var(--esp-2);
      border-radius: 999px;
      font-size: var(--fs-xs);
      font-weight: 600;
      letter-spacing: var(--track-sutil);
    }
    .spl-badge.abono   { background: rgba(90,181,116,0.15); color: var(--positive); }
    .spl-badge.consumo { background: rgba(215,119,119,0.15); color: var(--negative); }
    .spl-badge.anulado { background: rgba(150,150,150,0.18); color: var(--text-muted); }
    .spl-mov-table tr.spl-row-anulado td { opacity: 0.5; text-decoration: line-through; }
    .spl-mov-table tr.spl-row-anulado td:first-child,
    .spl-mov-table tr.spl-row-anulado td:nth-child(2),
    .spl-mov-table tr.spl-row-anulado td:nth-child(6),
    .spl-mov-table tr.spl-row-anulado td:last-child { text-decoration: none; }
    .spl-row-btn {
      padding: var(--esp-1) var(--esp-2);
      margin-left: var(--esp-1);
      border-radius: 6px;
      font-size: var(--fs-s);
      border: 1px solid var(--border-soft);
      background: var(--bg-elevated);
      color: var(--text-secondary);
      cursor: pointer;
      transition: all 0.12s;
    }
    .spl-row-btn:hover { background: var(--bg-card); border-color: var(--gold-dim); }
    .spl-row-btn.edit:hover { color: var(--gold-bright); border-color: var(--gold); }
    .spl-row-btn.del:hover  { color: var(--negative); border-color: var(--negative); background: rgba(215,119,119,0.06); }
    .spl-monto {
      font-family: var(--font-sans);
      font-variant-numeric: tabular-nums;
      font-weight: 700;
      white-space: nowrap;
    }
    .spl-monto.abono   { color: var(--positive); }
    .spl-monto.consumo { color: var(--negative); }
    .spl-saldo-resultante {
      font-family: var(--font-sans);
      font-variant-numeric: tabular-nums;
      color: var(--gold-bright);
      font-weight: 600;
    }
    .spl-empty {
      text-align: center;
      padding: var(--esp-7) var(--esp-4);
      color: var(--text-muted);
      border: 1px dashed var(--border-subtle);
      border-radius: var(--radius-md);
    }

    /* ── Verificación: lista compacta tipo fila ──────────────────────── */
    .ver-list {
      background: var(--bg-card);
      border: 1px solid var(--border-subtle);
      border-radius: var(--radius-md);
      overflow: hidden;
    }
    .ver-row {
      display: grid;
      grid-template-columns: 28px 92px 1fr auto auto;
      gap: var(--esp-2);
      align-items: center;
      padding: var(--esp-2) var(--esp-3);
      border-bottom: 1px solid var(--border-subtle);
      font-size: var(--fs-m);
      transition: background 0.12s;
    }
    .ver-row:last-of-type { border-bottom: none; }
    .ver-row:hover { background: var(--bg-elevated); }
    .ver-row.observada {
      background: rgba(229,160,94,0.06);
      border-left: 3px solid var(--warning);
      padding-left: var(--esp-2);
    }
    .ver-row.observada:hover { background: rgba(229,160,94,0.10); }

    .ver-row-check {
      width: 16px; height: 16px;
      cursor: pointer;
      accent-color: var(--gold);
    }
    .ver-row-thumb {
      width: 44px; height: 44px;
      border-radius: 6px;
      background: var(--bg-elevated);
      border: 1px solid var(--border-soft);
      overflow: hidden;
      display: flex; align-items: center; justify-content: center;
      cursor: zoom-in;
      flex-shrink: 0;
      position: relative;
    }
    .ver-row-thumb img {
      width: 100%; height: 100%;
      object-fit: cover;
      display: block;
    }
    /* Stack de 2 miniaturas (báscula + comprobante en la misma fila).
       Ancho fijo total = 2 thumbs de 44px + 4px gap = 92px. Coincide con
       la columna 2 del grid de .ver-row. SIN width:100% en los hijos
       (eso, combinado con flex-shrink:0 de .ver-row-thumb, hacía que
       cada thumb intentara medir 92px y los dos se desbordaran encima
       de la columna de datos). */
    .ver-row-thumb-stack {
      display: flex; gap: var(--esp-1);
      width: 92px; height: 44px;
    }
    /* La foto de la báscula en la revisión del cierre: más grande que una
       miniatura de lista, porque aquí es la prueba —se mira antes de tocar
       nada— y con un clic se abre en el visor, con lupa. */
    .cierre-rev-thumb { width: 112px; height: 112px; }
    @media (max-width: 640px) { .cierre-rev-thumb { width: 84px; height: 84px; } }
    .ver-row-thumb-stack .ver-row-thumb {
      width: 44px; height: 44px;
      flex: 0 0 44px;
    }
    .ver-row-thumb.empty {
      color: var(--text-dim);
      font-size: var(--fs-micro);
      cursor: default;
      text-align: center;
    }

    .ver-row-meta {
      display: flex; flex-direction: column;
      min-width: 0;
      line-height: var(--lh-titulo);
    }
    .ver-row-titulo {
      color: var(--text-primary);
      font-weight: 600;
      font-size: var(--fs-m);
      overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    .ver-row-titulo .ver-row-id {
      color: var(--text-muted);
      font-weight: 500;
      font-size: var(--fs-s);
      margin-left: var(--esp-1);
    }
    .ver-row-sub {
      color: var(--text-muted);
      font-size: var(--fs-s);
      overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    .ver-row-obs-marker {
      display: inline-block;
      margin-left: var(--esp-1);
      color: var(--warning);
      font-size: var(--fs-xs);
      font-weight: 600;
      cursor: help;
    }

    /* ── Revisión automática con IA ──────────────────────────────────────
       Tres cosas tienen que distinguirse de un vistazo en esta lista:
       lo que aprobó la máquina, lo que la máquina mandó al admin (y por
       qué), y lo que el admin ya revisó con sus ojos. Si las tres se vieran
       igual, la revisión automática no habría ahorrado nada. */
    .ver-ia-bar {
      display: flex; align-items: center; gap: var(--esp-3); flex-wrap: wrap;
      padding: var(--esp-2) var(--esp-3); margin: 0 0 var(--esp-3) 0;
      border: 1px solid var(--border-soft);
      border-radius: var(--radius);
      background: var(--bg-elevated);
    }
    .ver-ia-bar-texto { font-size: var(--fs-s); color: var(--text-secondary); flex: 1; min-width: 200px; }
    .ver-ia-bar-texto b { color: var(--text-primary); }
    .ver-ia-bar-sub { font-size: var(--fs-xs); color: var(--text-muted); margin-top: var(--esp-1); }

    /* Motivo por el que la IA mandó una transacción al admin. Ocupa su
       propia línea a propósito: es lo que hay que leer antes de mirar la
       foto, no una etiqueta más al lado del monto. */
    .ver-ia-motivo {
      margin-top: var(--esp-1);
      padding: var(--esp-2) var(--esp-2);
      border-radius: 6px;
      font-size: var(--fs-s);
      line-height: var(--lh-cuerpo);
      background: rgba(224, 90, 90, 0.08);
      border-left: 3px solid var(--negative);
      color: var(--text-secondary);
    }
    /* Un comprobante reciclado no es una discrepancia más: es lo más grave
       que esta revisión puede encontrar. */
    .ver-ia-motivo.grave {
      background: rgba(224, 90, 90, 0.16);
      border-left-width: 4px;
      color: var(--text-primary);
      font-weight: 500;
    }
    .ver-ia-motivo.leve {
      background: rgba(229, 160, 94, 0.10);
      border-left-color: var(--warning);
    }
    .ver-row-ia-marker {
      display: inline-block;
      margin-left: var(--esp-1);
      padding: var(--esp-1) var(--esp-2);
      border-radius: 999px;
      font-size: var(--fs-xs);
      font-weight: 700;
      white-space: nowrap;
      cursor: help;
      color: var(--positive);
      background: rgba(90, 180, 120, 0.10);
      border: 1px solid rgba(90, 180, 120, 0.35);
    }
    .ver-row-ia-marker.revision {
      color: var(--negative);
      background: rgba(224, 90, 90, 0.10);
      border-color: rgba(224, 90, 90, 0.35);
    }
    .ver-row-ia-marker.admin {
      color: var(--gold-bright);
      background: rgba(191, 155, 74, 0.10);
      border-color: rgba(191, 155, 74, 0.35);
    }

    /* Marca "Vision no confirmó el comprobante" en la fila de verificación.
       Más fuerte que la nota: fondo tenue + borde para que salte a la vista. */
    .ver-row-vision-marker {
      display: inline-block;
      margin-left: var(--esp-1);
      padding: var(--esp-1) var(--esp-2);
      border-radius: 999px;
      color: var(--negative);
      background: rgba(224, 90, 90, 0.10);
      border: 1px solid rgba(224, 90, 90, 0.35);
      font-size: var(--fs-xs);
      font-weight: 700;
      cursor: help;
      white-space: nowrap;
    }

    /* Regla del sobrepeso sano: el oro pesado debe ser un poco MAYOR que lo
       registrado. Si pesó igual o menos, la fila lo grita en rojo. Si sobró
       muchísimo, es sólo una observación (ámbar): no es una anomalía de
       dinero, pero puede ser algo pesado sin anotar. */
    .ver-row-peso-marker {
      display: inline-block;
      margin-left: var(--esp-1);
      padding: var(--esp-1) var(--esp-2);
      border-radius: 999px;
      color: var(--negative);
      background: rgba(224, 90, 90, 0.10);
      border: 1px solid rgba(224, 90, 90, 0.35);
      font-size: var(--fs-xs);
      font-weight: 700;
      cursor: help;
      white-space: nowrap;
    }
    .ver-row-peso-marker.observacion {
      color: var(--warning);
      background: rgba(229, 160, 94, 0.10);
      border-color: rgba(229, 160, 94, 0.38);
    }

    .ver-row-monto {
      font-family: var(--font-sans);
      font-variant-numeric: tabular-nums;
      color: var(--gold-bright);
      font-weight: 700;
      font-size: var(--fs-m);
      text-align: right;
      line-height: var(--lh-titulo);
      white-space: nowrap;
    }
    .ver-row-monto .ver-row-monto-sub {
      display: block;
      color: var(--text-muted);
      font-weight: 500;
      font-size: var(--fs-xs);
      letter-spacing: 0;
    }

    .ver-row-actions {
      display: flex; gap: var(--esp-1);
      align-items: center;
      flex-wrap: wrap;
      justify-content: flex-end;
    }
    .ver-row-btn {
      padding: var(--esp-1) var(--esp-2);
      border-radius: 6px;
      font-size: var(--fs-s);
      font-weight: 600;
      border: 1px solid transparent;
      cursor: pointer;
      transition: all 0.12s;
      white-space: nowrap;
    }
    .ver-row-btn.ok {
      background: linear-gradient(135deg,#166534 0%,#15803d 100%);
      color: #d1fae5;
      border-color: #14532d;
    }
    .ver-row-btn.ok:hover { transform: translateY(-1px); box-shadow: 0 3px 10px rgba(22,101,52,0.35); }
    .ver-row-btn.obs {
      background: linear-gradient(135deg,#92400e 0%,#b45309 100%);
      color: #fff7ed;
      border-color: #78350f;
    }
    .ver-row-btn.obs:hover { transform: translateY(-1px); }

    /* Panel expandible de observación bajo la fila */
    .ver-row-expand {
      grid-column: 1 / -1;
      display: none;
      padding: var(--esp-2) var(--esp-3) var(--esp-3);
      background: rgba(229,160,94,0.04);
      border-top: 1px dashed rgba(229,160,94,0.4);
      margin: var(--esp-1) calc(-1 * var(--esp-3)) calc(-1 * var(--esp-2));
    }
    .ver-row.expandida .ver-row-expand { display: block; }
    .ver-row-expand-existing {
      font-size: var(--fs-s);
      color: var(--text-secondary);
      background: rgba(229,160,94,0.08);
      border-left: 3px solid var(--warning);
      padding: var(--esp-1) var(--esp-2);
      border-radius: 4px;
      margin-bottom: var(--esp-2);
    }
    .ver-row-expand-existing-label {
      display: block;
      color: var(--warning);
      font-size: var(--fs-xs);
      font-weight: 600;
      text-transform: uppercase;
      letter-spacing: var(--track-etiqueta);
      margin-bottom: var(--esp-1);
    }
    .ver-row-expand textarea {
      width: 100%;
      padding: var(--esp-1) var(--esp-2);
      background: var(--bg-input);
      border: 1px solid var(--border-soft);
      border-radius: 6px;
      color: var(--text-primary);
      font-family: inherit;
      font-size: var(--fs-s);
      resize: vertical;
      min-height: 48px;
    }
    .ver-row-expand-actions {
      display: flex; gap: var(--esp-1);
      justify-content: flex-end;
      margin-top: var(--esp-1);
    }

    /* Responsive: en móvil colapsamos monto y acciones a otra fila */
    @media (max-width: 640px) {
      .ver-row {
        grid-template-columns: 24px 84px 1fr auto;
        gap: var(--esp-2);
      }
      .ver-row-thumb-stack { width: 84px; gap: var(--esp-1); }
      .ver-row-thumb-stack .ver-row-thumb { width: 40px; height: 40px; flex: 0 0 40px; }
      .ver-row-monto { grid-column: 2 / 5; text-align: left; padding-top: var(--esp-1); }
      .ver-row-actions { grid-column: 1 / 5; justify-content: flex-end; }
    }

    /* Botón "🔍 Detalle" de cada fila de verificación. Discreto en gris
       — el énfasis visual queda en los botones de acción (✓ / ⚠). */
    .ver-row-btn.detail {
      background: transparent;
      border-color: var(--border-soft);
      color: var(--text-secondary);
      padding: var(--esp-1) var(--esp-2);
    }
    /* Eliminar: se ve, pero no compite con "verificar". Contorno rojo, sin
       relleno — es la acción que menos veces se usa y la que más cuesta
       deshacer. */
    .ver-row-btn.danger {
      background: transparent;
      border-color: var(--negative);
      color: var(--negative);
      padding: var(--esp-1) var(--esp-2);
    }
    .ver-row-btn.danger:hover {
      background: rgba(215,119,119,0.12);
      transform: translateY(-1px);
    }
    .ver-row-btn.detail:hover {
      border-color: var(--gold);
      color: var(--gold-bright);
      background: rgba(201,169,97,0.06);
    }

    /* Modal "Detalle de transacción" desde Verificación */
    .ver-detail-fotos {
      display: flex;
      gap: var(--esp-3);
      flex-wrap: wrap;
      margin-bottom: var(--esp-4);
    }
    .ver-detail-foto {
      flex: 1 1 220px;
      min-width: 180px;
    }
    .ver-detail-foto-label {
      font-size: var(--fs-xs);
      color: var(--text-muted);
      text-transform: uppercase;
      letter-spacing: var(--track-etiqueta);
      font-weight: 600;
      margin-bottom: var(--esp-1);
    }
    .ver-detail-foto img {
      width: 100%;
      max-height: 260px;
      object-fit: cover;
      border-radius: 8px;
      border: 1px solid var(--border-soft);
      cursor: zoom-in;
      display: block;
      background: var(--bg-elevated);
    }
    .ver-detail-foto .ver-detail-foto-empty {
      width: 100%;
      min-height: 120px;
      border-radius: 8px;
      border: 1px dashed var(--border-soft);
      display: flex;
      align-items: center;
      justify-content: center;
      color: var(--text-dim);
      font-size: var(--fs-s);
    }
    .ver-detail-grid {
      display: grid;
      grid-template-columns: auto 1fr;
      gap: var(--esp-1) var(--esp-3);
      font-size: var(--fs-m);
      align-items: baseline;
    }
    .ver-detail-grid dt {
      color: var(--text-muted);
      font-size: var(--fs-s);
      font-weight: 500;
      white-space: nowrap;
    }
    .ver-detail-grid dd {
      color: var(--text-primary);
      margin: 0;
      word-break: break-word;
    }
    .ver-detail-grid dd.mono { font-variant-numeric: tabular-nums; font-weight: 600; }
    .ver-detail-grid dd.gold { color: var(--gold-bright); font-weight: 700; }
    .ver-detail-section-title {
      font-size: var(--fs-s);
      color: var(--gold-bright);
      text-transform: uppercase;
      letter-spacing: var(--track-etiqueta);
      font-weight: 600;
      margin: var(--esp-3) 0 var(--esp-2);
      padding-bottom: var(--esp-1);
      border-bottom: 1px solid var(--border-subtle);
    }
    .ver-detail-section-title:first-child { margin-top: 0; }
    .ver-detail-obs-box {
      background: rgba(229,160,94,0.08);
      border-left: 3px solid var(--warning);
      padding: var(--esp-2) var(--esp-3);
      border-radius: 6px;
      font-size: var(--fs-m);
      color: var(--text-secondary);
      margin-top: var(--esp-1);
    }

    .ver-detail-vision-aviso {
      background: rgba(224, 90, 90, 0.09);
      border: 1px solid rgba(224, 90, 90, 0.35);
      border-left: 3px solid var(--negative);
      padding: var(--esp-2) var(--esp-3);
      border-radius: 6px;
      font-size: var(--fs-m);
      color: var(--text-secondary);
      line-height: var(--lh-cuerpo);
      margin-bottom: var(--esp-3);
    }

    /* Veredicto de peso en el detalle: rojo si no sobró, ámbar si sobró
       demasiado, verde cuando el peso sobró como debe ser. */
    .ver-detail-peso-aviso {
      background: rgba(224, 90, 90, 0.09);
      border: 1px solid rgba(224, 90, 90, 0.35);
      border-left: 3px solid var(--negative);
      padding: var(--esp-2) var(--esp-3);
      border-radius: 6px;
      font-size: var(--fs-m);
      color: var(--text-secondary);
      line-height: var(--lh-cuerpo);
      margin-bottom: var(--esp-3);
    }
    .ver-detail-peso-aviso.sano {
      background: rgba(74, 168, 108, 0.08);
      border-color: rgba(74, 168, 108, 0.32);
      border-left-color: var(--positive);
    }
    .ver-detail-peso-aviso.observacion {
      background: rgba(229, 160, 94, 0.09);
      border-color: rgba(229, 160, 94, 0.35);
      border-left-color: var(--warning);
    }

    /* Bloque "Peso del día" del reporte de cierre y de la barra de
       verificación. Mismo lenguaje de color que el aviso de arriba. */
    .peso-dia-box {
      border: 1px solid rgba(224, 90, 90, 0.35);
      border-left: 3px solid var(--negative);
      background: rgba(224, 90, 90, 0.08);
      border-radius: 6px;
      padding: var(--esp-2) var(--esp-3);
      font-size: var(--fs-m);
      color: var(--text-secondary);
      line-height: var(--lh-cuerpo);
    }
    .peso-dia-box.sano {
      border-color: rgba(74, 168, 108, 0.30);
      border-left-color: var(--positive);
      background: rgba(74, 168, 108, 0.07);
    }
    .peso-dia-box.observacion {
      border-color: rgba(229, 160, 94, 0.35);
      border-left-color: var(--warning);
      background: rgba(229, 160, 94, 0.08);
    }
    .peso-dia-box.neutro {
      border-color: var(--border-subtle);
      border-left-color: var(--border-soft);
      background: var(--bg-card);
      color: var(--text-muted);
    }
    .peso-dia-box .peso-dia-titulo {
      font-weight: 700;
      color: var(--text-primary);
      margin-bottom: var(--esp-1);
    }
    .peso-dia-box .peso-dia-cifras {
      font-variant-numeric: tabular-nums;
      color: var(--text-muted);
      font-size: var(--fs-s);
      margin-top: var(--esp-1);
    }
    /* ── LO QUE LEYÓ LA IA EN LA FOTO ──────────────────────────────────
       Su propio renglón, con recuadro y en el tamaño del texto normal. Antes
       iba en la misma letra gris y pequeña que el dinero del turno, pegado a
       ese renglón, y se leía como parte de él: el dato que delata una foto
       que dice 0,21 g contra un peso digitado de 1,28 g no puede ir en letra
       de pie de página (Rich, 23-oct). */
    .peso-dia-box .peso-vision {
      margin-top: var(--esp-2); padding: var(--esp-2) var(--esp-3);
      border-left: 4px solid var(--border-soft);
      border-radius: 6px;
      background: rgba(255,255,255,.04);
      font-size: var(--fs-base); line-height: var(--lh-cuerpo);
      font-variant-numeric: tabular-nums;
    }
    .peso-dia-box .peso-vision.ok  { border-left-color: var(--positive) }
    .peso-dia-box .peso-vision.bad { border-left-color: var(--negative) }
    .peso-dia-box .peso-vision-detalle {
      font-size: var(--fs-m); color: var(--text-muted); margin-top: var(--esp-1);
    }
    .peso-dia-box ul {
      margin: var(--esp-2) 0 0 0;
      padding-left: var(--esp-4);
    }
    .peso-dia-box li { margin-bottom: var(--esp-1); }

    /* ═══════════════════════════════════════════════════════════════════
       PANTALLA DE BIENVENIDA — POLVO DORADO (7 s, sólo al iniciar sesión)
       ───────────────────────────────────────────────────────────────────
       El movimiento vive en un <canvas> (ver introPolvo en el JS): miles
       de partículas de polvo de oro con profundidad de campo. Aquí sólo
       está la capa, el texto y su revelado, sincronizados con la misma
       línea de tiempo:

         0   – 2.3 s  una corriente de polvo entra desde la esquina
                      inferior izquierda y fluye en diagonal hacia el centro
         2.3 – 4.6 s  el polvo se arremolina en el centro y de ahí se
                      revela el nombre: cada letra pasa de desenfocada a
                      nítida, escalonada
         4.6 – 5.8 s  el conjunto respira: el polvo flota alrededor del
                      nombre y la línea de luz termina de abrirse
         5.8 – 6.4 s  se apagan el polvo y el texto SOBRE el fondo negro,
                      que sigue opaco: la interfaz no asoma por detrás
         6.4 – 6.8 s  pausa: la pantalla se queda en negro absoluto
         6.8 – 7.3 s  recién ahí funde el negro y aparece el sistema

       El texto se revela con filter: blur → 0 y opacidad. Es lo único que
       no es transform/opacity puro, pero corre sobre 17 letras durante
       poco más de dos segundos y compensa: es justo lo que hace que
       parezca que el polvo forma las letras. ═════════════════════════ */
    /* ══ Bloqueo de pantalla por inactividad ══════════════════════════════
       Va por encima de TODO (intro incluida, 10000): si la pantalla está
       bloqueada, nada de lo que hay detrás se puede tocar ni leer con
       comodidad. El desenfoque no es adorno — evita que quien llegue lea
       datos de clientes sin la contraseña. */
    #lock-overlay {
      position: fixed;
      inset: 0;
      z-index: var(--capa-bloqueo);
      display: none;
      align-items: center;
      justify-content: center;
      padding: var(--esp-4);
      background: rgba(10, 8, 10, 0.92);
      backdrop-filter: blur(10px);
    }
    #lock-overlay.open { display: flex; }
    .lock-card {
      width: 100%;
      max-width: 380px;
      background: var(--bg-elevated);
      border: 1px solid var(--gold);
      border-radius: var(--radius);
      box-shadow: 0 24px 70px rgba(0,0,0,.65);
      padding: var(--esp-5) var(--esp-5) var(--esp-5);
      text-align: center;
    }
    .lock-icon {
      font-size: var(--fs-xl);
      color: var(--gold-bright);
      line-height: var(--lh-plano);
      margin-bottom: var(--esp-2);
    }
    .lock-title {
      font-size: var(--fs-m);
      color: var(--text-primary);
      margin-bottom: var(--esp-1);
      font-weight: 600;
    }
    .lock-sub {
      font-size: var(--fs-m);
      color: var(--text-secondary);
      line-height: var(--lh-cuerpo);
      margin-bottom: var(--esp-4);
    }
    .lock-user {
      display: inline-block;
      font-size: var(--fs-base);
      color: var(--gold-bright);
      font-weight: 700;
      margin-bottom: var(--esp-4);
    }
    /* Discreto: una línea que avisa, no un cartel que grita. No lleva el
       mensaje ni el nombre de nadie. */
    .lock-chat {
      display: flex; align-items: center; gap: var(--esp-2);
      margin: 0 0 var(--esp-4); padding: var(--esp-2) var(--esp-3);
      border: 1px solid rgba(201,169,97,.45); border-radius: 9px;
      background: rgba(201,169,97,.10);
      font-size: var(--fs-m); color: var(--gold-bright);
      text-align: left; line-height: var(--lh-cuerpo);
    }
    .lock-chat[hidden] { display: none }
    #lock-password {
      width: 100%;
      padding: var(--esp-3) var(--esp-3);
      font-size: var(--fs-m);
      text-align: center;
      background: var(--bg-input);
      border: 1px solid var(--border-soft);
      border-radius: 8px;
      color: var(--text-primary);
      font-family: inherit;
    }
    #lock-password:focus { border-color: var(--gold); }
    #lock-error {
      min-height: 18px;
      font-size: var(--fs-s);
      color: var(--negative);
      margin-top: var(--esp-2);
    }
    .lock-btn { width: 100%; margin-top: var(--esp-3); }
    .lock-logout {
      display: inline-block;
      margin-top: var(--esp-4);
      font-size: var(--fs-s);
      color: var(--text-muted);
      text-decoration: underline;
      cursor: pointer;
    }
    .lock-logout:hover { color: var(--text-secondary); }

    #intro-overlay {
      position: fixed;
      inset: 0;
      z-index: var(--capa-intro);           /* sobre el aviso de versión (9999) */
      display: none;            /* mostrarIntro() lo pone en flex */
      align-items: center;
      justify-content: center;
      overflow: hidden;
      background: #000;         /* negro absoluto: el brillo lo pone el polvo */
    }
    /* La CAPA NEGRA no se toca hasta el final: aguanta opaca al 100%
       mientras se apagan el polvo y el texto (5.8–6.4 s) y durante la pausa
       (6.4–6.8 s). Sólo entonces funde. Si esta capa se desvaneciera a la
       vez que el contenido, el sistema asomaría por detrás con las letras a
       medio borrar — que es exactamente el efecto que hay que evitar.
       93.15% de 7.3 s = 6.8 s. */
    #intro-overlay.show { animation: intro-veil 7.3s linear forwards; }
    @keyframes intro-veil {
      0%, 93.15% { opacity: 1; }
      100%       { opacity: 0; }
    }

    /* El contenido (texto + halo) se va antes, y solo. El polvo hace lo
       mismo por su cuenta desde el canvas (ver salida() en el JS). */
    #intro-overlay.show .intro-content {
      animation: intro-content-out 0.6s cubic-bezier(0.4, 0, 0.6, 1) 5.8s forwards;
    }
    @keyframes intro-content-out {
      from { opacity: 1; }
      to   { opacity: 0; }
    }

    #intro-canvas {
      position: absolute;
      inset: 0;
      width: 100%;
      height: 100%;
      display: block;
    }

    /* Halo cálido detrás del nombre: aparece con el texto y respira una
       vez en el tramo final. Va debajo del texto, nunca encima. */
    .intro-content {
      position: relative;
      z-index: 1;
      text-align: center;
      padding: 0 clamp(var(--esp-4), 5vw, var(--esp-7));
      max-width: min(94vw, 1000px);
    }
    .intro-content::before {
      content: '';
      position: absolute;
      left: 50%;
      top: 50%;
      width: min(150%, 900px);
      aspect-ratio: 2 / 1;
      transform: translate(-50%, -50%) scale(0.7);
      background: radial-gradient(ellipse at center, rgba(255, 194, 75, 0.20) 0%,
                  rgba(255, 194, 75, 0.07) 38%, transparent 70%);
      opacity: 0;
      pointer-events: none;
    }
    #intro-overlay.show .intro-content::before {
      animation: intro-halo 4.6s cubic-bezier(0.33, 0.7, 0.3, 1) 2.3s forwards;
    }
    @keyframes intro-halo {
      0%   { opacity: 0;    transform: translate(-50%, -50%) scale(0.7); }
      35%  { opacity: 1;    transform: translate(-50%, -50%) scale(1); }
      70%  { opacity: 0.82; transform: translate(-50%, -50%) scale(1.06); }
      100% { opacity: 0.55; transform: translate(-50%, -50%) scale(1.1); }
    }

    /* ── Textos ──────────────────────────────────────────────────────── */
    .intro-kicker {
      font-family: var(--font-sans);
      font-size: clamp(var(--fs-micro), 2vw, var(--fs-m));
      font-weight: 500;
      letter-spacing: var(--track-ancho);
      text-indent: 0.42em;      /* compensa el espaciado sobrante a la derecha */
      text-transform: uppercase;
      color: #e8d5a8;
      opacity: 0;
    }
    #intro-overlay.show .intro-kicker {
      animation: intro-soft-in 1.5s cubic-bezier(0.22, 0.8, 0.3, 1) 2s forwards;
    }
    @keyframes intro-soft-in {
      from { opacity: 0; filter: blur(8px); transform: translate3d(0, 8px, 0); }
      to   { opacity: 1; filter: blur(0);   transform: translate3d(0, 0, 0); }
    }

    .intro-title {
      /* La serif del logo, en versales y con mucho aire entre letras:
         cartel de película, no encabezado de sección. */
      font-family: var(--font-serif);
      font-style: normal;
      font-weight: 700;
      /* El tope de arriba (73,8px) es una cifra heroica suelta y se queda
         fuera de la escala, como los otros dos héroes del proyecto. */
      font-size: clamp(var(--fs-xl), 6.6vw, 73.8px);
      line-height: var(--lh-titulo);
      letter-spacing: var(--track-ancho);
      text-indent: 0.14em;
      text-transform: uppercase;
      color: #ffe9b0;
      text-shadow: 0 0 26px rgba(255, 194, 75, 0.55), 0 0 70px rgba(255, 165, 0, 0.28);
      margin: clamp(14px, 2.4vw, 22px) 0 0;
    }
    .intro-word { display: inline-block; white-space: nowrap; }
    .intro-letter {
      display: inline-block;
      opacity: 0;
      filter: blur(14px);
      transform: translate3d(0, 14px, 0) scale(1.08);
      will-change: filter, opacity, transform;
    }
    /* El retardo de cada letra lo pone el JS (55 ms de diferencia), de
       modo que el nombre se va condensando de izquierda a derecha. */
    #intro-overlay.show .intro-letter {
      animation: intro-letter-in 1.25s cubic-bezier(0.2, 0.75, 0.25, 1) both;
    }
    @keyframes intro-letter-in {
      to { opacity: 1; filter: blur(0); transform: translate3d(0, 0, 0) scale(1); }
    }

    .intro-user {
      font-family: var(--font-sans);
      font-size: clamp(var(--fs-xs), 2.2vw, var(--fs-m));
      font-weight: 500;
      letter-spacing: var(--track-ancho);
      color: rgba(255, 233, 176, 0.72);
      margin-top: clamp(14px, 2.2vw, 20px);
      opacity: 0;
      min-height: 1.2em;        /* reserva el alto aunque no haya nombre */
    }
    #intro-overlay.show .intro-user {
      animation: intro-soft-in 1.6s cubic-bezier(0.22, 0.8, 0.3, 1) 4s forwards;
    }

    /* Una fina línea de luz bajo el nombre, como un reflejo del polvo. */
    .intro-underline {
      width: min(62%, 460px);
      height: 1px;
      margin: clamp(16px, 2.4vw, 24px) auto 0;
      background: linear-gradient(90deg, transparent 0%, rgba(255, 194, 75, 0.85) 50%, transparent 100%);
      box-shadow: 0 0 14px rgba(255, 194, 75, 0.5);
      opacity: 0;
      transform: scaleX(0.2);
    }
    #intro-overlay.show .intro-underline {
      animation: intro-underline-in 2s cubic-bezier(0.16, 1, 0.3, 1) 3.6s forwards;
    }
    @keyframes intro-underline-in {
      to { opacity: 1; transform: scaleX(1); }
    }

    /* Sin movimiento: la misma portada, quieta, los mismos 7.3 s, y el
       corte al final es seco en vez de fundido. El canvas pinta un solo
       fotograma de polvo (ver introPolvo). */
    @media (prefers-reduced-motion: reduce) {
      #intro-overlay.show,
      #intro-overlay.show .intro-content,
      #intro-overlay.show .intro-content::before,
      #intro-overlay.show .intro-kicker,
      #intro-overlay.show .intro-letter,
      #intro-overlay.show .intro-underline,
      #intro-overlay.show .intro-user {
        animation: none !important;
        opacity: 1;
        filter: none;
        transform: none;
      }
      #intro-overlay.show .intro-content::before {
        opacity: 0.8;
        transform: translate(-50%, -50%) scale(1);
      }
    }

    /* ── "Recordar mi usuario" (pantalla de login) ───────────────────── */
    .login-recordar {
      display: flex;
      align-items: flex-start;
      gap: var(--esp-2);
      margin: var(--esp-3) 0 var(--esp-1);
      cursor: pointer;
      user-select: none;
      font-size: var(--fs-m);
      color: var(--text-secondary);
      letter-spacing: var(--track-sutil);
    }
    .login-recordar input {
      width: 18px;
      height: 18px;
      margin-top: var(--esp-1);
      flex-shrink: 0;
      accent-color: var(--gold);
      cursor: pointer;
    }
    .login-recordar small {
      display: block;
      margin-top: var(--esp-1);
      font-size: var(--fs-s);
      color: var(--text-muted);
      letter-spacing: var(--track-sutil);
    }
    .login-recordar:hover { color: var(--text-primary); }

    /* ═══════════════════════════════════════════════════════════════════
       RECIBO TÉRMICO 80 mm
       ───────────────────────────────────────────────────────────────────
       Se imprime por el navegador, sin drivers ni librerías: el rollo se
       instala como impresora normal de Windows y esto le manda una página
       del ancho exacto del papel.

       La hoja mide 80 mm de ancho y el largo exacto del recibo: se mide
       antes de imprimir y el tamaño lo pone _fijarHojaRecibo (en el JS), así
       el rollo corta donde termina. Márgenes en 0 porque el margen lo pone
       el propio papel; el respiro va en el padding de #recibo-print.

       Lo que manda es la IMPRESORA, no el CSS. Con el driver "Generic /
       Text Only" Chrome no le pasa un dibujo sino sólo el texto, renglón por
       renglón (ver "Maquetado" en el JS): el recibo llega ya armado como
       texto de 48 columnas, centrado con espacios y con separadores de
       guiones. Aquí sólo se muestra en monoespaciada con la geometría de la
       3nStar, para que la vista previa sea el papel:
         · 72 mm de zona escrita / 48 columnas = 1.5 mm por carácter, y
           Courier avanza 0.6 em → letra de 2.5 mm
         · renglón de la impresora: 3.75 mm → interlineado 1.5
         · nada de negritas, tamaños ni bordes: el driver no los manda, y la
           vista previa no puede prometer lo que el papel no trae

       Al imprimir se ocultan TODOS los hermanos del recibo (la app entera,
       modales incluidos) en vez de esconder cosas una por una: si mañana se
       agrega una sección nueva, no se cuela en el papel.

       Negro sobre blanco y sin fondos. ════════════════════════════════════ */
    #recibo-print { display: none; }

    /* El formato del papel vive FUERA de @media print a propósito: así vale
       también en pantalla —donde el recibo sigue oculto— y se puede MEDIR
       antes de imprimir para pedir una hoja del largo justo. Todas las
       reglas cuelgan de #recibo-print: no alcanzan nada fuera del recibo. */
    #recibo-print {
      width: 80mm;
      margin: 0;
      padding: 3mm 4mm 0;     /* el espacio para cortar va en el propio texto */
      background: #fff;
      color: #000;
      -webkit-print-color-adjust: exact;
      print-color-adjust: exact;
    }
    /* Mientras se mide: maquetado, pero fuera de la vista y sin pintarse. */
    #recibo-print.rc-midiendo {
      display: block;
      position: absolute;
      left: -10000px;
      top: 0;
      visibility: hidden;
    }

    #recibo-print .rc-papel {
      margin: 0;
      font-family: 'Courier New', Courier, monospace;
      font-size: 2.5mm;
      font-weight: 400;
      line-height: var(--lh-cuerpo);
      letter-spacing: 0;
      white-space: pre;       /* los espacios del centrado son parte del texto */
    }

    @media print {
      /* Sin márgenes para todo lo que se imprime, como ya estaba. El TAMAÑO
         de la hoja no va aquí: lo pone _fijarHojaRecibo sólo mientras se
         imprime un recibo (si fuera fijo, los informes saldrían a 80 mm). */
      @page { margin: 0; }

      html, body {
        height: auto !important;
        overflow: visible !important;
        background: #fff !important;
      }
      body.imprimiendo-recibo > *:not(#recibo-print) { display: none !important; }

      /* El resplandor dorado del fondo es un ::before de body, fijo y a
         pantalla completa. Un pseudo-elemento no es un hijo, así que la
         regla de arriba no lo alcanza: en papel quedaba como una capa
         encima del recibo y la hoja salía EN BLANCO. */
      body.imprimiendo-recibo::before,
      body.imprimiendo-recibo::after { display: none !important; }

      body.imprimiendo-recibo #recibo-print { display: block !important; }

      #recibo-print * { color: #000 !important; background: transparent !important; }
    }

    /* Nada de esto es información: si el equipo o el sistema pide menos
       movimiento, se apaga todo el movimiento y queda el color. */
    @media (prefers-reduced-motion: reduce) {
      *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
      }
    }

    /* ═══════════════════════════════════════════════════════════════════
       LA LIBRETA — capa visual (fiel al diseño aprobado)
       ═══════════════════════════════════════════════════════════════════

       Copia del diseño de referencia aprobado: escritorio negro con malla
       dorada, barra superior con la marca, siete pestañas repartidas a lo
       ancho colgando de la libreta, y la hoja abierta con su
       pila de hojas detrás y los cantos de colores a la derecha.

       Esta capa va al final del <style> para ganar por cascada sobre el
       chasis heredado. Lo que hace, además de vestir el chasis nuevo, es
       re-declarar los tokens que ya usaban las ~4.000 líneas de estilo de
       arriba (--bg-card, --text-primary, --border-subtle…): así las
       pantallas que todavía no se han migrado una por una ya heredan la
       paleta de la referencia en vez de quedar con el traje viejo.

       El tema claro sólo vuelve a declarar variables. Ninguna regla de
       maqueta se escribe dos veces.
    ══════════════════════════════════════════════════════════════════════ */

    :root{
      /* Paleta de la referencia, tal cual */
      --n0:#020204; --n1:#0b0a06; --n2:#100e09; --n3:#181408; --pagina:#0e0c07; --pagina2:#13110a;
      --oro:#f6c945; --oro-claro:#ffe291; --oro-med:#c9a227; --oro-dim:#41340f; --linea:#241d09;
      --texto:#efe9d8; --gris:#918e84; --gris-2:#615e56;
      --verde:#3ddc84; --rojo:#ef5350; --cian:#38dde4;
      --glow:rgba(246,201,69,.5); --glow-suave:rgba(246,201,69,.12);
      --sombra:0 30px 70px -30px rgba(0,0,0,.95);

      /* Colores de cada pestaña (los de la referencia) */
      --c-inicio:#f6c945; --c-registrar:#3ddc84; --c-historial:#38dde4;
      --c-internet:#f472b6; --c-cuentas:#a78bfa; --c-cierre:#fb923c; --c-admin:#f87171;

      /* Una sola letra para todo: Times New Roman, ya instalada. */
      --font-serif:'Times New Roman', Times, 'Liberation Serif', serif;
      --font-sans: 'Times New Roman', Times, 'Liberation Serif', serif;

      /* ── Puente con el chasis heredado ─────────────────────────────
         Los mismos nombres de siempre, apuntando a la paleta nueva. */
      --bg-base:        var(--pagina);
      --bg-elevated:    var(--n3);
      --bg-card:        var(--n2);
      --bg-input:       var(--n1);
      --bg-overlay:     rgba(2,2,4,.86);
      --bg-gold-start:  #2c220e;
      --bg-gold-end:    #40310f;
      --bg-gold-grad:   linear-gradient(135deg, #2c220e 0%, #40310f 100%);
      --oro-macizo:     linear-gradient(170deg, var(--oro-claro), var(--oro) 60%);

      --border-subtle:  var(--linea);
      --border-soft:    var(--oro-dim);
      --border-strong:  var(--oro-med);
      --border-accent:  var(--oro-med);
      --border-gold:    var(--oro-med);

      --gold:           var(--oro);
      --gold-bright:    var(--oro-claro);
      --gold-dim:       var(--oro-med);
      --gold-light:     var(--oro-claro);
      --gold-dark:      var(--oro-med);
      --text-on-gold:   #141001;

      --text-primary:   var(--texto);
      --text-secondary: var(--texto);
      --text-muted:     var(--gris);
      --text-dim:       var(--gris-2);
      --text-dark:      var(--texto);
      --text-med:       var(--gris);
      --white:          var(--n2);
      --border:         var(--linea);

      --positive:       var(--verde);
      --positive-bg:    rgba(61,220,132,.12);
      --negative:       var(--rojo);
      --negative-bg:    rgba(239,83,80,.12);
      --warning:        var(--oro);
      --cyan-accent:    var(--cian);
      --rose:           var(--oro-med);
      --rose-light:     var(--n1);
      --rose-mid:       var(--n3);
      --rose-deep:      var(--oro);
      --rose-med:       var(--oro);

      --radius-sm:9px; --radius-md:14px; --radius-lg:16px; --radius:14px;
      --shadow-card:0 20px 50px -26px rgba(0,0,0,.85), inset 0 1px 0 var(--glow-suave);
      --shadow:var(--shadow-card); --shadow-lg:var(--shadow-card);
      --shadow-gold:0 0 36px -14px var(--glow);
    }

    /* El tema claro es de papel, no de pantalla: el escritorio es una mesa de
       madera clara (--n0, beige) y encima van las hojas, en gris claro apenas
       teñido. Antes las hojas eran casi blanco puro y golpeaban la vista a las
       horas de mostrador.

       Las medidas:
         · las hojas y pestañas quedan con un 2% de saturación —gris de
           verdad, no crema— y el escritorio es 23% más oscuro que ellas, así
           que papel y mesa se distinguen sin esfuerzo;
         · texto, grises, verde, rojo y cian pasan de 4,5:1 sobre las tres
           superficies donde pueden caer: hoja, pestaña y escritorio.

       El DORADO es la excepción, y es a propósito: es el color de los botones
       de siempre (#7a5a00 → #9c7500) y se devolvió tal cual se pidió. Como
       esas dos variables también se usan para texto suelto (la marca, el
       botón de tema), ese texto dorado queda en 3,6:1 sobre la hoja y 2,8:1
       sobre el escritorio. Queda escrito acá para que nadie lo "arregle" sin
       saber que cambiaría el color de los botones.

       Los dos grises son dos escalones distintos (7,4:1 y 6,1:1 sobre la
       hoja): si se igualaran, se perdería la diferencia entre "apagado" y
       "tenue", que es información. */
    [data-tema="claro"]{
      --n0:#ddd3b9; --n1:#e9e8e4; --n2:#eeedea; --n3:#e4e3df; --pagina:#f0efec; --pagina2:#eae9e6;
      --oro:#9c7500; --oro-claro:#7a5a00; --oro-med:#634908; --oro-dim:#c4b184; --linea:#ded3b6;
      --texto:#262214; --gris:#4f4c3e; --gris-2:#5c5848;
      --verde:#145c33; --rojo:#a8221b; --cian:#00656e;
      --glow:rgba(110,80,0,.28); --glow-suave:rgba(110,80,0,.07);
      --sombra:0 24px 55px -28px rgba(80,62,18,.42);

      --text-on-gold:#fff;
      /* Con la luz encendida el tinte es crema; encima, texto oscuro. */
      --bg-gold-start:#f0e6cd;
      --bg-gold-end:#e2d5b2;
      --bg-gold-grad:linear-gradient(135deg, #f0e6cd 0%, #e2d5b2 100%);
      --positive-bg:rgba(20,92,51,.12);
      --negative-bg:rgba(168,34,27,.10);
      --shadow-card:0 14px 34px -22px rgba(80,62,18,.5), inset 0 1px 0 rgba(255,252,240,.55);
    }

    /* ═══════════════════════════════════════════════════════════════════
       EL ESCRITORIO
    ══════════════════════════════════════════════════════════════════════ */
    *, *::before, *::after{ box-sizing:border-box }
    /* La raíz manda sobre todo lo escrito en rem, que en este archivo es
       exactamente el texto de cuerpo: descripciones, notas y subtítulos.
       Subirla de 16 a 18 px es el único sitio donde se toca su tamaño. */
    html{ height:auto; overflow:visible; font-size:var(--fs-base)}
    body{
      display:block; min-height:100vh; height:auto;
      overflow-x:hidden; overflow-y:auto;
      background:var(--n0); color:var(--texto);
      font-family:var(--font-sans); font-size:var(--fs-base);
      transition:background .4s;
    }
    body::before{
      content:''; position:fixed; inset:0; z-index:0; pointer-events:none;
      background:radial-gradient(1100px 520px at 50% -12%, var(--glow-suave), transparent 60%);
    }
    .malla{
      position:fixed; inset:0; z-index:0; pointer-events:none; opacity:.05;
      background-image:linear-gradient(var(--oro) 1px, transparent 1px), linear-gradient(90deg, var(--oro) 1px, transparent 1px);
      background-size:72px 72px;
      mask-image:radial-gradient(ellipse 80% 45% at 50% 0%, #000, transparent 90%);
      -webkit-mask-image:radial-gradient(ellipse 80% 45% at 50% 0%, #000, transparent 90%);
    }
    body.unauthed .malla{ display:none }

    /* #app es el "wrap" de la referencia: la app entera centrada. */
    #app{
      position:relative; z-index:1;
      max-width:1220px; margin:0 auto; padding:0 var(--esp-5) var(--esp-8);
      display:block; height:auto; overflow:visible;
    }
    ::-webkit-scrollbar{ width:9px; height:9px }
    ::-webkit-scrollbar-track{ background:transparent }
    ::-webkit-scrollbar-thumb{ background:var(--oro-dim); border-radius:99px }
    ::-webkit-scrollbar-thumb:hover{ background:var(--oro-med) }

    /* ═══════════════════════════════════════════════════════════════════
       BARRA SUPERIOR DEL ESCRITORIO (fuera de la hoja)
    ══════════════════════════════════════════════════════════════════════ */
    .top{ display:flex; align-items:center; gap:var(--esp-4); padding:var(--esp-4) var(--esp-1) 0}
    .marca{ font-style:italic; font-size:var(--fs-xl); line-height:var(--lh-plano); white-space:nowrap; font-weight:bold; color:var(--texto) }
    .marca em{ color:var(--oro); font-style:italic }
    .marca small{ font-style:normal; font-weight:normal; font-size:var(--fs-micro); letter-spacing:5px; color:var(--gris-2); display:block; margin-top:var(--esp-1)}

    /* El buscador global de siempre, con el traje de la referencia. */
    #bg-wrap{ position:relative; flex:1; max-width:330px; min-width:150px; margin:0 }
    #bg-input{
      width:100%; border:1px solid var(--oro-dim); border-radius:10px;
      padding:var(--esp-2) var(--esp-6) var(--esp-2) var(--esp-6); background:var(--glow-suave); color:var(--texto);
      font-family:var(--font-sans); font-size:var(--fs-s); font-style:italic;
    }
    #bg-input::placeholder{ color:var(--gris-2); font-style:italic }
    #bg-input:focus{ border-color:var(--oro-med); box-shadow:0 0 18px -8px var(--glow) }
    #bg-icon{ position:absolute; left:11px; top:50%; transform:translateY(-50%); width:15px; height:15px; color:var(--oro-med); pointer-events:none }
    #bg-clear{ position:absolute; right:8px; top:50%; transform:translateY(-50%); background:none; border:none; color:var(--gris); cursor:pointer; font-size:var(--fs-xs)}
    /* El panel de resultados conserva su tamaño de siempre (cabe una
       transacción entera por línea); aquí sólo cambia de traje. */
    #bg-panel{ background:var(--n2); border:1px solid var(--oro-dim); border-radius:12px; box-shadow:var(--sombra) }

    .util{ margin-left:auto; display:flex; gap:var(--esp-4); align-items:center; font-size:var(--fs-micro); letter-spacing: var(--track-ancho); color:var(--gris); white-space:nowrap }
    .dot{ width:5px; height:5px; border-radius:50%; background:var(--verde); box-shadow:0 0 8px var(--verde); display:inline-block; margin-right:var(--esp-1); animation:pulso 2.6s infinite }
    @keyframes pulso{ 0%,100%{opacity:1} 50%{opacity:.3} }

    .tema-btn{
      cursor:pointer; border:1px solid var(--oro-dim); background:var(--glow-suave); color:var(--oro);
      border-radius:20px; padding:var(--esp-2) var(--esp-4); font-family:inherit; font-size:var(--fs-micro); letter-spacing: var(--track-ancho);
      white-space:nowrap; transition:box-shadow .25s, color .25s;
    }
    .tema-btn:hover{ box-shadow:0 0 16px -6px var(--glow); color:var(--oro-claro) }
    [data-tema="claro"] .tema-btn:hover{ color:var(--oro-med) }

    /* Sesión: avatar + nombre + rol, con su menú. */
    .sesion{ position:relative; cursor:pointer }
    .sesion .pill{
      display:flex; align-items:center; gap:var(--esp-2); border:1px solid var(--oro-med); border-radius:24px;
      padding:var(--esp-1) var(--esp-4) var(--esp-1) var(--esp-2); background:var(--glow-suave); transition:all .25s;
    }
    .sesion:hover .pill{ box-shadow:0 0 18px -6px var(--glow) }
    .sesion .ava{
      width:30px; height:30px; border-radius:50%; border:1px solid var(--oro-med);
      display:flex; align-items:center; justify-content:center;
      font-size:var(--fs-s); font-style:italic; color:var(--oro); background:var(--n2); text-transform:uppercase;
    }
    .sesion .qn{ font-size:var(--fs-xs); line-height:var(--lh-titulo); text-align:left }
    .sesion .qn b{ display:block; color:var(--texto) }
    .sesion .qn small{ font-size:var(--fs-micro); color:var(--oro-med); letter-spacing: var(--track-ancho); text-transform:uppercase }
    .sesion .menu-s{
      position:absolute; right:0; top:calc(100% + 8px); min-width:210px;
      border:1px solid var(--oro-dim); border-radius:12px; background:var(--n2);
      box-shadow:var(--sombra); padding:var(--esp-2);
      opacity:0; pointer-events:none; transform:translateY(-6px); transition:all .3s; z-index:99;
    }
    /* El menú se abre con un CLIC y se queda abierto: lo cierran un clic
       fuera, Escape, o elegir una opción. Antes lo abría :hover, y como la
       clase '.abierto' la pone el JS en el MENÚ (no en '.sesion', que era lo
       que este selector miraba), el hover era lo ÚNICO que lo mostraba: el
       menú vivía mientras el puntero o el dedo estaban encima y se cerraba
       al soltar, sin darle tiempo a nadie de llegar a "Cerrar sesión". */
    .sesion .menu-s.abierto{ opacity:1; pointer-events:auto; transform:none }
    .menu-s button{
      display:block; width:100%; text-align:left; background:none; border:none; cursor:pointer;
      padding:var(--esp-2) var(--esp-3); border-radius:8px; font-family:var(--font-sans); font-size:var(--fs-s);
      color:var(--gris); transition:all .2s;
    }
    .menu-s button:hover{ color:var(--oro); background:var(--glow-suave) }
    .menu-s button.rojo:hover{ color:var(--rojo) }

    /* ── LA CAMPANITA ─────────────────────────────────────────────────
       Vive entre los botones de la barra y la sesión, y se viste como
       ellos: círculo con filo dorado sobre el mismo fondo tenue. Redonda y
       sólo con el símbolo, porque al lado del pill de la sesión dos cosas
       con texto se pelean — y porque el globito tiene que ser lo único
       que llame la atención.

       Es el HISTORIAL. El muro de Inicio sigue donde estaba, grande y en
       medio, y el cartel rojo del mostrador no se toca: esto es para
       después, no en lugar de ellos. */
    .sr-only{
      position:absolute; width:1px; height:1px; padding:0; margin:calc(-1 * var(--esp-1));
      overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
    }
    .campana{ position:relative }
    .campana-btn{
      position:relative; cursor:pointer; display:flex; align-items:center; justify-content:center;
      width:40px; height:40px; border-radius:50%;
      border:1px solid var(--oro-dim); background:var(--glow-suave); color:var(--oro);
      transition:box-shadow .25s, color .25s;
    }
    .campana-btn svg{ width:19px; height:19px }
    .campana-btn:hover{ box-shadow:0 0 16px -6px var(--glow); color:var(--oro-claro) }
    [data-tema="claro"] .campana-btn:hover{ color:var(--oro-med) }
    .campana-btn.tiene-avisos{ border-color:var(--oro-med) }
    /* El globito: rojo, y sólo cuando hay algo sin ver. Un número que está
       siempre puesto se deja de mirar. */
    .campana-globo{
      position:absolute; top:-3px; right:-3px; min-width:18px; height:18px; padding:0 var(--esp-1);
      border-radius:9px; background:var(--negative); color:#fff;
      font-family:var(--font-sans); font-size:var(--fs-micro); font-weight:700; line-height:18px;
      text-align:center; letter-spacing:0; box-shadow:0 0 0 2px var(--n1);
    }
    .campana-panel{
      position:absolute; right:0; top:calc(100% + 8px); width:400px; max-width:92vw;
      border:1px solid var(--oro-dim); border-radius:12px; background:var(--n2);
      box-shadow:var(--sombra); z-index:120;
      opacity:0; pointer-events:none; transform:translateY(-6px); transition:all .3s;
      display:flex; flex-direction:column; max-height:min(70vh, 560px);
    }
    .campana-panel.abierto{ opacity:1; pointer-events:auto; transform:none }
    .campana-cab{
      display:flex; align-items:baseline; gap:var(--esp-2); flex-wrap:wrap;
      padding:var(--esp-3) var(--esp-3) var(--esp-2); border-bottom:1px solid var(--oro-dim);
    }
    .campana-cab b{ font-family:var(--font-serif); font-style:italic; font-size:var(--fs-m); color:var(--texto); letter-spacing:0 }
    .campana-cab #campana-resumen{ font-size:var(--fs-xs); color:var(--gris); letter-spacing: var(--track-etiqueta) }
    /* Las dos acciones de la cabecera, juntas y a la derecha: marcar todo
       como visto y la papelera. Van en un grupo para que al esconderse una
       la otra no se mueva de sitio. */
    .campana-cab-acc{
      margin-left:auto; display:flex; align-items:center; gap:var(--esp-3); flex-shrink:0;
    }
    .campana-todos{
      cursor:pointer; background:none; border:none; padding:0;
      font-family:var(--font-sans); font-size:var(--fs-xs); color:var(--oro-med); letter-spacing:0;
      text-decoration:underline; text-underline-offset:3px;
    }
    .campana-todos:hover{ color:var(--oro-claro) }
    .campana-todos[hidden]{ display:none }
    /* La papelera. Gris mientras no la toquen: borrar no es la acción
       principal de este panel y no tiene por qué llamar la atención. Se
       pone roja al pasar por encima, que es lo que va a hacer. */
    .campana-borrar{
      cursor:pointer; background:none; border:none; padding:var(--esp-1);
      display:flex; align-items:center; color:var(--gris);
      transition:color .2s; border-radius:5px;
    }
    .campana-borrar svg{ width:15px; height:15px }
    .campana-borrar:hover{ color:var(--negative) }
    .campana-borrar[hidden]{ display:none }
    /* La pregunta. Dentro del panel, no un cartel del navegador: así se lee
       al lado de la lista que se va a borrar, dice cuántos son y recuerda
       que los sin ver se quedan. */
    .campana-confirma{
      padding:var(--esp-3) var(--esp-3); border-bottom:1px solid var(--oro-dim);
      background:var(--glow-suave); font-size:var(--fs-xs); color:var(--texto);
      letter-spacing:0; line-height:var(--lh-cuerpo);
    }
    .campana-confirma[hidden]{ display:none }
    .campana-confirma .cc-aviso{ color:var(--gris); display:block; margin-top:var(--esp-1)}
    .campana-confirma .cc-btns{ display:flex; gap:var(--esp-2); margin-top:var(--esp-2); flex-wrap:wrap }
    /* El recorrido va DENTRO del panel: la página de detrás no se mueve. */
    .campana-lista{ overflow-y:auto; padding:var(--esp-2); flex:1; min-height:60px }
    .campana-pie{
      padding:var(--esp-2) var(--esp-3); border-top:1px solid var(--oro-dim);
      font-size:var(--fs-xs); color:var(--gris-2); letter-spacing:0; line-height:var(--lh-cuerpo);
    }
    /* Cada aviso. El filo de color lo pone el JS según el tipo, igual que en
       el muro, para que un aviso sea del mismo color en los dos sitios. */
    .campana-item{
      border:1px solid var(--oro-dim); border-left-width:3px; border-radius:10px;
      padding:var(--esp-2) var(--esp-3); margin-bottom:var(--esp-2); background:transparent;
    }
    .campana-item:last-child{ margin-bottom:0 }
    .campana-item.sin-ver{ background:var(--glow-suave) }
    .campana-item .ci-tit{
      font-family:var(--font-sans); font-size:var(--fs-s); font-weight:700; color:var(--texto);
      line-height:var(--lh-titulo); letter-spacing:0;
    }
    .campana-item .ci-cuando{
      font-size:var(--fs-xs); color:var(--gris-2); letter-spacing:0; margin-top:var(--esp-1);
    }
    .campana-item .ci-punto{
      display:inline-block; width:7px; height:7px; border-radius:50%;
      background:var(--negative); margin-right:var(--esp-1); vertical-align:middle;
    }
    .campana-item .ci-det{
      font-size:var(--fs-xs); color:var(--text-secondary); line-height:var(--lh-cuerpo);
      margin-top:var(--esp-1); letter-spacing:0;
    }
    .campana-item .ci-det div{ margin-bottom:var(--esp-1)}
    /* Cerrado: sólo el titular y la fecha. Se abre al pulsarlo. */
    .campana-item .ci-det[hidden]{ display:none }
    .campana-item .ci-cab{ cursor:pointer }
    .campana-item .ci-acciones{ margin-top:var(--esp-2); display:flex; gap:var(--esp-2); flex-wrap:wrap }
    /* El `display:flex` de arriba le gana al display:none que el navegador
       le da a [hidden], así que hay que decirlo a mano: sin esto, un aviso
       ya visto enseñaba sus botones aunque estuviera plegado. */
    .campana-item .ci-acciones[hidden]{ display:none }
    .campana-vacio{ padding:var(--esp-5) var(--esp-3); text-align:center; color:var(--gris-2); font-size:var(--fs-s); letter-spacing:0 }

    /* ═══════════════════════════════════════════════════════════════════
       LA LIBRETA
    ══════════════════════════════════════════════════════════════════════ */
    .libreta{ position:relative; margin-top:84px; min-height:640px }

    .hoja{ --c:var(--oro); position:absolute; inset:0; z-index:1 }
    .hoja.on{ position:relative; z-index:6; animation:traer .6s cubic-bezier(.2,.85,.25,1) }
    /* la hoja ENTERA (con su pestaña pegada) viene del fondo hacia adelante */
    @keyframes traer{
      from{ opacity:0; transform:scale(.93) translateY(18px); filter:blur(3px) }
      60%{ filter:blur(0) }
      to{ opacity:1; transform:none }
    }
    .hoja:not(.on) .cuerpo{ opacity:0; pointer-events:none }
    .hoja:not(.on){ pointer-events:none }
    .hoja:not(.on) .pestana{ pointer-events:auto }

    /* Pestañas: a lo ancho de toda la página, colgando de la hoja. */
    .pestana{
      position:absolute; top:-56px; height:57px; width:12.5%; min-width:0; padding:var(--esp-2) var(--esp-2) 0;
      border:1px solid var(--linea); border-bottom:none; border-radius:13px 13px 0 0;
      background:linear-gradient(180deg, var(--n3), var(--pagina2));
      box-shadow:inset 0 3px 0 color-mix(in srgb, var(--c) 55%, transparent), 0 -8px 22px -12px rgba(0,0,0,.7);
      font-size:clamp(var(--fs-micro), 1.05vw, var(--fs-xs)); letter-spacing:clamp(0px, .16vw, 2px); color:var(--gris);
      text-align:center; cursor:pointer; transition:color .3s, background .3s, box-shadow .3s, top .3s, height .3s; z-index:2;
      font-family:var(--font-sans);
      white-space:nowrap; overflow:hidden;
    }
    .pestana .mini{ display:block; font-size:clamp(var(--fs-s), 1.6vw, var(--fs-base)); font-style:italic; color:var(--c); opacity:.85; margin-bottom:var(--esp-1)}
    .pestana:hover{ transform:translateY(-5px); color:var(--texto) }
    .pestana .badge{
      position:absolute; top:6px; right:7px; font-size:var(--fs-micro); font-weight:bold; color:#141001;
      background:var(--c); border-radius:20px; padding:var(--esp-1) var(--esp-2); min-width:20px;
      box-shadow:0 0 10px color-mix(in srgb, var(--c) 60%, transparent); display:none;
    }
    .pestana.tiene-aviso .badge{ display:block }
    [data-tema="claro"] .pestana .badge{ color:var(--text-on-gold) }

    /* Pestaña de la hoja abierta: sube con su hoja, fundidas en una pieza. */
    .hoja.on .pestana{
      top:-70px; height:71px; padding-top:var(--esp-4);
      color:var(--c);
      border-color:color-mix(in srgb, var(--c) 60%, transparent); border-bottom:none;
      background:linear-gradient(180deg, var(--n3) 0%, var(--pagina) 100%);
      /* La tercera sombra es el resplandor de la raya de abajo. Va en la
         pestaña y no en el pseudo-elemento porque `overflow:hidden`
         recorta lo que pinta un hijo, pero NO la sombra de la propia
         caja: desde aquí sí puede salirse hacia la hoja. */
      box-shadow:inset 0 3px 0 var(--c),
                 0 -12px 32px -12px color-mix(in srgb, var(--c) 45%, transparent),
                 0 7px 22px -4px color-mix(in srgb, var(--c) 55%, transparent);
    }
    .hoja.on .pestana .mini{ opacity:1; text-shadow:0 0 16px color-mix(in srgb, var(--c) 60%, transparent) }
    /* ── LA LÍNEA DE LA PESTAÑA ABIERTA (Rich, 9-nov-2026) ────────────
       Esta franja ya existía: 2px pegados al borde de abajo de la
       pestaña abierta, pintados del color de la HOJA para fundir las
       dos piezas. Ahora va del color de la SECCIÓN —oro en Inicio,
       verde en Registrar, azul en Historial, rosa en Internet, morado
       en Cuentas, naranja en Cierre, rojo en Admin— y se convierte en
       la raya que dice dónde estás.

       No se cambia la geometría: la franja mide 4px y arranca 2px por
       debajo, así que `overflow:hidden` recorta la mitad y lo que se ve
       son 2px exactos, a ras del borde. Y el ancho sale de `left:0 /
       right:0`, que para un absoluto es la caja de relleno del padre:
       la raya cruza la pestaña entera, no se queda corta.

       El resplandor NO puede ir aquí: `overflow:hidden` se come la
       sombra de un pseudo-elemento. Va en la pestaña, que es quien
       puede irradiar hacia fuera — ver la regla de arriba. */
    .hoja.on .pestana::after{ content:''; position:absolute; left:0; right:0; bottom:-2px; height:4px; background:var(--c) }

    .cuerpo{
      position:relative; border:1px solid color-mix(in srgb, var(--c) 55%, transparent);
      border-radius:16px; min-height:600px;
      background:linear-gradient(170deg, var(--pagina2) 0%, var(--pagina) 22%);
      box-shadow:var(--sombra), 0 0 55px -22px color-mix(in srgb, var(--c) 45%, transparent), inset 0 1px 0 var(--glow-suave);
      padding:var(--esp-6) var(--esp-6) var(--esp-7) var(--esp-7);
    }
    /* ── EL FILO DE ARRIBA DE LA HOJA ABIERTA (Rich, 9-nov-2026) ──────
       La raya de la pestaña mide 2px y va al color entero de la sección.
       El borde de arriba de la hoja era 1px al 55%, así que al lado de
       la pestaña se veía apagado y la línea parecía cortarse justo
       donde empieza la pestaña. Ahora las dos piezas se leen como una
       sola, encendida de lado a lado:

         * el borde pasa al color entero (quita el 55% de mezcla);
         * `inset 0 1px 0 var(--c)` pone el segundo píxel por dentro, y
           así el filo mide los mismos 2px que la raya SIN mover nada:
           una sombra no ocupa sitio, un borde de 2px sí movería el
           contenido un píxel hacia abajo. Esta capa ocupa el lugar del
           `inset 0 1px 0 var(--glow-suave)` de la regla de arriba —
           misma geometría, mismo oficio, el color de la sección en vez
           del blanco. El viejo se queda al final, debajo, por si algún
           tema no define `--c`.
         * `inset 0 10px 24px -8px` es el resplandor hacia abajo, sobre
           el contenido. Es la pareja por dentro del `0 7px 22px -4px`
           que la pestaña tira hacia fuera: mismo color, misma mezcla al
           55%, mismo desenfoque. Cae dentro de los 32px de relleno de
           arriba, así que baña el filo y no llega al título.

       La pestaña NO se toca. El único desajuste que queda es de un
       píxel: la raya de la pestaña va de -1 a 1 y el filo de la hoja de
       0 a 2, porque la pestaña sobresale un píxel por encima de la
       hoja. Se solapan en el píxel del medio y la pestaña queda
       mínimamente adelantada, que es justo lo que es. */
    .hoja.on .cuerpo{
      border-top-color:var(--c);
      box-shadow:var(--sombra),
                 0 0 55px -22px color-mix(in srgb, var(--c) 45%, transparent),
                 inset 0 1px 0 var(--c),
                 inset 0 10px 24px -8px color-mix(in srgb, var(--c) 55%, transparent),
                 inset 0 1px 0 var(--glow-suave);
    }
    /* Con la luz encendida el papel es casi blanco y un resplandor al 55%
       deja de leerse como luz: tiñe la hoja de verde, de naranja o de
       rosa treinta píxeles hacia abajo. Se baja al 30% y se recoge
       (menos desenfoque, menos caída) para que acompañe al filo sin
       manchar. La línea en sí no cambia: va al color entero en los dos
       temas, igual que la raya de la pestaña. */
    [data-tema="claro"] .hoja.on .cuerpo{
      box-shadow:var(--sombra),
                 0 0 55px -22px color-mix(in srgb, var(--c) 45%, transparent),
                 inset 0 1px 0 var(--c),
                 inset 0 6px 14px -8px color-mix(in srgb, var(--c) 30%, transparent),
                 inset 0 1px 0 var(--glow-suave);
    }
    .hoja.on .cuerpo::after{
      content:''; position:absolute; z-index:-1; left:8px; right:-6px; top:10px; bottom:-6px;
      border-radius:16px; background:var(--n2); border:1px solid var(--linea);
      box-shadow:6px 8px 0 -2px var(--n1), 6px 8px 0 -1px var(--linea), 12px 16px 0 -5px var(--n0), 12px 16px 0 -4px var(--linea);
    }
    /* Aquí iba la espiral: ocho anillos pegados al borde izquierdo de la
       LIBRETA, por debajo de la hoja abierta (z-index 2 contra 6). La hoja
       los tapaba siempre, así que nunca se veían… salvo al cambiar de
       pestaña: la hoja entra encogida y transparente ("traer") y los anillos
       asomaban como 3-5 huecos en el borde izquierdo —más o menos según lo
       alta que fuera la página—. Se quitó: la hoja terminada se ve igual. */
    .cantos{
      position:absolute; right:-2px; top:26px; bottom:26px; width:7px; border-radius:4px; opacity:.9; z-index:2; pointer-events:none;
      background:linear-gradient(180deg,
        #f6c945 0% 14%, #3ddc84 14% 28%, #38dde4 28% 42%, #f472b6 42% 57%,
        #a78bfa 57% 71%, #fb923c 71% 85%, #f87171 85% 100%);
      box-shadow:-2px 0 8px rgba(0,0,0,.5);
    }
    .cantos::before{ content:''; position:absolute; inset:0; border-radius:4px;
      background:repeating-linear-gradient(180deg, transparent 0 3px, rgba(0,0,0,.35) 3px 4px) }

    /* ═══════════════════════════════════════════════════════════════════
       DENTRO DE LA HOJA
    ══════════════════════════════════════════════════════════════════════ */
    /* Índice de la hoja: los destinos de siempre, con el traje nuevo. */
    .subnav{
      display:none; flex-wrap:wrap; align-items:center;
      column-gap:var(--esp-2); row-gap:var(--esp-2);
      margin:0 0 var(--esp-5); max-width:100%;
    }
    .subnav.abierta{ display:flex }
    .subnav.una-sola{ display:none }
    .nav-item{
      display:inline-flex; align-items:center; gap:var(--esp-2); margin:0;
      font-family:var(--font-sans); font-size:var(--fs-xs); padding:var(--esp-2) var(--esp-4); border-radius:20px;
      border:1px solid var(--linea); background:transparent; color:var(--gris);
      cursor:pointer; transition:all .25s;
    }
    .nav-item{ max-width:100% }
    .nav-item:hover{ color:var(--texto); border-color:var(--oro-dim); background:var(--glow-suave) }
    .nav-item.active{
      color:var(--c); border-color:color-mix(in srgb, var(--c) 60%, transparent);
      background:var(--glow-suave); box-shadow:0 0 14px -6px color-mix(in srgb, var(--c) 50%, transparent);
    }
    .nav-icon{ font-size:var(--fs-xs); width:auto; color:inherit; opacity:.85 }
    .nav-badge{
      display:none; min-width:19px; padding:var(--esp-1) var(--esp-2); border-radius:20px;
      background:var(--c); color:#141001; font-size:var(--fs-micro); font-weight:bold; text-align:center;
    }
    [data-tema="claro"] .nav-badge{ color:var(--text-on-gold) }

    /* Secciones: sólo se ve la activa, con la aparición de la referencia. */
    .section{ display:none }
    .section.active{ display:block; animation:traer .4s cubic-bezier(.2,.85,.25,1) }

    /* Títulos: el chasis los escribe con .section-title / .section-sub /
       .gold-line; la referencia los llama .titulo / .subt / .raya.

       CURSIVA LIGERA (Rich, 9-nov-2026): el título iba en negrita. Ahora
       va en peso normal, que en Times es lo más ligero que existe: la
       familia sólo trae Regular y Bold, y Chrome no adelgaza una letra
       que no tiene —al pedir 300 pintaría exactamente este mismo 400—.
       No se toca nada más: mismo tamaño, mismo color, misma cursiva y
       la raya de abajo igual.

       Los títulos dentro de las ventanas emergentes se quedan en
       negrita: los fija `.modal .section-title{font-weight:700}`, que
       pesa más que esta regla. */
    .section-title, .titulo{
      font-family:var(--font-serif); font-style:italic; font-size:var(--fs-xl); font-weight:normal;
      color:var(--texto); margin:0 0 0; letter-spacing:0;
    }
    .section-sub{ font-size:var(--fs-m); color:var(--gris); margin:var(--esp-1) 0 0; font-style:italic }
    .gold-line, .raya{
      height:2px; width:160px; border-radius:2px; border:none;
      background:linear-gradient(90deg, var(--c), transparent);
      box-shadow:0 0 10px color-mix(in srgb, var(--c) 55%, transparent);
      margin:var(--esp-3) 0 var(--esp-5);
    }

    /* Tarjetas */
    .card, .kpi-card{
      border-radius:14px; border:1px solid var(--linea); position:relative; overflow:hidden;
      background:linear-gradient(165deg, var(--n3), var(--n1) 70%);
      box-shadow:0 20px 50px -26px rgba(0,0,0,.85), inset 0 1px 0 var(--glow-suave);
      transition:all .4s cubic-bezier(.2,.8,.2,1);
      margin-bottom:var(--esp-3);
    }
    .card:hover, .kpi-card:hover{
      transform:translateY(-3px); border-color:var(--oro-dim);
      box-shadow:0 26px 60px -26px rgba(0,0,0,.9), 0 0 36px -14px var(--glow);
    }
    .card-header{ padding:var(--esp-4) var(--esp-4); border-bottom:1px solid var(--linea) }
    .card-title{ font-family:var(--font-serif); font-style:italic; font-size:var(--fs-base); font-weight:bold; color:var(--texto) }
    .card-body{ padding:var(--esp-4)}
    .eti{ font-size:var(--fs-micro); letter-spacing:3px; color:var(--oro-med); text-transform:uppercase }
    .cifra{
      font-weight:bold; font-variant-numeric:tabular-nums;
      background:linear-gradient(176deg, var(--oro-claro) 8%, var(--oro) 45%, var(--oro-med) 92%);
      -webkit-background-clip:text; background-clip:text; color:transparent;
    }

    /* Tablas */
    table, .tabla{ width:100%; border-collapse:collapse; font-size:var(--fs-s); font-family:var(--font-sans) }
    th, .tabla th{
      font-size:var(--fs-micro); letter-spacing: var(--track-ancho); color:var(--oro-med); text-align:left;
      padding:var(--esp-3) var(--esp-4); border-bottom:1px solid var(--linea); font-weight:normal;
      text-transform:uppercase; background:none;
    }
    thead tr{ background:none }
    td, .tabla td{ padding:var(--esp-3) var(--esp-4); border-bottom:1px solid var(--linea); color:var(--texto); vertical-align:middle }
    tbody tr{ transition:background .25s }
    tbody tr:hover{ background:var(--glow-suave) }
    .oro-t{ color:var(--oro); font-size:var(--fs-m); font-weight:bold }

    /* Chips, filtros y botones */
    .chip{ font-size:var(--fs-micro); padding:var(--esp-1) var(--esp-3); border-radius:20px; border:1px solid; display:inline-block; letter-spacing: var(--track-ancho) }
    .chip.ok{ color:var(--verde); border-color:var(--verde) }
    .chip.mal{ color:var(--rojo); border-color:var(--rojo) }
    .chip.oro{ color:var(--oro); border-color:var(--oro-med) }

    .btn, .btn-gold, .btn-primary{
      cursor:pointer; border:none; border-radius:9px; font-family:var(--font-sans);
      font-size:var(--fs-xs); letter-spacing: var(--track-ancho); padding:var(--esp-3) var(--esp-5); font-weight:bold;
      color:#141001; background:linear-gradient(170deg, var(--oro-claro), var(--oro) 60%);
      box-shadow:0 12px 32px -12px var(--glow); transition:all .25s;
      display:inline-flex; align-items:center; justify-content:center; gap:var(--esp-2);
    }
    [data-tema="claro"] .btn, [data-tema="claro"] .btn-gold, [data-tema="claro"] .btn-primary{ color:var(--text-on-gold) }
    .btn:hover, .btn-gold:hover, .btn-primary:hover{ transform:translateY(-2px); color:#141001 }
    [data-tema="claro"] .btn:hover, [data-tema="claro"] .btn-gold:hover, [data-tema="claro"] .btn-primary:hover{ color:var(--text-on-gold) }
    /* Botón secundario: dorado sobre fondo transparente y borde dorado.
       Tiene que leerse ANTES de pasar el mouse — "Cancelar" y "Descartar"
       son la salida, y una salida invisible no es una salida. */
    .btn.linea, .btn-outline,
    button.btn.linea, button.btn-outline{
      background:transparent; color:var(--oro) !important;
      border:1px solid var(--oro-med); box-shadow:none;
    }
    .btn.linea:hover, .btn-outline:hover{
      background:var(--glow-suave); color:var(--oro-claro) !important;
      border-color:var(--oro); transform:translateY(-2px);
    }
    /* Sobre crema, el dorado medio se queda en 4,1:1; el oscuro llega a
       6,6:1 y el botón se lee sin esfuerzo. */
    [data-tema="claro"] .btn.linea, [data-tema="claro"] .btn-outline{ color:var(--oro-claro) !important }
    [data-tema="claro"] .btn.linea:hover, [data-tema="claro"] .btn-outline:hover{ color:var(--oro-claro) !important }
    /* Los de peligro, igual: rojo visible de entrada. */
    .btn-danger, button.btn-danger{ color:var(--rojo) !important }
    .btn-danger:hover{ color:var(--rojo) !important; background:rgba(239,83,80,.12) }
    .btn-danger{ background:transparent; color:var(--rojo); border:1px solid var(--rojo); box-shadow:none }
    .btn-danger:hover{ background:rgba(239,83,80,.10); color:var(--rojo) }
    /* "Devolución" en una pendiente de pago: la acción más delicada, en
       rojo marcada o sin marcar. Sin marcar, contorno rojo; marcada, relleno
       rojo. Sobre el rojo claro del tema oscuro va tinta oscura (6:1) y sobre
       el rojo hondo del claro, blanco (7:1): con blanco al revés no se leería. */
    .btn.pend-rojo, .btn.pend-rojo:hover{
      background:transparent; color:var(--rojo) !important; border:1px solid var(--rojo);
      box-shadow:none;
    }
    .btn.pend-rojo:hover{ background:rgba(239,83,80,.12) }
    .btn.pend-rojo[aria-pressed="true"], .btn.pend-rojo[aria-pressed="true"]:hover{
      background:var(--rojo); color:#141001 !important; border-color:var(--rojo);
      box-shadow:0 12px 32px -12px rgba(239,83,80,.55);
    }
    [data-tema="claro"] .btn.pend-rojo[aria-pressed="true"],
    [data-tema="claro"] .btn.pend-rojo[aria-pressed="true"]:hover{ color:var(--text-on-gold) !important }
    .btn-sm{ padding:var(--esp-2) var(--esp-4); font-size:var(--fs-xs); letter-spacing: var(--track-etiqueta) }
    .btn-icon{ width:34px; height:34px; padding:0; border-radius:9px }
    .btn:disabled, .btn[disabled], .btn.is-disabled{ opacity:.45; cursor:not-allowed; transform:none; box-shadow:none }

    /* Formularios */
    label{ color:var(--gris); font-size:var(--fs-xs); letter-spacing: var(--track-etiqueta) }
    input, select, textarea{
      font-family:var(--font-sans); font-size:var(--fs-s);
      background:var(--n1); border:1px solid var(--linea); color:var(--texto);
      border-radius:9px; padding:var(--esp-2) var(--esp-3); color-scheme:dark;
    }
    [data-tema="claro"] input, [data-tema="claro"] select, [data-tema="claro"] textarea{ color-scheme:light; background:var(--n2) }
    input::placeholder, textarea::placeholder{ color:var(--gris-2); font-style:italic }
    input:focus, select:focus, textarea:focus{
      border-color:var(--oro-med); box-shadow:0 0 16px -8px var(--glow); background:var(--n2);
    }
    [data-tema="claro"] input:focus, [data-tema="claro"] select:focus, [data-tema="claro"] textarea:focus{ background:var(--n3) }
    select option{ background:var(--n2); color:var(--texto) }

    /* Avisos */
    .alert{ border-radius:10px; padding:var(--esp-3) var(--esp-4); font-size:var(--fs-m)}
    .alert-success{ background:var(--positive-bg); border:1px solid var(--verde); color:var(--verde) }
    .alert-error{ background:var(--negative-bg); border:1px solid var(--rojo); color:var(--rojo) }
    .empty{ color:var(--gris); font-style:italic; text-align:center; padding:var(--esp-6) var(--esp-4)}
    .empty-icon{ color:var(--oro-med); opacity:.5 }
    .badge{ font-size:var(--fs-micro); padding:var(--esp-1) var(--esp-3); border-radius:20px; border:1px solid; display:inline-block; letter-spacing: var(--track-ancho) }
    .badge-green{ color:var(--verde); border-color:var(--verde); background:none }
    .text-gold{ color:var(--oro) } .text-green{ color:var(--verde) } .text-red{ color:var(--rojo) }
    .text-muted{ color:var(--gris) }
    .fw-serif{ font-family:var(--font-serif); font-style:italic; color:var(--texto) }

    /* Rejillas y piezas del bento */
    .grid2{ display:grid; grid-template-columns:1fr 1fr; gap:var(--esp-3)}
    .bento{ display:grid; grid-template-columns:minmax(0,1.55fr) minmax(0,1fr); grid-template-rows:auto auto; gap:var(--esp-3); margin-bottom:var(--esp-4)}
    .bento .card{ margin-bottom:0 }
    .spark{ height:76px; width:100%; display:block; margin-top:auto }
    .barras{ display:flex; align-items:stretch; justify-content:center; gap:var(--esp-1); height:124px }
    /* Con muchas horas las barras se reparten el ancho; con una sola, el
       tope de 64px evita el pilar gigante que salía antes. */
    .barra{ flex:1 1 0; max-width:64px; display:flex; flex-direction:column; gap:var(--esp-1); align-items:center; height:100% }
    /* La barra vive DENTRO de esta caja, no en todo el alto de la tarjeta:
       así la hora de abajo siempre tiene su sitio y nunca acaba encima del
       oro. `min-height:0` es lo que permite que la caja se encoja. */
    .barra-caja{ flex:1 1 auto; min-height:0; width:100%; display:flex; flex-direction:column; justify-content:flex-end; align-items:center }
    .barra i{
      width:100%; border-radius:4px 4px 2px 2px; min-height:3px;
      background:linear-gradient(180deg, var(--oro), var(--oro-med));
      box-shadow:0 0 12px -3px var(--glow);
      display:flex; align-items:flex-start; justify-content:center; padding-top:var(--esp-1);
    }
    /* La cifra SOBRE el oro va en negro y en negrita. Dorado sobre dorado
       no se lee, y justamente ésta es la cifra que se viene a mirar. El
       oro es oro en los dos temas, así que el negro sirve para los dos. */
    .barra i .barra-n{ color:#141001; font-weight:bold; font-size:var(--fs-xs); line-height:var(--lh-plano)}
    /* Barra demasiado baja para meterle el número dentro: se pone encima, y
       ahí va en el color normal del texto porque el fondo ya no es dorado. */
    .barra-n.fuera{ color:var(--texto); font-weight:bold; font-size:var(--fs-xs); line-height:var(--lh-plano); margin-bottom:var(--esp-1)}
    /* La hora va fuera de la barra: color de texto normal, no el tenue. */
    .barra small{ font-size:var(--fs-xs); color:var(--gris); flex:0 0 auto; font-variant-numeric:tabular-nums }
    .anillo{ position:relative; width:78px; height:78px; flex-shrink:0 }
    .anillo-txt{ position:absolute; inset:0; display:flex; flex-direction:column; align-items:center; justify-content:center }

    /* ═══════════════════════════════════════════════════════════════════
       BLOQUES, LISTAS Y TABLERO  (Rich, 10-nov-2026)

       El color no se escribe en ningún sitio: lo pone la hoja abierta,
       que ya declara `--c`. La misma regla pinta oro en Inicio y naranja
       en Cierre del Día.

       EL REPUESTO IMPORTA. Fuera de la libreta —los modales que cuelgan
       del final del documento— `--c` no existe, y `color-mix()` con una
       variable vacía es inválido: se cae la declaración entera y el
       elemento se queda sin borde o sin color. Por eso todas las reglas
       piden `var(--c, var(--oro))`.

       NADA SE QUITA. Aquí no se borra ningún elemento ni se esconde
       nada: son bordes, fondos, un pseudo-elemento y dos sombras.
    ══════════════════════════════════════════════════════════════════════ */

    /* ── El título de cada bloque, con su raya ─────────────────────────
       El hilo de la cabecera se queda —separa— y encima se le posa un
       trozo de 56px en el color de la sección, bajo el título. La raya no
       cruza la tarjeta: marca dónde empieza. Es la misma figura que la
       raya bajo la pestaña, en pequeño. */
    .card-header{ position:relative }
    .card-header::after{
      content:''; position:absolute; left:var(--esp-4); bottom:-1px;
      width:56px; height:2px; background:var(--c, var(--oro));
      box-shadow:0 2px 10px -2px color-mix(in srgb, var(--c, var(--oro)) 70%, transparent);
    }

    /* ── La actividad reciente: el icono, en su cuadrito ───────────────
       El filo va del color del TIPO de operación, que no es el de la
       sección: el tipo tiene su propio código y eso significa algo. El
       JS ya pinta ese color en línea sobre el icono, así que
       `currentColor` lo recoge solo, sin escribir ni uno. */
    .muro-card-icon{
      border:1px solid currentColor;
      background:color-mix(in srgb, currentColor 12%, transparent);
      box-shadow:0 0 16px -8px currentColor;
    }

    /* ── Las tarjetas de tipo, en Registrar ────────────────────────────
       Filo de la sección en sordina; al apuntarla, se enciende. Se quita
       el salto de dos píxeles al pasar por encima: era el tic de
       catálogo de ventas y aquí no pega. El icono se alinea con el
       título en vez de quedar centrado sobre texto que va a la
       izquierda.

       ACOTADO A LA REJILLA DE REGISTRAR. La clase `.cap-tipo-card` la
       reutilizan los botones de "¿Con qué se puede pagar?" dentro de un
       modal de Configuración, que vive en la hoja de Administración: con
       la regla suelta, esos botones salían con el filo ROJO mientras su
       estado elegido sigue siendo oro (`.pcat-moneda-btn.selected`), y
       quedaban descasados. Pidiendo `.cap-tipos-grid` delante, la regla
       sólo alcanza a las tarjetas de Registrar, que es lo que se pidió.
       Configuración se queda exactamente como está. */
    .cap-tipos-grid .cap-tipo-card{
      border-color:color-mix(in srgb, var(--c, var(--oro)) 30%, transparent);
      transition:border-color .18s, background .18s, box-shadow .18s;
    }
    .cap-tipos-grid .cap-tipo-card .cap-tipo-icon{ color:var(--c, var(--oro)); justify-content:flex-start }
    .cap-tipos-grid .cap-tipo-card:hover,
    .cap-tipos-grid .cap-tipo-card:focus-visible,
    .cap-tipos-grid .cap-tipo-card.elegida{
      transform:none;
      border-color:var(--c, var(--oro));
      background:color-mix(in srgb, var(--c, var(--oro)) 8%, transparent);
      box-shadow:0 0 30px -12px var(--c, var(--oro)),
                 inset 0 0 0 1px color-mix(in srgb, var(--c, var(--oro)) 35%, transparent);
    }

    /* ── El tablero de Inicio ──────────────────────────────────────────
       Ya estaba montado con estas clases; lo que le faltaba era el
       trato. */
    .bento .card, .grid2 .card{ border-color:color-mix(in srgb, var(--c, var(--oro)) 26%, transparent) }
    /* Los bloques que llevan a otra pantalla se encienden al apuntarlos:
       eso dice "esto se pulsa" mejor que un cursor. */
    .bento .card.u-clic:hover,
    .bento .card.u-clic:focus-visible{
      border-color:var(--c, var(--oro));
      box-shadow:0 0 34px -14px color-mix(in srgb, var(--c, var(--oro)) 80%, transparent);
    }
    /* La cifra grande ya traía el degradado del oro recortado sobre el
       texto. Le faltaba la luz: un `drop-shadow` sigue la forma de las
       letras, no la caja, así que el resplandor sale del número y no de
       un rectángulo detrás. */
    .cifra{ filter:drop-shadow(0 0 26px color-mix(in srgb, var(--c, var(--oro)) 40%, transparent)) }
    /* El rótulo pequeño pasa del oro fijo al color de la sección,
       mezclado con el gris para que siga siendo rótulo y no titular. */
    .eti{ color:color-mix(in srgb, var(--c, var(--oro)) 70%, var(--gris)) }
    /* Los chips: punto a la izquierda y cápsula con fondo tenue. El
       color NO se escribe — sale de `currentColor`, que ya pone la
       variante (.ok verde, .mal rojo, .oro dorado). Los colores con
       significado siguen significando lo mismo. */
    .chip{
      display:inline-flex; align-items:center; gap:7px;
      background:color-mix(in srgb, currentColor 11%, transparent);
      border-color:color-mix(in srgb, currentColor 50%, transparent);
    }
    .chip::before{
      content:''; flex:0 0 auto; width:7px; height:7px; border-radius:50%;
      background:currentColor; box-shadow:0 0 8px -1px currentColor;
    }
    /* La curva del día y el arco del anillo llevan el color como
       ATRIBUTO del SVG, y una regla de CSS le gana a un atributo de
       presentación: por eso no hay que tocar el marcado. */
    .spark path{ stroke:var(--c, var(--oro)) }
    #muro-anillo-arco{ stroke:var(--c, var(--oro)) }
    /* Las barras ya eran doradas y ya llevan el número dentro, en negro
       sobre el oro. Sólo cambia el color por el de la sección. */
    .barra i{
      background:linear-gradient(180deg, var(--c, var(--oro)), color-mix(in srgb, var(--c, var(--oro)) 58%, #000));
      box-shadow:0 0 14px -3px color-mix(in srgb, var(--c, var(--oro)) 70%, transparent);
    }
    /* La tabla sin rejilla va SÓLO en la del tablero. Quitarle la línea
       a todas las tablas del sistema dejaría la Bitácora, Usuarios y los
       Movimientos —que tienen decenas de filas— sin nada donde apoyar el
       ojo. Ahí la rejilla no es adorno: es lo que deja seguir una fila
       de izquierda a derecha sin perderla. */
    #muro-monedas td{ border-bottom:none }
    #muro-monedas th{
      color:color-mix(in srgb, var(--c, var(--oro)) 70%, var(--gris));
      border-bottom-color:color-mix(in srgb, var(--c, var(--oro)) 40%, transparent);
    }

    /* ── El aviso de pendientes de pago, en Inicio ─────────────────────
       Cada línea salía subrayada y centrada, como un enlace dentro de un
       documento de Word. En una aplicación los enlaces no se subrayan, y
       una fila de datos no se centra: se alinea a la izquierda y el dato
       de la derecha se va al borde.

       Las tres cosas vienen de `.pm-history-link`, no del `style=` que el
       código escribe en cada fila —ese sólo fija el reparto—, así que se
       arreglan desde aquí sin tocar app.js:

         * `align-self:stretch` en vez de `center`. El contenedor es una
           columna flexible; centrada, cada fila se encogía a su contenido
           y quedaba flotando en medio. Estirada, ocupa el ancho entero y
           el `justify-content:space-between` que ya traía manda los días
           al borde derecho: los días quedan en columna solos.
         * fuera el subrayado.
         * y como el subrayado era lo único que decía "esto se pulsa", la
           fila se enciende al pasar por encima. Sin eso quedaría un
           renglón de texto muerto.

       Acotado a `#muro-pendientes-pago`: la misma clase aparece 24 veces
       en app.js, y las otras 23 son enlaces sueltos dentro de una frase
       —"Ver historial de cambios", "ver mes actual", "foto"—, donde el
       subrayado está bien puesto y se queda. */
    #muro-pendientes-pago .pm-history-link{
      align-self:stretch;
      text-decoration:none;
      transition:background .15s, color .15s;
    }
    #muro-pendientes-pago .pm-history-link:hover{
      background:color-mix(in srgb, var(--c, var(--oro)) 9%, transparent);
    }

    /* ═══════════════════════════════════════════════════════════════════
       "+ REGISTRAR": botón flotante, siempre a mano
    ══════════════════════════════════════════════════════════════════════ */
    #registrar-bar{ display:none }
    /* En el flujo de la hoja y centrado, no flotando sobre el contenido.
       Va ARRIBA del hueco, pegado a la raya dorada del título: el aire
       queda DEBAJO, separándolo de las tarjetas. Antes el hueco se repartía
       casi por igual arriba y abajo y el botón se leía como parte del
       bloque de tarjetas en vez de como parte del encabezado. */
    #registrar-bar.visible{ display:flex; justify-content:center; margin:0 0 var(--esp-6)}
    /* La raya cede su margen de abajo cuando el botón viene detrás: ese
       margen es justo el aire que había que quitar de encima del botón. */
    .raya:has(+ #registrar-bar.visible),
    .gold-line:has(+ #registrar-bar.visible){ margin-bottom:var(--esp-2)}
    #btn-registrar-main{
      position:static; width:auto; min-height:0; max-width:none;
      display:inline-flex; align-items:center; justify-content:center; gap:var(--esp-3);
      padding:var(--esp-4) var(--esp-7); border:none; border-radius:30px;
      background:linear-gradient(170deg, var(--oro-claro), var(--oro) 60%); color:#141001;
      font-family:var(--font-sans); font-size:var(--fs-m); font-weight:bold; letter-spacing: var(--track-ancho);
      box-shadow:0 14px 34px -14px var(--glow), 0 0 0 1px var(--oro-med);
      transition:all .25s;
    }
    /* El oro NO es el mismo en los dos temas, y por eso la letra tampoco.
       En el oscuro el oro es brillante (#f6c945) y encima va letra oscura.
       En el claro el mismo nombre vale un oro oscuro (#9c7500): ahí la letra
       oscura da 4,49 de contraste —por debajo del mínimo legible— y el
       blanco todavía menos (4,24). Se baja el fondo a --oro-claro (#7a5a00),
       que con blanco da 6,38, y es lo que ya hace .btn-gold en este tema. */
    [data-tema="claro"] #btn-registrar-main{
      background:var(--oro-claro); color:var(--text-on-gold);
    }
    #btn-registrar-main:hover{ transform:translateY(-3px); box-shadow:0 24px 50px -14px var(--glow), 0 0 0 1px var(--oro) }
    #btn-registrar-main .reg-plus{ font-size:var(--fs-base); line-height:var(--lh-plano); margin:0 }

    /* ═══════════════════════════════════════════════════════════════════
       PANTALLA DE ENTRADA Y BLOQUEO
    ══════════════════════════════════════════════════════════════════════ */
    #login-screen{ background:var(--n0); background-image:radial-gradient(1100px 520px at 50% -12%, var(--glow-suave), transparent 60%) }
    .login-card{
      background:linear-gradient(170deg, var(--pagina2) 0%, var(--pagina) 22%);
      border:1px solid var(--oro); border-radius:16px; box-shadow:var(--sombra);
      /* Sólo para que el resplandor de abajo se apoye en la tarjeta. No
         mueve nada: sin `top`/`left` un relativo se queda donde estaba, y
         dentro de la tarjeta no hay ningún hijo absoluto al que servirle
         de referencia. */
      position:relative;
    }
    /* ── EL LATIDO DEL RECUADRO DE ENTRADA (Rich, 9-nov-2026) ─────────
       El resplandor va en un pseudo-elemento calcado a la tarjeta, no en
       la tarjeta misma, por dos razones:

         * lo que late es la OPACIDAD de esa capa, no una sombra. Animar
           una sombra obliga al navegador a volver a dibujarla en cada
           cuadro; la opacidad la resuelve el compositor, así que el
           latido sale continuo aunque la máquina esté ocupada —y esta
           pantalla se abre en el equipo de la caja, que no va sobrado—;
         * la tarjeta conserva su propia `box-shadow` (la sombra negra
           que la levanta del fondo) sin tener que reescribirla.

       No hace falta `z-index`: una sombra exterior se recorta contra la
       caja que la proyecta, así que esta cae ENTERA por fuera del
       recuadro y no pasa por encima de los campos ni del botón.

       La opacidad en reposo es .72. Importa: cuando el sistema del
       usuario pide menos movimiento se apaga la animación y la capa se
       queda en ese valor —encendida y quieta—, no en el pico. */
    .login-card::before,
    .lock-card::before{
      content:''; position:absolute; inset:0; border-radius:inherit; pointer-events:none;
      opacity:.72;
      /* Tres capas, como un tubo de neón: el filo encendido pegado al
         borde, el halo medio y el velo que llega lejos y casi no se ve.
         Sin la primera, la luz parece venir de detrás de la tarjeta en
         vez de del propio borde. */
      box-shadow:0 0 9px -1px var(--glow),
                 0 0 28px -2px var(--glow),
                 0 0 64px 4px color-mix(in srgb, var(--oro) 20%, transparent);
      animation:latido-oro 4s ease-in-out infinite;
    }
    /* Ida y vuelta en 4 s, con `ease-in-out` a los dos lados: sube y baja
       sin un solo salto. El suelo es .34 para que nunca llegue a
       apagarse del todo —un parpadeo que se corta cansa más que uno que
       respira—. */
    @keyframes latido-oro{
      0%, 100%{ opacity:.34 }
      50%     { opacity:1 }
    }
    /* Con la luz encendida, NO late. Y no es capricho: en este tema
       `--oro` vale #9c7500, un mostaza oscuro, no el #f6c945 de la
       noche. Un halo de ese color sobre papel casi blanco no ilumina —
       oscurece—, así que se lee como una sombra sucia, y un latido no
       hace más que ponerla a respirar. Lo que sí funciona en papel es un
       filo: el borde dorado de la tarjeta con un cerco finito al 14% y
       un velo muy corto detrás. Queda encendido y quieto. */
    [data-tema="claro"] .login-card::before,
    [data-tema="claro"] .lock-card::before{
      opacity:1;
      box-shadow:0 0 0 3px color-mix(in srgb, var(--oro) 14%, transparent),
                 0 0 14px -5px color-mix(in srgb, var(--oro) 34%, transparent);
      animation:none;
    }
    .login-brand{ font-family:var(--font-serif); font-style:italic; color:var(--texto) }
    .login-brand span{ color:var(--oro) }
    .login-sub{ font-family:var(--font-sans); font-style:normal; font-size:var(--fs-micro); letter-spacing:5px; color:var(--gris-2) }
    /* Página cargada con la sesión bloqueada: ni la libreta ni el login se
       pintan por detrás. No es sólo estética — detrás del candado no debe
       quedar ni un dato del negocio a la vista. */
    body.bloqueada-al-entrar #app,
    body.bloqueada-al-entrar #login-screen,
    body.bloqueada-al-entrar .malla,
    html.bloqueada-al-entrar #app,
    html.bloqueada-al-entrar #login-screen,
    html.bloqueada-al-entrar .malla{ display:none !important }

    /* El recuadro del bloqueo lleva el mismo tratamiento que el de la
       entrada: borde en el dorado de la casa y el resplandor que late.
       Las reglas no se repiten — la capa `::before`, el `@keyframes`, el
       parche del tema claro y el de «menos movimiento» están escritos
       una sola vez, más arriba, con los dos recuadros en el selector.
       Aquí sólo va lo propio de éste.

       `position:relative` es para que la capa del resplandor se apoye en
       la tarjeta. Sin `top`/`left` no mueve nada, y dentro del bloqueo
       no hay ningún hijo absoluto al que servirle de referencia.

       Lo demás de esta pantalla se queda como estaba: el ancho, el
       relleno, el texto centrado y su propia sombra negra —que viene de
       una regla anterior y no se toca— siguen igual. */
    .lock-card{
      background:linear-gradient(170deg, var(--pagina2) 0%, var(--pagina) 22%);
      border:1px solid var(--oro); border-radius:16px;
      position:relative;
    }

    /* ── Modales ──────────────────────────────────────────────────────
       Eran estrechos para lo que se hace dentro (formularios con dos
       columnas, listas de productos). Se ensanchan un 40% —los anchos que
       traía cada uno escritos a mano se recalculan abajo— y el contenido
       crece con ellos: título, campos, botones y aire.
       El tope de 92% del ancho de la ventana los mantiene dentro de
       cualquier pantalla. */
    .modal{
      max-width:min(784px, 92vw);
      padding:0;
    }
    .modal .modal-header, .modal .modal-head{ padding:var(--esp-4) var(--esp-6)}
    .modal .modal-body{ padding:var(--esp-5) var(--esp-6)}
    .modal .modal-footer, .modal .modal-foot{ padding:var(--esp-4) var(--esp-6)}
    .modal .modal-title{ font-size:var(--fs-l)}
    .modal .form-grid{ gap:var(--esp-4)}
    .modal label{ font-size:var(--fs-s)}
    .modal input, .modal select, .modal textarea{ font-size:var(--fs-m); padding:var(--esp-3) var(--esp-4)}
    .modal .btn{ font-size:var(--fs-s); padding:var(--esp-3) var(--esp-6)}
    .modal .btn-sm{ font-size:var(--fs-s); padding:var(--esp-2) var(--esp-4)}
    .modal .btn-row{ gap:var(--esp-3)}

    /* ── Grosor de la letra dentro del modal ──────────────────────────
       El tamaño y el ancho ya estaban bien; lo que se leía flojo era el
       trazo. Un modal se lee de pie, deprisa y a veces con el papel del
       mostrador de por medio, así que dentro va todo más grueso que en
       el resto de la app. Sólo cambia el peso: ni un píxel de tamaño,
       de ancho ni de espaciado se toca aquí.

       Va por PESO y no por tamaño porque la letra de la app es Times New
       Roman, que trae dos trazos (normal y negrita): cualquier valor de
       600 para arriba cae en la negrita real de la fuente, sin que el
       navegador tenga que engordarla él. */
    .modal .modal-body,
    .modal .modal-head, .modal .modal-header,
    .modal .modal-foot, .modal .modal-footer{ font-weight:600 }

    /* Lo que nombra las cosas va un punto más arriba todavía: son las
       palabras que se buscan con el ojo antes de leer nada. */
    .modal label,
    .modal .modal-title,
    .modal .section-title,
    .modal .cap-tipos-section-title,
    .modal .ver-detail-section-title,
    .modal legend,
    .modal dt,
    .modal th{ font-weight:700 }

    /* Lo que el operador escribe y lo que elige en una lista: se lee
       tanto como la etiqueta que lo nombra. */
    .modal input, .modal select, .modal textarea, .modal option{ font-weight:600 }

    /* Las excepciones se respetan solas: los "(opcional)" y las notas al
       pie llevan font-weight:400 escrito en su propia etiqueta, y un
       estilo en línea gana a todo esto. La jerarquía se mantiene. */

    /* Modales: el mismo papel de la hoja. */
    .modal{
      background:linear-gradient(170deg, var(--pagina2) 0%, var(--pagina) 22%);
      border:1px solid var(--oro-dim); border-radius:16px; box-shadow:var(--sombra);
    }
    .modal-header, .modal-head, .modal-footer, .modal-foot{ border-color:var(--linea) }
    .modal-title{ font-family:var(--font-serif); font-style:italic; color:var(--texto); font-size:var(--fs-base)}
    .modal-close{ background:var(--n2); color:var(--gris); border-radius:9px }
    .modal-close:hover{ background:var(--glow-suave); color:var(--oro) }

    /* ═══════════════════════════════════════════════════════════════════
       PANTALLAS CHICAS
    ══════════════════════════════════════════════════════════════════════ */
    @media (max-width:900px){
      #app{ padding:0 var(--esp-3) var(--esp-6)}
      .top{ flex-wrap:wrap; gap:var(--esp-3)}
      .marca{ font-size:var(--fs-l)}
      #bg-wrap{ order:5; flex:1 1 100%; max-width:none }
      .util{ font-size:var(--fs-micro); gap:var(--esp-2)}
      .bento, .grid2{ grid-template-columns:1fr }
      .cuerpo{ padding:var(--esp-5) var(--esp-4) var(--esp-5) var(--esp-6)}
      .pestana{ letter-spacing:0 }
      .libreta{ margin-top:74px }
      .sesion .qn{ display:none }
      .cantos{ display:none }
      /* En pantalla chica el panel no cabe colgando de la campana: se pone
         fijo, con un dedo de margen a cada lado, y recorre por dentro. Así
         no tapa la barra ni empuja nada de la página. */
      .campana-panel{
        position:fixed; left:10px; right:10px; width:auto; max-width:none;
        /* El `top` real lo pone el JS al abrirla, justo debajo del botón: en
           pantalla chica la barra se parte en dos filas (el buscador baja a
           la suya) y la campana no está siempre a la misma altura. Esto es
           sólo el valor de partida. */
        top:68px; max-height:min(72vh, 520px);
      }
      .campana-btn{ width:36px; height:36px }
      .campana-btn svg{ width:17px; height:17px }
    }
    /* Sólo el símbolo: por debajo de esto, siete nombres no caben sin
       cortarse. El nombre sigue estando en el title de cada pestaña. */
    @media (max-width:720px){
      .pestana{ padding-top:var(--esp-3)}
      .pestana .rotulo{ display:none }
      .pestana .mini{ font-size:var(--fs-base); margin-bottom:0 }
      .hoja.on .pestana{ padding-top:var(--esp-4)}
    }

    @media (max-width:640px){
      .cuerpo{ padding:var(--esp-5) var(--esp-3)}
      #btn-registrar-main{ padding:var(--esp-3) var(--esp-5); font-size:var(--fs-s)}
    }

    /* En papel sobran los mandos: lo que se imprime es el resultado. */
    @media print{
      .inf-paso, #registrar-bar{ display:none !important }
      .inf-cabecera{ border-top:none; margin-top:0 }
      .gf-card{ break-inside:avoid; page-break-inside:avoid }
    }
    @media print{
      body{ background:#fff }
      .malla, .top, .pestana, .cantos, .subnav, #registrar-bar{ display:none !important }
      .cuerpo{ border:none; box-shadow:none; padding:0; background:none }
      /* El filo encendido de la hoja abierta tiene más peso que la regla
         de arriba (dos clases contra una), así que se apaga aparte: en
         papel no se imprime una línea de color ni un resplandor. */
      .hoja.on .cuerpo{ border:none; box-shadow:none }
      .hoja.on .cuerpo::after{ display:none }
    }
    @media (prefers-reduced-motion:reduce){
      .hoja.on, .section.active{ animation:none !important }
      /* Los recuadros de entrada y de bloqueo dejan de latir y se quedan
         encendidos fijos: la capa del resplandor vuelve a su opacidad en
         reposo (.72), que es justo el punto medio del latido. */
      .login-card::before,
      .lock-card::before{ animation:none !important }
    }
    /* ═══════════════════════════════════════════════════════════════════
       REMATES — lo que quedó del traje anterior dentro de las hojas
       ═══════════════════════════════════════════════════════════════════
       Reglas viejas con el color escrito a mano (blancos sobre fondo
       oscuro, fondos negros fijos). Con el tema claro encendido eran
       texto invisible; aquí vuelven a hablar en variables.
    ══════════════════════════════════════════════════════════════════════ */
    .disp-persona-card-saldo, .disp-hero-value, .ing-cat-label,
    .pm-panel .pm-field-value{ color:var(--texto) }
    .disp-modal-title span, .pm-table-wrap td{ color:var(--texto) }
    .pm-filters input[type="date"]{ background:var(--n1); color:var(--texto); border-color:var(--linea) }
    [data-tema="claro"] .pm-filters input[type="date"]{ background:var(--n2) }
    [data-tema="claro"] .cap-photo-btn:hover,
    [data-tema="claro"] .pa-btn-aprobar,
    [data-tema="claro"] .ver-row-btn.ok{ color:var(--text-on-gold) }

    /* Actividad reciente: renglones de libreta, no tarjetas dentro de una
       tarjeta. La franja de color de cada evento se conserva — es la que
       dice de un vistazo de qué tipo es. */
    #muro-content{ gap:0 }
    #muro-content .muro-card{
      background:transparent; border:none; border-bottom:1px solid var(--linea);
      border-left:3px solid var(--linea); border-radius:0; padding:var(--esp-3) var(--esp-3);
      transition:background .25s;
    }
    #muro-content .muro-card:hover{ background:var(--glow-suave) }
    #muro-content .muro-card:last-child{ border-bottom:none }
    .muro-card-icon{ background:var(--glow-suave); color:var(--oro); border-radius:9px }
    .muro-card-titulo{ color:var(--texto); font-weight:bold }
    .muro-card-tiempo{ color:var(--gris-2); font-size:var(--fs-s); font-style:italic }
    .muro-day-sep{ color:var(--oro-med); font-size:var(--fs-micro); letter-spacing:3px; text-transform:uppercase; padding:var(--esp-3) var(--esp-3) var(--esp-1)}
    .muro-empty{ color:var(--gris); font-style:italic; text-align:center; padding:var(--esp-6)}

    /* El resto de piezas heredadas que dan color por su cuenta. */
    .spinner{ border-color:var(--linea); border-top-color:var(--oro) }
    .disp-hero, .ver-row, .precios-card{
      background:linear-gradient(165deg, var(--n3), var(--n1) 70%);
      border:1px solid var(--linea); border-radius:14px;
    }
    .gold-line + .section-sub{ margin-top:calc(-1 * var(--esp-3))}

    /* Cabecera de hoja + índice: cuando la hoja tiene índice, el nombre de
       la página lo dice la pestaña marcada del índice, así que el título
       que cada sección trae dentro sobra — se calla para no repetir
       encabezado. Su subtítulo sí se queda: es el que explica la página. */
    /* El aviso de servidor viejo empuja la página hacia abajo; ahora que
       la página entera se desplaza, no debe además recortarle el alto. */
    body.con-version-banner #app{ height:auto; margin-top:var(--version-banner-h, 42px) }

    .hoja-cabecera{ display:block }
    .cuerpo:has(.subnav.una-sola) .hoja-cabecera{ display:none }
    .subnav.abierta:not(.una-sola) ~ .section .section-title{ display:none }
    .subnav.abierta:not(.una-sola) ~ .section .section-sub{ margin-top:0 }

    /* Los títulos que el chasis escribe como <p> arrastraban el margen de
       párrafo; en la hoja mandan las medidas de la referencia. */
    p.section-title{ margin:0 }
    p.section-sub{ margin:var(--esp-1) 0 0}

    /* ── Cuenta seleccionada en Disponibles ───────────────────────────
       Al seleccionarla, la tarjeta se pone dorada (.selected cambia el
       fondo a --bg-gold-grad). El texto tiene que cambiar con ella: si no,
       el nombre y el monto se quedan en el color de sobre papel y sobre
       oro no se leen. Va a --text-on-gold, que es el token que ya usan los
       botones dorados y que se da vuelta solo con el tema: tinta oscura
       sobre el oro brillante del tema oscuro, blanco sobre el oro apagado
       del tema claro. Aplica igual a las cuentas en bolívares y a las de
       USDT, que comparten tarjeta. */
    .disp-persona-card.selected .disp-persona-card-name,
    .disp-persona-card.selected .disp-persona-card-saldo,
    .disp-persona-card.selected .disp-persona-card-saldo small,
    .disp-persona-card.selected .disp-persona-card-sub,
    .disp-persona-card.selected .disp-persona-card-badge{ color:var(--text-on-gold) }

    /* Lo secundario (la unidad y el rótulo de abajo) se atenúa, pero sin
       pasarse: con el tema claro el oro es oscuro y hay menos margen. */
    .disp-persona-card.selected .disp-persona-card-saldo small,
    .disp-persona-card.selected .disp-persona-card-sub{ opacity:.72 }
    [data-tema="claro"] .disp-persona-card.selected .disp-persona-card-saldo small,
    [data-tema="claro"] .disp-persona-card.selected .disp-persona-card-sub{ opacity:.9 }

    /* Las insignias ("Cuenta principal", "USDT") llevaban fondo y borde
       propios pensados sobre la tarjeta apagada; sobre el oro se pierden. */
    .disp-persona-card.selected .disp-persona-card-badge{
      background:rgba(0,0,0,.16); border-color:transparent;
    }
    /* El oro del tema claro es oscuro de por sí; para esta tarjeta baja un
       punto más, que es lo que hace legible al blanco de encima. */
    [data-tema="claro"] .disp-persona-card.selected{
      background:linear-gradient(170deg, #6f5200, #8a6600 60%);
    }
    [data-tema="claro"] .disp-persona-card.selected .disp-persona-card-badge{
      background:rgba(255,255,255,.22);
    }

    /* ═══════════════════════════════════════════════════════════════════
       TIPOGRAFÍA DEL PANEL DE VERIFICACIÓN
       ═══════════════════════════════════════════════════════════════════
       Esta pantalla se LEE, no se hojea: son frases —por qué la IA mandó
       una transacción al admin, qué se registró, a qué hora— y estaban
       escritas del tamaño de una etiqueta. Suben dos puntos.

       El tamaño sale de estas tres variables y de ningún otro sitio: para
       moverlo otra vez se tocan aquí, no fila por fila. Van con el ámbito
       de la sección para no arrastrar al resto de la app.

       Quedan fuera a propósito, porque son de mirar y no de leer:
         · el monto de la derecha (.ver-row-monto y su subtítulo),
         · las etiquetas cortas de estado (IA, peso, Vision, observada),
         · los botones de la fila y el índice de arriba.
    ══════════════════════════════════════════════════════════════════════ */
    #section-verificacion{
      /* Esta hoja tenía su propia mini-escala de 17/16/15px, que son tres
         peldaños de un píxel: justo lo que la escala vino a quitar. Ahora
         apunta a los escalones de verdad, y mantiene los tres niveles
         separados —18/16/14— en vez de confundirse en dos. */
      --ver-texto: var(--fs-base);  /* frases: el titular de la fila y el aviso de la IA */
      --ver-aviso: var(--fs-m);     /* el motivo de la IA y la observación guardada */
      --ver-nota:  var(--fs-s);     /* segunda línea: hora, detalle y pie del aviso */
    }
    #section-verificacion .section-sub{ font-size:var(--fs-base)}
    #section-verificacion .ver-row{ font-size:var(--ver-texto) }
    #section-verificacion .ver-row-titulo{ font-size:var(--ver-texto) }
    #section-verificacion .ver-ia-bar-texto{ font-size:var(--ver-texto) }
    #section-verificacion .ver-row-titulo .ver-row-id,
    #section-verificacion .ver-row-sub,
    #section-verificacion .ver-ia-bar-sub{ font-size:var(--ver-nota) }
    #section-verificacion .ver-ia-motivo,
    #section-verificacion .ver-row-expand-existing,
    #section-verificacion .ver-row-expand textarea{ font-size:var(--ver-aviso) }

    /* ═══════════════════════════════════════════════════════════════════
       REGISTRAR DENTRO DE LA HOJA
       ═══════════════════════════════════════════════════════════════════
       El formulario de registro era un modal y ahora es una sección más.
       Lo que le daba forma (el ancho del modal, su cuerpo con scroll
       propio y el pie con los botones) lo pone aquí la hoja. El formulario
       en sí —campos, validaciones y guardado— no cambia ni una línea.
    ══════════════════════════════════════════════════════════════════════ */
    #section-registrar #cap-body{ max-width:920px }
    #section-registrar .cap-step-hint{ font-size:var(--fs-s); font-style:italic }

    /* El pie con Descartar / Guardar. Lo enciende el mismo código de
       siempre (style.display = '' cuando el paso 2 está en marcha), así
       que el display de aquí es el que manda cuando está encendido. */
    #section-registrar #cap-footer{
      display:flex; gap:var(--esp-2); justify-content:flex-end; align-items:center;
      max-width:920px;
      margin-top:var(--esp-5); padding-top:var(--esp-4);
      border-top:1px solid var(--linea);
    }

    /* La cabecera del paso 2 se separa un poco del título de la hoja. */
    #section-registrar .cap-back{ margin-top:var(--esp-1)}

    /* El total que manda el catálogo, junto a la lista de la venta. */
    #cap-total-esperado{ display:none }
    #cap-total-esperado.visible{
      display:block; font-size:var(--fs-m); color:var(--gris);
      border-top:1px solid var(--linea); padding-top:var(--esp-2); margin-top:var(--esp-1);
    }
    #cap-total-esperado b{ color:var(--oro); font-size:var(--fs-base)}

    /* Lo que falta para poder guardar. Va pegado al botón, que es donde
       mira el operador cuando no pasa nada al pulsarlo. */
    #cap-falta{ display:none; flex:1; min-width:200px; font-size:var(--fs-s); line-height:var(--lh-cuerpo)}
    #cap-falta.visible{ display:block }
    #cap-falta b{ color:var(--oro); font-weight:bold }
    #cap-falta ul{ margin:var(--esp-1) 0 0 var(--esp-4); padding:0; color:var(--gris) }
    #cap-falta li{ margin:var(--esp-1) 0}

    /* ═══════════════════════════════════════════════════════════════════
       LETRA CHICA QUE HAY QUE PODER LEER
       ═══════════════════════════════════════════════════════════════════
       Estas notas las lee alguien de pie, con prisa y a veces con la vista
       cansada, en una pantalla que no va a acercar con el navegador porque
       no sabe cómo. Así que ninguna baja de 14 px, y las que ya estaban por
       encima suben dos puntos igual. Donde la estética y la vista se pelean,
       gana la vista.
    ══════════════════════════════════════════════════════════════════════ */
    /* Registrar: la línea que explica cada tipo de transacción. */
    .cap-tipo-card .cap-tipo-sub{ font-size:var(--fs-m); line-height:var(--lh-cuerpo)}
    .cap-tipos-section-title{ font-size:var(--fs-s)}
    /* Con la nota más grande, alguna tarjeta necesita dos líneas. La
       rejilla estira todas las de la misma fila, así que crecen parejas;
       el mínimo sube para que las de una línea no queden apretadas. */
    .cap-tipo-card{ min-height:88px }
    .cap-tipos-grid{ align-items:stretch }

    /* Cuentas: la nota bajo el nombre de cada cuenta y sus rótulos. */
    .disp-persona-card-sub{ font-size:var(--fs-s)}
    .disp-persona-card-badge{ font-size:var(--fs-s)}
    .disp-persona-card-saldo small{ font-size:var(--fs-s)}
    .disp-group-label{ font-size:var(--fs-s)}
    .disp-hero-label{ font-size:var(--fs-m)}
    .disp-kpi-label{ font-size:var(--fs-s)}
    .disp-persona-card-add .add-label{ font-size:var(--fs-m)}
    .spl-hero-label{ font-size:var(--fs-s)}
    .spl-hero-meta{ font-size:var(--fs-m)}
    .spl-hero-warning{ font-size:var(--fs-s)}
    .spl-badge{ font-size:var(--fs-s)}
    .spl-mov-table thead th{ font-size:var(--fs-s)}

    /* Cierre: lo que sí tiene clase propia. */
    .peso-dia-box{ font-size:var(--fs-base)}
    .peso-dia-box .peso-dia-cifras{ font-size:var(--fs-m)}

    /* Que crecer no rompa nada: las notas envuelven en vez de cortarse. */
    .cap-tipo-sub, .disp-persona-card-sub, .spl-hero-meta, .spl-hero-warning{
      white-space:normal; overflow-wrap:anywhere;
    }

    /* ── Dos textos de Administración, dos puntos más grandes ──────────
       El índice de esta hoja es lo que más se pulsa del panel del admin, y
       el botón que dispara la revisión automática es el que se busca con
       prisa. Suben de 13 a 15 px y de 12 a 14. Nada más de esta pantalla
       cambia: el contador de la píldora (el "4"), el ícono de cada
       entrada, el aviso de la IA y las tarjetas de transacción se quedan
       exactamente como estaban.

       El ícono no crece a propósito: la píldora alinea al centro, así que
       un ícono de 13 junto a un texto de 15 sigue centrado, y el conjunto
       queda menos cargado que subiéndolo todo. */
    #subnav-admin .nav-item{ font-size:var(--fs-s)}
    #ver-ia-btn{ font-size:var(--fs-s)}

    /* ── Sobre oro macizo, tinta oscura ────────────────────────────────
       Los rellenos dorados de verdad llevan SIEMPRE --text-on-gold, que se
       da vuelta con el tema. Se listan juntos para que quede a la vista
       cuáles son y para que el próximo que se sume no lo olvide. */
    .btn:not(.linea):not(.btn-outline):not(.btn-danger),
    .btn-gold, .btn-primary, #btn-registrar-main,
    .nav-badge, .pestana .badge, .cap-substep-num,
    .disp-persona-card.selected .disp-persona-card-name,
    .disp-persona-card.selected .disp-persona-card-saldo{ color:var(--text-on-gold) }

    /* Servicio o producto con stock: dos opciones grandes, sin jerga. */
    .psub-tipos{ display:flex; flex-direction:column; gap:var(--esp-2)}
    .psub-tipo{
      display:flex; align-items:flex-start; gap:var(--esp-2); cursor:pointer;
      padding:var(--esp-2) var(--esp-3); border:1px solid var(--linea); border-radius:10px;
      transition:border-color .18s, background .18s;
    }
    .psub-tipo:hover{ border-color:var(--oro-dim); background:var(--glow-suave) }
    .psub-tipo input{ margin-top:var(--esp-1); accent-color:var(--oro); width:auto }
    .psub-tipo span{ display:flex; flex-direction:column; gap:var(--esp-1)}
    .psub-tipo b{ font-size:var(--fs-s); color:var(--texto) }
    .psub-tipo small{ font-size:var(--fs-xs); color:var(--gris); font-style:italic }
    .psub-tipo:has(input:checked){ border-color:var(--oro-med); background:var(--glow-suave) }

    /* La fila del producto agotado o por acabarse, en la lista de Precios. */
    .precios-subitem-row.stock-bajo{ border-left:3px solid var(--warning); padding-left:var(--esp-2)}
    .precios-subitem-row.stock-agotado{ border-left:3px solid var(--negative); padding-left:var(--esp-2)}
    /* Al llegar desde el aviso de Inicio, la fila parpadea un momento: la
       lista es larga y "te llevé a Precios" no sirve de nada si hay que
       volver a buscar con el dedo cuál era. */
    .precios-subitem-row.precios-row-senalada{
      animation: precios-senalar 2.4s ease-out;
      border-radius:6px;
    }
    @keyframes precios-senalar{
      0%,55%{ background: rgba(245,197,66,.28) }
      100%  { background: transparent }
    }

    /* ═══════════════════════════════════════════════════════════════════
       AYUDA EN UN "?" — la explicación deja de ocupar pantalla
       ═══════════════════════════════════════════════════════════════════
       Las explicaciones de cómo funciona algo llenaban la vista de letra
       gris que se lee una vez en la vida. Ahora viven detrás de un signo de
       interrogación en la esquina de aquello que explican: se abre con un
       clic (con el mouse y con el dedo, no sólo al pasar por encima) y se
       cierra al tocar fuera o con Esc.

       Lo que NO se esconde: los datos y los estados. Un monto, una fecha,
       un nombre o un aviso de lo que está pasando ahora se quedan a la
       vista. Detrás del "?" sólo va lo que explica cómo funciona algo.
    ══════════════════════════════════════════════════════════════════════ */
    .ayuda{
      flex:0 0 auto;
      width:20px; height:20px; padding:0;
      display:inline-flex; align-items:center; justify-content:center;
      border:none; border-radius:50%;
      background:var(--gris-2); color:var(--n0);
      font-family:var(--font-sans); font-size:var(--fs-xs); font-weight:bold; line-height:var(--lh-plano);
      cursor:pointer; opacity:.75;
      transition:opacity .15s, background .15s, transform .15s;
      vertical-align:middle;
    }
    .ayuda:hover, .ayuda[aria-expanded="true"]{ opacity:1; background:var(--oro); color:#141001; transform:scale(1.06) }
    [data-tema="claro"] .ayuda{ color:var(--text-on-gold) }
    [data-tema="claro"] .ayuda:hover, [data-tema="claro"] .ayuda[aria-expanded="true"]{ color:var(--text-on-gold) }
    .ayuda:focus-visible{ outline:2px solid var(--oro); outline-offset:2px }

    /* En una tarjeta o una sección, el "?" va arriba a la derecha. */
    .con-ayuda{ position:relative }
    .con-ayuda > .ayuda{ position:absolute; top:10px; right:10px; z-index:3 }

    /* El globo: uno solo para toda la app, se mueve a donde haga falta. */
    #ayuda-globo{
      position:fixed; z-index:var(--capa-ayuda); display:none;
      max-width:min(340px, 92vw);
      padding:var(--esp-3) var(--esp-3);
      background:var(--n2); color:var(--texto);
      border:1px solid var(--oro-dim); border-radius:12px;
      box-shadow:var(--sombra);
      font-family:var(--font-sans); font-size:var(--fs-s); line-height:var(--lh-cuerpo);
    }
    #ayuda-globo.abierto{ display:block; animation:ayuda-entra .16s ease-out }
    @keyframes ayuda-entra{ from{ opacity:0; transform:translateY(-4px) } to{ opacity:1; transform:none } }
    #ayuda-globo::before{
      content:''; position:absolute; width:10px; height:10px;
      background:var(--n2); border-left:1px solid var(--oro-dim); border-top:1px solid var(--oro-dim);
      transform:rotate(45deg);
      top:var(--flecha-y, -6px); left:var(--flecha-x, 20px);
    }
    #ayuda-globo.abajo::before{ transform:rotate(225deg) }

    /* En las tarjetas de Configuración el control vive a la derecha, así
       que la esquina se le reserva al "?" para que no se pisen. */
    .cfg-card.con-ayuda{ padding-right:var(--esp-6)}

    /* Rótulo corto + "?" para los formularios: el campo dice qué es y el
       signo guarda la explicación de qué pasa al usarlo. */
    .form-hint-ayuda{
      display:flex; align-items:center; gap:var(--esp-2);
      font-size:var(--fs-s); color:var(--gris); margin-bottom:var(--esp-2); font-style:italic;
    }


    /* ═══════════════════════════════════════════════════════════════════
       ACABADO — NÚMEROS CUADRADOS, FOCO Y APAGADO  (28-oct)
       ═══════════════════════════════════════════════════════════════════

       Tres reglas y ni un color, ni un tamaño de letra, ni un espaciado
       nuevos. Van al FINAL a propósito: aquí ganan por cascada a las casi
       6.000 líneas de arriba, que es lo que permite que valgan en las 19
       pantallas sin ir a tocarlas una por una.
    ══════════════════════════════════════════════════════════════════════ */

    /* ── 1. El dinero cuadra en vertical ──────────────────────────────
       Esta regla ya existía y estaba bien resuelta, pero vivía presa de
       una sola tabla (`.gf-tabla .num`, más arriba en este archivo). Las
       27 celdas de los informes la tenían; las otras 59 de la app pedían
       `class="num"` a un estilo que fuera de esa tabla no existía.

       Las tres partes hacen falta por separado:
         · a la derecha  — las comas caen en la misma vertical;
         · tabular-nums  — el 1 ocupa lo que el 8, así que las cifras no
                           bailan de una fila a la siguiente;
         · nowrap        — "Bs. 148.900,00" no se parte en dos renglones
                           cuando la ventana se estrecha. */
    .num, td.num, th.num{
      text-align: right;
      font-variant-numeric: tabular-nums;
      white-space: nowrap;
    }
    /* Ancho mínimo de la columna de importes. Al filtrar, la cifra más
       larga puede desaparecer: la columna se encogía y las comas saltaban
       de sitio entre una búsqueda y la siguiente. En pantalla estrecha se
       suelta, que ahí cada píxel de ancho vale más que esto. */
    td.num, th.num{ min-width: 8ch }
    @media (max-width: 640px){ td.num, th.num{ min-width: 0 } }

    /* ── 2. El foco de teclado: un anillo, y uno solo ─────────────────
       Había 10 `outline:none` contra 5 reglas de foco, así que quien
       navega con Tab perdía el rastro a media pantalla. Esos diez ya no
       están; el anillo se declara AQUÍ, una vez, y lo hereda todo lo que
       se puede enfocar: botones, campos, selects, enlaces, las pestañas
       de la libreta y las filas con tabindex.

       Va con :focus-visible y no con :focus porque el navegador lo
       enciende al llegar con el teclado y no al hacer clic con el ratón.
       Ésa es la diferencia entre una ayuda y un borde que estorba. */
    :focus-visible{
      outline: 2px solid var(--oro);
      outline-offset: 2px;
    }
    /* Dentro de algo que recorta —la ventanita del chat, la lista de la
       campana, una tabla con desplazamiento— el anillo por fuera se come
       el borde y se ve a medias: ahí va por dentro. */
    .chat-cuerpo :focus-visible,
    .campana-lista :focus-visible,
    .table-wrap :focus-visible{ outline-offset: -2px }

    /* ── 3. Un botón apagado se ve apagado ────────────────────────────
       `.btn:disabled` ya lo hacía bien. Lo que faltaba eran los veinte
       botones con apellido propio que no heredan de .btn —.pa-btn,
       .ct-btn, .cw-btn, .tema-btn, .lock-btn, .cap-back-btn…—: salían
       idénticos a uno vivo y se pulsaban sin que pasara nada. Los de
       aprobar y rechazar del lote de verificación son justo de ésos.

       Se declara sobre el ELEMENTO, no sobre la clase, así que alcanza a
       todos sin tocar ni uno: mismo color, mismo tamaño, mismo padding;
       sólo se apaga. La segunda regla anula el levantarse que cada botón
       trae por su cuenta en :hover. */
    button:disabled, button[disabled], .btn.is-disabled{
      opacity: .45;
      cursor: not-allowed;
      box-shadow: none;
      transform: none;
    }
    button:disabled:hover, button[disabled]:hover, .btn.is-disabled:hover{
      transform: none;
      box-shadow: none;
    }

    /* ═══════════════════════════════════════════════════════════════════
       DATOS DE DESTINO — el interruptor y el formulario (Rich, 5-nov-2026)

       Se ve igual en el escritorio del admin y en el teléfono: el
       interruptor es un BOTÓN grande con su explicación debajo, no un
       conmutador pequeño. En 390 px de ancho un conmutador de 40 px al
       lado de un texto largo es justo lo que no se acierta con el pulgar,
       y aquí un toque de más abre la edición de datos de dinero.
    ══════════════════════════════════════════════════════════════════════ */
    .destino-inter{
      border: 1px solid var(--border);
      border-radius: 10px;
      padding: var(--esp-3);
      margin: var(--esp-1) 0 var(--esp-4);
      background: var(--bg-elevated);
    }
    /* Encendido se ve verde, con los mismos tonos que el resto del sistema
       (los de .cap-discrep.ok): así cambia solo con el tema claro. */
    .destino-inter.encendido{
      border-color: rgba(90, 181, 116, 0.35);
      background: rgba(90, 181, 116, 0.12);
    }
    .destino-inter.encendido .destino-inter-titulo{ color: var(--positive) }
    .destino-inter-titulo{
      font-weight: 600;
      font-size: var(--fs-s);
      margin-bottom: var(--esp-1);
    }
    .destino-inter-nota{
      font-size: var(--fs-xs);
      line-height: var(--lh-cuerpo);
      color: var(--text-muted);
      margin-top: var(--esp-2);
    }
    .destino-inter .btn{
      width: 100%;
      min-height: 48px;
      font-size: var(--fs-m);
    }
    .destino-datos{
      font-size: var(--fs-s);
      line-height: var(--lh-cuerpo);
      margin: 0 0 var(--esp-3);
    }
    .destino-datos b{ font-weight: 600 }
    .destino-ref{
      font-size: var(--fs-xs);
      color: var(--text-muted);
      margin-top: var(--esp-1);
    }
    .destino-grid{
      display: grid;
      grid-template-columns: repeat(2, minmax(0, 1fr));
      gap: var(--esp-2);
    }
    .destino-grid .full{ grid-column: 1 / -1 }
    @media (max-width: 560px){
      .destino-grid{ grid-template-columns: 1fr }
      .destino-inter{ padding: var(--esp-2)}
    }

    /* ══════════════════════════════════════════════════════════════════════
       LAS CLASES TRANSVERSALES  ·  tanda 1

       Esto reemplaza 696 atributos style="..." que vivían dentro del
       JavaScript y del index.html. No cambia el aspecto de nada: cada clase
       dice exactamente lo que decía el style= que sustituye, con el valor ya
       en la escala cerrada en b1be0e7.

       POR QUÉ ESTÁN AL FINAL DEL ARCHIVO
       Un style= en línea le ganaba a cualquier regla de aquí, pasara lo que
       pasara. Una clase no: compite. Puestas al final, ganan por orden a
       cualquier regla de la misma especificidad escrita más arriba, que es
       lo que necesitan para hacer el mismo trabajo. Donde una regla con más
       peso (un #id, dos clases encadenadas) les gane, se le sube la
       especificidad a ESA clase y se anota — nunca !important.

       POR QUÉ "u-"
       Utilidad. Hay 686 clases en este archivo y ninguna empieza por u- ni
       por c-, así que no hay forma de chocar con .btn, .card, .cap-*, .pm-*
       ni con ninguna otra.

       TODO EN TOKENS, NUNCA EN VALORES FIJOS
       Hay 26 parches de tema claro en este archivo. Una clase que escriba
       un color a pelo se queda fuera de ellos y el tema claro la pierde.
       Con var(--text-muted) el tema la sigue solo.
       ══════════════════════════════════════════════════════════════════════ */

    /* ── El color del texto ──────────────────────────────────────────── */
    .u-gris       { color: var(--text-muted) }
    .u-gris-sec   { color: var(--text-secondary) }
    .u-gris-tenue { color: var(--text-dim) }
    .u-texto      { color: var(--text-primary) }

    /* ── El tamaño ───────────────────────────────────────────────────── */
    .u-t-xs   { font-size: var(--fs-xs) }      /* 12 */
    .u-t-s    { font-size: var(--fs-s) }       /* 14 */
    .u-t-m    { font-size: var(--fs-m) }       /* 16 */
    .u-t-base { font-size: var(--fs-base) }    /* 18, el del cuerpo */

    /* ── El peso ─────────────────────────────────────────────────────── */
    .u-fuerte { font-weight: 700 }
    .u-normal { font-weight: 400 }

    /* ── Separación por arriba ───────────────────────────────────────── */
    .u-mt-0 { margin-top: 0 }
    .u-mt-1 { margin-top: var(--esp-1) }       /* 4 */
    .u-mt-2 { margin-top: var(--esp-2) }       /* 8 */
    .u-mt-3 { margin-top: var(--esp-3) }       /* 12 */
    .u-mt-4 { margin-top: var(--esp-4) }       /* 16 */

    /* ── Separación por abajo ────────────────────────────────────────── */
    .u-mb-1 { margin-bottom: var(--esp-1) }
    .u-mb-2 { margin-bottom: var(--esp-2) }
    .u-mb-3 { margin-bottom: var(--esp-3) }
    .u-mb-4 { margin-bottom: var(--esp-4) }

    /* ── Relleno ─────────────────────────────────────────────────────── */
    .u-p-0    { padding: 0 }
    .u-p-3    { padding: var(--esp-3) }
    .u-p-4    { padding: var(--esp-4) }
    .u-p-fila { padding: var(--esp-3) var(--esp-4) }

    /* ── Sueltas ─────────────────────────────────────────────────────── */
    .u-lh-cuerpo { line-height: var(--lh-cuerpo) }

    /* 48px es el alto mínimo para que un dedo acierte en la tablet. No es
       un número de diseño: es ergonomía, y por eso no se redondea. */
    .u-btn-tactil { min-height: 48px }

    /* ── La más repetida de todas: la nota gris bajo un título ───────── */
    .u-nota {
      color: var(--text-muted);
      font-size: var(--fs-s);
      margin-top: var(--esp-1);
    }

    /* ── Tanda 2: el bloque compartido ────────────────────────── */
    .u-al-final       { justify-content: flex-end }
    .u-flex-1         { flex: 1 }
    .u-gap-2          { gap: var(--esp-2) }
    .u-gap-3          { gap: var(--esp-3) }
    .u-gris-crudo     { color: var(--gris) }   /* el gris del tema, que NO es el mismo que --text-muted en claro */
    .u-m-0            { margin: 0 }
    .u-m-sub          { margin: var(--esp-1) 0 var(--esp-3) }
    .u-p-chip         { padding: var(--esp-2) var(--esp-3) }
    .u-track-ancho    { letter-spacing: var(--track-ancho) }

    /* Estas tres necesitan un punto más de peso, y aquí está el porqué:
       el style= en línea que sustituyen le ganaba SIEMPRE a cualquier regla.
       Una clase suelta no: `.modal .btn-row` (gap 12), `.modal .form-grid`
       (gap 16) y `.ver-row-btn.danger` (padding 4/8) llevan dos selectores y
       se las comían. Repetir la clase en el selector da exactamente el mismo
       peso que ellas, y por ir después, gana. No hace falta ningún !important.
       Comprobado en el navegador: sin esto, seis filas de botones de modal
       pasaban de 8px de hueco a 12. */
    .u-gap-2.u-gap-2   { gap: var(--esp-2) }
    .u-gap-3.u-gap-3   { gap: var(--esp-3) }
    .u-p-chip.u-p-chip { padding: var(--esp-2) var(--esp-3) }

    /* ── Tanda 3: registrar ───────────────────────────────────── */
    .u-cursiva        { font-style: italic }
    .u-flex-1         { flex: 1 }
    .u-m-0            { margin: 0 }
    .u-m-titulo       { margin: var(--esp-3) 0 var(--esp-1) }   /* título de bloque dentro de una tarjeta */
    .u-mayus          { text-transform: uppercase }
    .u-ml-2           { margin-left: var(--esp-2) }
    .u-semi           { font-weight: 600 }
    .u-track-sutil    { letter-spacing: var(--track-sutil) }

    /* ── Tanda 4: inicio / el muro ────────────────────────────── */
    .u-borde-abajo    { border-bottom: 1px solid var(--border-soft) }
    .u-clic           { cursor: pointer }
    .u-cursiva        { font-style: italic }
    .u-gris-2         { color: var(--gris-2) }
    .u-gris-crudo     { color: var(--gris) }   /* el gris del tema, que NO es el mismo que --text-muted en claro */
    .u-m-sub          { margin: var(--esp-1) 0 var(--esp-3) }
    .u-ml-auto        { margin-left: auto }
    .u-p-ancho        { padding: var(--esp-4) var(--esp-5) }
    .u-p-chip         { padding: var(--esp-2) var(--esp-3) }
    .u-p-v2           { padding: var(--esp-2) 0 }
    .u-parte-palabras { overflow-wrap: anywhere }   /* para que una referencia larga no desborde */
    .u-semi           { font-weight: 600 }
    .u-t-2xl          { font-size: var(--fs-2xl) }   /* el 36: cifras grandes */
    .u-t-l            { font-size: var(--fs-l) }   /* el 22 de la escala */

    /* ── Tanda 5: configuración ───────────────────────────────── */
    .u-borde-abajo    { border-bottom: 1px solid var(--border-soft) }
    .u-borde-arriba   { border-top: 1px solid var(--border-soft) }
    .u-clic           { cursor: pointer }
    .u-flex-1         { flex: 1 }
    .u-m-0            { margin: 0 }
    .u-m-b2           { margin: 0 0 var(--esp-2) }
    .u-m-sub          { margin: var(--esp-1) 0 var(--esp-3) }
    .u-ml-1           { margin-left: var(--esp-1) }
    .u-ml-auto        { margin-left: auto }
    .u-mono           { font-family: ui-monospace, monospace }   /* referencias y claves: que los dígitos midan igual */
    .u-p-v2           { padding: var(--esp-2) 0 }
    .u-semi           { font-weight: 600 }
    .u-t-2xl          { font-size: var(--fs-2xl) }   /* el 36: cifras grandes */

    /* ── Tanda 6: cierre ──────────────────────────────────────── */
    .u-m-0            { margin: 0 }
    .u-m-sub          { margin: var(--esp-1) 0 var(--esp-3) }
    .u-m-titulo       { margin: var(--esp-3) 0 var(--esp-1) }   /* título de bloque dentro de una tarjeta */
    .u-mayus          { text-transform: uppercase }
    .u-ml-2           { margin-left: var(--esp-2) }
    .u-scroll-x       { overflow-x: auto }
    .u-semi           { font-weight: 600 }
    .u-sin-cortar     { white-space: nowrap }
    .u-track-sutil    { letter-spacing: var(--track-sutil) }

    /* ── Tanda 7: clientes de internet ────────────────────────── */
    .u-ml-2           { margin-left: var(--esp-2) }
    .u-scroll-x       { overflow-x: auto }

    /* ── Tanda 8: las ligeras ─────────────────────────────────── */
    .u-borde-abajo    { border-bottom: 1px solid var(--border-soft) }
    .u-clic           { cursor: pointer }
    .u-flex-1         { flex: 1 }
    .u-m-0            { margin: 0 }
    .u-mayus          { text-transform: uppercase }
    .u-ml-1           { margin-left: var(--esp-1) }
    .u-ml-2           { margin-left: var(--esp-2) }
    .u-ml-auto        { margin-left: auto }
    .u-p-chip         { padding: var(--esp-2) var(--esp-3) }
    .u-p-v2           { padding: var(--esp-2) 0 }
    .u-parte-palabras { overflow-wrap: anywhere }   /* para que una referencia larga no desborde */
    .u-semi           { font-weight: 600 }
    .u-sin-cortar     { white-space: nowrap }
    .u-t-l            { font-size: var(--fs-l) }   /* el 22 de la escala */
    .u-track-sutil    { letter-spacing: var(--track-sutil) }

    /* ══════════════════════════════════════════════════════════════════
       EL VISOR DEL COMPROBANTE QUE ESPERA OK
       ══════════════════════════════════════════════════════════════════

       Lo abre el botón "Ver" del cartel rojo de arriba (ver EL VISOR DEL
       COMPROBANTE QUE ESPERA OK en public/js/app.js). No es una sección a
       propósito: el que da el OK es el mostrador, y la sección donde vivía
       el cartel con las filas es sólo del admin.

       La foto manda. Ocupa el ancho entero y hasta media pantalla de alto,
       entera y sin recortar: lo que se está haciendo aquí es LEER un
       comprobante, no reconocer una miniatura. */
    .vok-modal {
      max-width: min(640px, 94vw);
      max-height: 92vh;
      display: flex;
      flex-direction: column;
    }
    .vok-modal .modal-body {
      overflow-y: auto;
      -webkit-overflow-scrolling: touch;
    }
    .vok-foto {
      display: block;
      width: 100%;
      max-height: 50vh;
      object-fit: contain;
      border: 1px solid var(--border-soft);
      border-radius: var(--radius);
      background: var(--bg-elevated);
    }
    /* Varios esperando: una raya entre ellos y aire, para que no se lean
       como una sola ficha larga. */
    .vok-ficha + .vok-ficha {
      margin-top: var(--esp-5);
      padding-top: var(--esp-5);
      border-top: 1px solid var(--border-soft);
    }

    /* ── La alerta a la que llegó una notificación ─────────────────────
       Al tocar la notificación del teléfono, el sistema abre Inicio y trae
       ESA alerta a la vista. Un resalte corto para que se vea cuál es: con
       cinco avisos parecidos, llegar y no saber cuál sonó es llegar a
       ninguna parte. Se quita solo a los 4 segundos. */
    .wh-senalada {
      box-shadow: 0 0 0 3px rgba(27, 77, 143, .35);
      transition: box-shadow .25s;
    }
    @media (prefers-reduced-motion: reduce) {
      .wh-senalada { transition: none }
    }
