/* Los tokens de diseño (colores, tipografía, espaciado, radios) viven en tokens.css */

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

body {
  font-family:'Inter',Arial,sans-serif;
  color:var(--text);
  background:var(--bg)
}

.appbar {
  background:var(--navy);
  color:var(--surface);
  padding:14px 22px;
  display:flex;
  align-items:center;
  justify-content:space-between
}

.appbar h1 {
  font-family:'Inter';
  font-size:var(--fs-lg);
  font-weight:600;
  letter-spacing:.3px
}

.appbar .brand {
  display:flex;
  align-items:center;
  gap:12px
}

.appbar .dot {
  font-size:var(--fs-base);
  color:var(--c-gray-300)
}

.app {
  display:grid;
  grid-template-columns:430px 1fr;
  gap:22px;
  max-width:1280px;
  margin:22px auto;
  padding:0 22px;
  align-items:start
}

.pane {
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:12px;
  padding:20px
}

.pane h2 {
  font-family:'Inter';
  font-size:var(--fs-base);
  text-transform:uppercase;
  letter-spacing:1.5px;
  color:var(--gray);
  margin-bottom:14px
}

label {
  display:block;
  font-size:var(--fs-sm);
  color:var(--gray);
  margin:10px 0 4px
}

input,select,textarea {
  width:100%;
  padding:9px 11px;
  border:1px solid var(--line);
  border-radius:7px;
  font:inherit;
  font-size:var(--fs-md);
  color:var(--text);
  background:var(--surface)
}

/* ── El foco, y la coma que faltaba (revisión de UI, 3-ago) ─────────────────────────────────
   Esta regla decía `input:focus,select,textarea:focus`. Sin el `:focus` del `select`, el segundo
   selector aplicaba a TODOS los desplegables siempre: se veían permanentemente enfocados, así que
   el foco dejaba de significar nada. Y el `outline:none` quitaba el anillo del navegador sin poner
   nada en su lugar, lo que hacía casi imposible navegar con Tab.
   El anillo va en `:focus-visible`, que el navegador solo activa con teclado: quien usa el ratón
   no ve ningún borde extra, y quien usa Tab siempre sabe dónde está. */
input:focus,select:focus,textarea:focus {
  outline:none;
  border-color:var(--navy)
}
input:focus-visible,select:focus-visible,textarea:focus-visible,
button:focus-visible,a:focus-visible,[tabindex]:focus-visible {
  outline:2px solid var(--navy);
  outline-offset:2px
}

.two {
  display:grid;
  grid-template-columns:1fr 1fr;
  gap:12px
}

.three {
  display:grid;
  grid-template-columns:1fr 1fr 1fr;
  gap:12px
}

table.form {
  width:100%;
  border-collapse:collapse;
  margin-top:6px
}

table.form th {
  font-size:var(--fs-xs);
  color:var(--gray);
  text-transform:uppercase;
  letter-spacing:.5px;
  text-align:left;
  padding:4px 5px;
  font-weight:600
}

table.form td {
  padding:5px 4px
}

table.form input,table.form select {
  padding:8px 9px;
  font-size:var(--fs-md)
}

/* 12-sep · `.del` DECIA DOS COSAS PEGADAS y una de las dos no era suya: «esto destruye» (el rojo)
   y «esto mide 30 px». El ancho fijo se escribio para el aspa × de la cotizacion, que necesita
   11.9 px y le sobran 18 — y es letal en cuanto el boton lleva TEXTO: `.mini` le pone
   `padding:8px 12px`, asi que de los 30 px quedan 4 para la palabra. Medido en Chrome real:
   «Borrar» mide 36.1 px y le faltan 32.1; «Rechazar» mide 55.6 y le faltan 51.6; y a 2400 px de
   ventana SIGUE cortado, o sea que ensanchar la columna nunca fue el arreglo. Mordia en TRES
   sitios (Solicitudes de acceso, la ficha de la contraparte y la ficha de la operacion).
   Ahora el ancho vive en `.del.aspa`, que es el modificador del boton de SOLO ICONO, y quien
   manana escriba `class="mini del">Texto` no puede reintroducirlo. */
.del {
  background:var(--surface);
  border:1px solid var(--line);
  color:var(--danger);
  border-radius:6px;
  cursor:pointer;
  font-size:var(--fs-lg);
  line-height:1
}

.del.aspa {
  width:30px;
  height:32px;
  padding:0
}

.del:hover {
  background:var(--danger-bg)
}

.btn {
  display:inline-block;
  background:var(--navy);
  color:var(--surface);
  border:2px solid var(--navy);
  padding:10px 18px;
  border-radius:8px;
  font-weight:600;
  font-size:var(--fs-md);
  cursor:pointer;
  transition:.15s
}

.btn:hover {
  background:var(--navy-hover)
}

.btn.ghost {
  background:var(--surface);
  color:var(--navy)
}

.btn.ghost:hover {
  background:var(--light)
}

.btn.add {
  background:var(--light);
  color:var(--navy);
  border-color:var(--line);
  width:100%;
  margin-top:8px
}

.toolbar {
  display:flex;
  gap:10px;
  flex-wrap:wrap;
  margin-top:16px
}

.hint {
  font-size:var(--fs-sm);
  color:var(--gray);
  margin-top:10px;
  line-height:1.5
}

input[type=file] {
  padding:8px;
  font-size:var(--fs-base);
  background:var(--light);
  cursor:pointer
}

.csfmsg {
  margin-top:10px;
  padding:10px 12px;
  border-radius:8px;
  font-size:var(--fs-base);
  line-height:1.5
}

.csfmsg.load {
  background:var(--light);
  color:var(--gray)
}

.csfmsg.ok {
  background:var(--ok-bg);
  color:var(--ok);
  border:1px solid var(--ok-border)
}

.csfmsg.warn {
  background:var(--warn-bg);
  color:var(--warn);
  border:1px solid var(--warn-border)
}

/* documento */
.preview {
  overflow:auto
}

#doc {
  background:var(--surface);
  color:var(--text);
  width:820px;
  max-width:100%;
  margin:0 auto;
  padding:46px 50px;
  border:1px solid var(--line);
  border-radius:6px;
  box-shadow:0 2px 14px rgba(0,0,0,.06);
  font-size:var(--fs-md);
  line-height:1.5
}

#doc h1 {
  font-family:'Playfair Display',serif;
  color:var(--navy);
  font-size:30px; /* documento: escala propia, no UI */
  letter-spacing:2px
}

.d-head {
  display:flex;
  justify-content:space-between;
  align-items:flex-start;
  border-bottom:2px solid var(--navy);
  padding-bottom:18px;
  margin-bottom:22px
}

.d-head img {
  height:82px
}

.d-title {
  text-align:right
}

.d-meta {
  font-size:var(--fs-xs);
  color:var(--gray);
  margin-top:6px;
  line-height:1.6
}

.d-parties {
  display:grid;
  grid-template-columns:1fr 1fr;
  gap:30px;
  margin-bottom:24px
}

.d-parties h4 {
  font-size:var(--fs-xs);
  text-transform:uppercase;
  letter-spacing:1px;
  color:var(--navy);
  margin-bottom:6px
}

.d-parties .blk {
  font-size:var(--fs-xs);
  color:var(--text);
  line-height:1.55
}

table.d-table {
  width:100%;
  border-collapse:collapse;
  margin-bottom:18px
}

table.d-table th {
  background:var(--navy);
  color:var(--surface);
  font-size:var(--fs-sm);
  text-transform:uppercase;
  letter-spacing:.5px;
  padding:9px 10px;
  text-align:left;
  font-weight:600
}

table.d-table td {
  padding:9px 10px;
  border-bottom:1px solid var(--line);
  font-size:var(--fs-md)
}

table.d-table th.c,table.d-table td.c {
  text-align:center
}

table.d-table th.r,table.d-table td.r {
  text-align:right
}

.d-totals {
  display:flex;
  flex-direction:column;
  align-items:flex-end;
  gap:12px;
  margin-bottom:14px
}

.d-multinote {
  font-size:var(--fs-xs);
  color:var(--gray);
  font-style:italic;
  text-align:right;
  margin:0 0 18px;
  line-height:1.5;
  max-width:420px;
  margin-left:auto
}

.totbox {
  width:22rem;
  max-width:100%
}

.totbox>div {
  display:flex;
  justify-content:space-between;
  gap:var(--sp-4);
  padding:6px 0;
  font-size:var(--fs-md);
  color:var(--text)
}

.totbox>div span {
  color:var(--gray)
}

.totbox .grand {
  border-top:2px solid var(--navy);
  margin-top:4px;
  padding-top:8px;
  font-size:16px /* documento: escala propia, no UI */
}

.totbox .grand span,.totbox .grand b {
  color:var(--navy);
  font-weight:700
}

.totbox b {
  white-space:nowrap;
  text-align:right
}

.d-enletras {
  margin:0 0 20px;
  font-size:var(--fs-xs);
  color:var(--navy);
  letter-spacing:.3px;
  line-height:1.7;
  font-weight:600
}

.d-notes {
  border-top:1px solid var(--line);
  padding-top:14px;
  margin-top:6px
}

.d-notes h4 {
  font-size:var(--fs-xs);
  text-transform:uppercase;
  letter-spacing:1px;
  color:var(--navy);
  margin-bottom:6px
}

.d-notes .txt {
  font-size:var(--fs-sm);
  color:var(--gray);
  line-height:1.6;
  white-space:pre-line
}

.d-foot {
  margin-top:26px;
  text-align:center;
  font-size:var(--fs-sm);
  color:var(--gray);
  border-top:1px solid var(--line);
  padding-top:14px;
  font-style:italic
}

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


}

@media print {
  @page {
  size:letter;
  margin:0
}

body {
  background:var(--surface)!important
}

/* GENERAL, no una lista (regla O): la lista vieja enumeraba dos vistas y las pantallas
   nacidas después quedaron fuera — imprimir desde Operaciones sacaba el documento Y la
   pantalla entera debajo (J, 14-ago). Toda vista que no sea la del documento desaparece. */
.sidebar,.form-pane,#login,#toast,
div[id^="view-"]:not(#view-cotizador) {
  display:none!important
}

#app,#view-cotizador {
  display:block!important;
  margin:0!important;
  padding:0!important
}

.app {
  display:block!important;
  margin:0!important;
  padding:0!important;
  max-width:none!important;
  gap:0!important
}

.pane.preview {
  display:block!important;
  border:none!important;
  box-shadow:none!important;
  padding:0!important;
  margin:0!important;
  background:none!important;
  max-width:none!important
}

.pane.preview .preview {
  overflow:visible!important
}

#doc {
  box-shadow:none!important;
  border:none!important;
  border-radius:0!important;
  width:100%!important;
  max-width:none!important;
  padding:14mm!important;
  margin:0!important
}

.d-table tr, .d-table td, .d-totals, .totbox { break-inside:avoid!important }


}

.loginwrap {
  min-height:100vh;
  display:flex;
  align-items:center;
  justify-content:center;
  background:var(--navy)
}

.loginbox {
  background:var(--surface);
  border-radius:14px;
  padding:38px 34px;
  width:340px;
  text-align:center;
  box-shadow:0 10px 40px rgba(0,0,0,.25)
}

.loginbox img {
  height:64px;
  margin-bottom:16px
}

.loginbox h1 {
  font-family:'Inter';
  font-size:var(--fs-lg);
  font-weight:600;
  color:var(--navy);
  margin-bottom:4px
}

.loginbox .lsub {
  font-size:var(--fs-base);
  color:var(--gray);
  margin-bottom:18px
}

.loginbox input {
  width:100%;
  padding:11px 13px;
  border:1px solid var(--line);
  border-radius:8px;
  margin-bottom:12px;
  font:inherit;
  font-size:var(--fs-md)
}

.loginbox .btn {
  width:100%
}

.lmsg {
  font-size:var(--fs-base);
  color:var(--danger);
  margin-top:12px;
  min-height:16px
}

.topbar {
  position:sticky;
  top:0;
  z-index:30;
  background:var(--navy);
  color:var(--surface);
  display:flex;
  align-items:center;
  gap:16px;
  padding:10px 22px
}

.tb-logo {
  height:32px
}

.tabs {
  display:flex;
  gap:6px;
  margin-left:8px
}

.tab {
  background:transparent;
  border:none;
  color:var(--c-gray-300);
  font:inherit;
  font-size:var(--fs-md);
  font-weight:500;
  padding:8px 14px;
  border-radius:8px;
  cursor:pointer
}

.tab.on {
  background:rgba(255,255,255,.12);
  color:var(--surface)
}

.tb-right {
  margin-left:auto;
  display:flex;
  align-items:center;
  gap:12px
}

.userlabel {
  font-size:var(--fs-base);
  color:var(--c-gray-300)
}

.topbar .btn {
  background:var(--surface);
  color:var(--navy);
  border-color:var(--surface)
}

.topbar .btn:hover {
  background:var(--bg)
}

.topbar .btn.ghost {
  background:transparent;
  color:var(--surface);
  border-color:rgba(255,255,255,.45)
}

.topbar .btn.ghost:hover {
  background:rgba(255,255,255,.12)
}

.btn.sm {
  padding:8px 14px;
  font-size:var(--fs-base)
}

.histwrap {
  max-width:min(1480px, 94vw);
  margin:26px auto;
  padding:0 28px
}

.histwrap h2 {
  font-family:'Playfair Display',serif;
  color:var(--navy);
  margin-bottom:14px
}

#histsearch {
  width:100%;
  padding:11px 13px;
  border:1px solid var(--line);
  border-radius:8px;
  margin-bottom:16px;
  font:inherit;
  font-size:var(--fs-md)
}

.hrow {
  display:flex;
  align-items:center;
  gap:16px;
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:10px;
  padding:14px 16px;
  margin-bottom:10px
}

.hinfo {
  flex:1 1 auto;
  min-width:0;
  font-size:var(--fs-md);
  color:var(--text);
  word-break:break-word
}

.muted {
  color:var(--gray);
  font-size:var(--fs-base)
}

.hact {
  flex:0 0 auto;
  display:flex;
  gap:8px;
  flex-wrap:wrap;
  align-items:center
}

.mini {
  text-align:center;
  background:var(--surface);
  border:1px solid var(--line);
  color:var(--navy);
  border-radius:7px;
  padding:8px 12px;
  font-size:var(--fs-base);
  font-weight:500;
  cursor:pointer;
  transition:.15s;
  white-space:nowrap
}

.mini:hover {
  background:var(--light);
  border-color:var(--gray)
}

.mini.primary {
  background:var(--navy);
  color:var(--surface);
  border-color:var(--navy)
}

.mini.primary:hover {
  background:var(--navy-hover)
}

.mini.del {
  background:var(--danger);
  color:var(--surface);
  border-color:var(--danger)
}

.mini.del:hover {
  background:var(--danger);
  border-color:var(--danger)
}

@media(max-width:640px) {
  .hrow {
  flex-wrap:wrap
}

.hact {
  width:100%;
  justify-content:flex-end
}


}

/* ── Avisos (revisión de UI, 3-ago) ────────────────────────────────────────────────────────
   Antes esto era UN solo recuadro azul marino que se sobrescribía a sí mismo. Cuando una acción
   tenía algo que contar y además terminaba bien, el segundo aviso borraba el primero antes de que
   se alcanzara a leer — y el que se perdía era siempre el importante. Ahora es una COLA, apilada
   abajo a la derecha, donde el color dice de qué se trata y el tipo decide cuánto vive. */
/* AUD2·9 · la esquina es la IZQUIERDA desde el 21-sep. El cajón ⚡ Clasificar vive fijo a la
   derecha con «Guardar y siguiente ⏎» en su pie, y los botones de los modales también: el aviso
   caía ENCIMA y el botón dejaba de responder (AUD2-02 — `elementFromPoint` devolvía el aviso).
   Se mueve el aviso, que es lo que sobra en esa esquina, y no el cajón, que es donde se trabaja. */
.toast {
  position:fixed;
  bottom:20px;
  left:20px;
  right:auto;
  transform:none;
  z-index:100;
  display:flex;
  flex-direction:column;
  gap:8px;
  width:min(400px,calc(100vw - 40px));
  pointer-events:none;
  background:none;
  padding:0
}
.aviso {
  pointer-events:auto;
  display:flex;
  gap:10px;
  align-items:flex-start;
  border:1px solid;
  border-radius:var(--r-md);
  padding:10px 12px;
  font-size:var(--fs-base);
  line-height:1.45;
  background:var(--surface);
  box-shadow:0 6px 22px rgba(28,49,73,.16);
  animation:avisoEntra .18s ease-out
}
@keyframes avisoEntra { from{opacity:0;transform:translateY(8px)} to{opacity:1;transform:none} }
.aviso .ico { flex:none; font-weight:700; line-height:1.35 }
.aviso .txt { flex:1; min-width:0; overflow-wrap:anywhere }
.aviso .x {
  flex:none; background:none; border:none; cursor:pointer; color:inherit;
  opacity:.5; font-size:15px; line-height:1; padding:2px 4px; width:auto
}
.aviso .x:hover { opacity:1 }
.aviso.ok   { background:var(--ok-bg);     border-color:var(--ok-border);     color:var(--ok) }
.aviso.warn { background:var(--warn-bg);   border-color:var(--warn-border);   color:var(--warn) }
.aviso.err  { background:var(--danger-bg); border-color:var(--danger-border); color:var(--danger) }
/* MORDIDA #7 (15-ago) · un aviso repetido no se apila: el visible se re-resalta y cuenta ×N.
   currentColor: el contador hereda el color del tipo (rojo en err, ambar en warn). */
.aviso .veces { font-size:var(--fs-xs); font-weight:700; background:currentColor; color:var(--surface); border-radius:20px; padding:0 var(--sp-2); margin-left:var(--sp-2) }
.aviso.resalta { animation:avisoEntra .3s ease-out }
/* ── LA BARRA DE VIDA (AUD2·9) ───────────────────────────────────────────────────────────────
   No es un adorno que acompañe a un `setTimeout`: ES el reloj. El aviso se quita en el
   `animationend` de esta barra, así que `:hover` pausándola pausa TAMBIÉN el cierre — un solo
   mecanismo, y no dos que haya que mantener de acuerdo. La duración la pone el JS por elemento
   (`style.animationDuration`), porque depende del tipo y del `ms` que pase el llamador.
   ⚠ Su modo de falla es el bueno: si la animación no corre —CSS que no cargó, motor que la
     ignora— el `animationend` no llega y el aviso se queda hasta la ✕, que es exactamente lo
     que hacía hasta hoy. Falla hacia quedarse, nunca hacia desaparecer. */
.barra-vida { height:3px; background:currentColor; opacity:.35; border-radius:2px;
  margin-top:var(--sp-1); width:100%; animation:avisoVida linear forwards }
.aviso:hover .barra-vida { animation-play-state:paused }
@keyframes avisoVida { from{width:100%} to{width:0} }
/* ── UN AVISO EN FLUJO NO ES UN TOAST (2-sep) ───────────────────────────────────────────────
   `.aviso` nació para la COLA de `.toast`, donde la separación entre recuadros la pone el
   `gap:8px` del contenedor — por eso la regla base no declara margen. Hoy se usa 36 veces
   DENTRO de un panel, en flujo normal, y ahí no hay gap que lo separe de lo que sigue.
   MEDIDO en Chrome sobre el previo `revisado` (viewport 1424, animación en reposo): la franja
   «✅ Revisado. Cierre final.» termina en 961.8 y el párrafo «Lo revisó:» empieza en 959.8 —
   se encima 2px, y encima le cae la sombra de 6px que el recuadro proyecta hacia abajo.
   El aire se pone AQUÍ, una vez, y no en el llamador: el llamador 37 nacería pegado otra vez
   (principio 1). La cola conserva su cero, donde el `gap` ya lo hace y un margen se sumaría. */
.aviso { margin-bottom:var(--e3) }
.toast .aviso { margin-bottom:0 }

/* ── Un campo de dinero que se quedó vacío (revisión de UI, 3-ago) ─────────────────────────── */
.campo-mal { border-color:var(--danger); background:var(--danger-bg) }

/* ── Confirmación de algo que no se puede deshacer ─────────────────────────────────────────── */
.cnf-ov {
  position:fixed; inset:0; background:rgba(28,49,73,.45); z-index:120;
  display:flex; align-items:center; justify-content:center; padding:20px
}
.cnf-card {
  background:var(--surface); border-radius:var(--r-lg); max-width:480px; width:100%;
  padding:22px; box-shadow:0 18px 50px rgba(28,49,73,.28); max-height:90vh; overflow:auto
}
.cnf-card h3 { font-size:var(--fs-lg); margin-bottom:2px }
.cnf-sub { font-size:var(--fs-base); color:var(--gray); margin-bottom:14px }
.cnf-lleva {
  border:1px solid var(--danger-border); background:var(--danger-bg);
  border-radius:var(--r-md); padding:11px 13px; font-size:var(--fs-base); margin-bottom:14px
}
.cnf-lleva ul { margin:6px 0 0 18px }
.cnf-lleva li { margin:2px 0 }
.cnf-suelto { margin-top:8px; padding-top:8px; border-top:1px solid var(--danger-border) }
.cnf-irrev { font-size:var(--fs-sm); color:var(--danger); font-weight:600; margin-bottom:12px }
.cnf-in { margin-bottom:16px }
.cnf-acts { display:flex; gap:8px; justify-content:flex-end }

.dashstats {
  display:flex;
  gap:12px;
  flex-wrap:wrap;
  margin-bottom:18px
}

.dashstats.fixed .statcard{flex:0 1 210px;padding:8px 14px}
.dashstats.fixed .statcard .n{white-space:nowrap;font-size:var(--fs-lg);min-height:0;margin-bottom:1px}
.dashstats.fixed .statcard .l{white-space:nowrap;letter-spacing:.3px}
.statcard {
  flex:1 1 130px;
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:10px;
  padding:14px 16px
}

.statcard .n {
  font-family:'Inter',system-ui,sans-serif;
  font-size:var(--fs-xl);
  font-weight:600;
  font-variant-numeric:tabular-nums;
  color:var(--navy);
  line-height:1.15;
  min-height:28px;
  display:flex;
  align-items:center
}

.statcard .l {
  font-size:var(--fs-sm);
  color:var(--gray);
  text-transform:uppercase;
  letter-spacing:.5px;
  margin-top:4px
}

.dashfilters {
  display:flex;
  gap:10px;
  flex-wrap:wrap;
  margin-bottom:16px
}

.dashfilters select {
  flex:0 1 210px;
  width:auto;
  padding:8px 11px;
  border:1px solid var(--line);
  border-radius:8px;
  font:inherit;
  font-size:var(--fs-md);
  background:var(--surface)
}

.dashfilters #histsearch {
  flex:1 1 220px;
  margin:0;
  width:auto;
  padding:8px 11px
}
/* La fila entera baja de tres renglones a uno: los filtros dejan de ser un bloque alto
   entre el título y la lista. */

.badge {
  display:inline-block;
  padding:3px 10px;
  border-radius:20px;
  font-size:var(--fs-sm);
  font-weight:600;
  letter-spacing:.3px
}

.badge.pend {
  background:var(--warn-bg);
  color:var(--warn)
}

.badge.apro {
  background:var(--ok-bg);
  color:var(--ok)
}

.badge.rech {
  background:var(--danger-bg);
  color:var(--danger)
}

/* AUD2 pregunta 2 · la palabra del estado cuando la operación NO está cerrada: «Abierta» o
   «Facturada». No es una falta ni un logro — es en qué punto va —, así que va en el azul de
   espera que `tokens.css` ya declara y que la pestaña del expediente ya usa. */
.badge.lista {
  background:var(--espera-bg);
  color:var(--espera)
}

.hstatus {
  padding:6px 8px;
  border:1px solid var(--line);
  border-radius:7px;
  font:inherit;
  font-size:var(--fs-base);
  background:var(--surface);
  cursor:pointer
}

/* Secciones Clientes / Solicitudes / Solicitar acceso */
.cli-form {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 18px 20px;
  margin-bottom: 16px;
}
.cli-form input {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  font: inherit;
  font-size:var(--fs-md);
  margin-bottom: 10px;
}
.cli-form .two {
  display: flex;
  gap: 10px;
}
.cli-form .two input {
  flex: 1;
  min-width: 0;
}
.secsearch {
  width: 100%;
  padding: 11px 13px;
  border: 1px solid var(--line);
  border-radius: 8px;
  font: inherit;
  font-size:var(--fs-md);
  margin-bottom: 16px;
}
.lsub2 {
  margin-top: 14px;
  font-size:var(--fs-base);
}
.lsub2 a {
  color: var(--navy);
  text-decoration: underline;
  cursor: pointer;
}
#solicitar-form input {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  margin-top: 10px;
  font: inherit;
  font-size:var(--fs-md);
}
#solicitar-form .btn {
  width: 100%;
  margin-top: 10px;
}

/* Operaciones */
.op-saldo {
  display: flex;
  align-items: center;
  font-weight: 600;
  color: var(--navy);
  font-size:var(--fs-md);
  padding: 0 4px;
}

/* Sub-navegación de catálogos (hub) */
.subnav {
  display: flex;
  gap: 8px;
  margin-bottom: 18px;
  border-bottom: 1px solid var(--line);
}
.subtab {
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--gray);
  font: inherit;
  font-size:var(--fs-md);
  font-weight: 500;
  padding: 8px 4px;
  margin-right: 10px;
  cursor: pointer;
}
.subtab.on {
  color: var(--navy);
  border-bottom-color: var(--navy);
}
/* 15.06 · UNA PESTAÑA APAGADA SE TIENE QUE VER APAGADA. `disabled` sin estilo se ve igual que
   una pestaña que sólo no está seleccionada —las dos en `var(--gray)` y las dos con el cursor
   de mano—, así que «apagada» lo sabría el navegador y no quien mira. Medido en Chrome a 1280
   antes de escribir esto: `pointer-events: auto`, `opacity: 1`, el MISMO color que la inactiva.
   La forma es la que la casa ya usa para un botón apagado (`.btn[disabled]`:3155 y
   `.btn.mini[disabled]`:2762, las dos `opacity:.4; cursor:not-allowed`): no se estrena una
   tercera respuesta a la misma pregunta. El `cursor` se deja LLEGAR —nada de
   `pointer-events:none`— porque es el que le dice a la mano que ahí no hay nada; quien impide
   el clic es el `disabled`, que el navegador respeta solo. */
.subtab[disabled]{ opacity:.4; cursor:not-allowed }

/* Operación: secciones estimado / real
   TANDA 9 · CIERRE (24-ago noche 2): el micro-rótulo se vuelve GLOBAL — la fusión que la hoja
   del lenguaje dejó escrita («cuando todas hablen igual, estos scopes se funden con la regla
   base»). El título susurra, el dato habla (gramática 1): --fs-rotulo + .06em + 800 + navy. */
.seclabel {
  font-size:var(--fs-rotulo);
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--navy);
  font-weight: 800;
  margin: 8px 0 6px;
}
.op-real {
  background: var(--light);
  border-radius: 8px;
  padding: 10px 12px;
  font-size:var(--fs-base);
  color: var(--text);
  margin-bottom: 10px;
}

.tree-i{color:var(--gray-soft);font-weight:400}
.implbl{display:block;font-size:var(--fs-sm);color:var(--gray);margin:2px 0 6px;font-weight:600}
.impfield{width:100%;box-sizing:border-box;padding:10px 12px;border:1px solid var(--line);border-radius:8px;font:inherit;font-size:var(--fs-md);margin-bottom:12px}
.modal-ov{position:fixed;inset:0;background:rgba(20,28,45,.5);display:flex;align-items:flex-start;justify-content:center;z-index:1000;padding:40px 16px;overflow:auto}
/* La sombra del modal ES --sombra-3 (tanda 9: el token nació DE este valor); ahora la nombra. */
.modal-card{background:var(--surface);border-radius:14px;max-width:640px;width:100%;padding:26px 28px;position:relative;box-shadow:var(--sombra-3)}
.modal-x{position:absolute;top:14px;right:16px;border:none;background:none;font-size:var(--fs-lg);cursor:pointer;color:var(--gray)}
.pdf-modal{padding:24px 16px;align-items:flex-start}
.pdf-modal[hidden]{display:none!important}
.pdf-modal-card{max-width:980px;padding:0;overflow:hidden}
.pdf-modal-head{display:flex;align-items:flex-start;justify-content:space-between;gap:20px;padding:22px 28px 16px;border-bottom:1px solid var(--line)}
.pdf-modal-head h3{margin:2px 36px 0 0}
.pdf-modal-head .modal-x{top:20px;right:24px}
.pdf-modal-preview{max-height:calc(100vh - 220px);padding:24px;background:var(--light)}
.pdf-modal-foot{display:flex;align-items:center;justify-content:space-between;gap:18px;padding:16px 28px;border-top:1px solid var(--line);background:var(--surface)}
.pdf-modal-estado{font-size:var(--fs-base);color:var(--gray)}
.pdf-modal-estado.generando{color:var(--navy);font-weight:600}
.pdf-modal-estado.descargado{color:var(--ok);font-weight:600}
.pdf-modal-estado.error{color:var(--danger);font-weight:600}
.pdf-modal-acciones{display:flex;gap:10px;flex-shrink:0}
.pdf-modal button:disabled{cursor:wait;opacity:.58}
@media(max-width:640px){
  .pdf-modal{padding:8px}
  .pdf-modal-head{padding:18px 18px 14px}
  .pdf-modal-preview{padding:12px;max-height:calc(100vh - 205px)}
  .pdf-modal-foot{align-items:stretch;flex-direction:column;padding:14px 18px}
  .pdf-modal-acciones{width:100%}
  .pdf-modal-acciones .btn{flex:1}
}
/* ── E5|2 (2b, rebote del 7-sep) · EL VISOR DE LA REVISIÓN DEL EXPEDIENTE ──────────────────────
   Cuatro clases y las cuatro son de LAYOUT: ni un hex, ni un color propio — todo por token, y
   `--ok`/`--danger` no se tocan (styles.css ya lo dice medido: «--ok ya significa 23 cosas»). La
   forma la pone el molde `.pdf-modal-*` de arriba, que este diálogo REUSA entero; esto sólo agrega
   lo que el molde no tenía: una tira para elegir archivo cuando el renglón trae varios, el visor,
   y el cuerpo con el motivo entre la vista previa y el pie.
   Se DESCARTÓ pintar el diálogo (banda de cabecera + «Rechazar» rojo): el porqué vive en
   `expRevisarDialogoHTML`, junto al código que decide. */
.expr-tira{display:flex;flex-wrap:wrap;gap:7px;margin-bottom:10px}
.expr-chip{border:1px solid var(--line);border-radius:6px;background:var(--surface);
  color:var(--gray);font:inherit;font-family:var(--font-mono);font-size:var(--fs-xs);
  padding:4px 9px;cursor:pointer}
.expr-chip.on{border-color:var(--navy);color:var(--navy);background:var(--light);font-weight:700}
/* La altura se acota a propósito: `.pdf-modal-preview` trae `max-height` pero la tarjeta recorta
   con `overflow:hidden`, así que un visor sin techo se ve CORTADO y nadie sabe por qué. */
.expr-visor{display:block;width:100%;height:min(52vh,520px);min-height:220px;
  border:1px solid var(--line);border-radius:6px;background:var(--surface);object-fit:contain}
.expr-cuerpo{padding:14px 28px 18px}
/* ── 14.92 · LA SESIÓN DEL EXPEDIENTE, POR CLIENTE ─────────────────────────────────────────────
   Todo es LAYOUT: cero clases de color nuevas y cero `hex` (los trinquetes `hexEnJS` y
   `estilosInline` sólo pueden bajar). El molde sigue siendo `.pdf-modal-*`, que ya vive arriba con
   su `@media`; lo que estas reglas añaden es la columna de la tira al lado del documento y la
   barra de avance. La app interna es de COMPUTADORA (J, 5-sep): a partir de 900 px la sesión son
   dos columnas, y por debajo se apilan para que nada quede inalcanzable. */
.expr-sesion{border:1px solid var(--line);border-radius:8px;background:var(--surface);overflow:hidden}
.expr-ses{display:flex;align-items:stretch;gap:0}
.expr-ses-col{flex:0 0 300px;border-right:1px solid var(--line);padding:14px 16px;
  max-height:min(70vh,720px);overflow:auto}
.expr-ses-doc{flex:1 1 auto;min-width:0}
.expr-pista{margin:0 0 8px;font-size:var(--fs-xs)}
.expr-tira-v{display:flex;flex-direction:column;gap:2px}
.expr-fila{display:flex;align-items:center;gap:8px;padding:5px 6px;border-radius:6px}
.expr-fila.on{background:var(--light)}
.expr-fila:hover{background:var(--hover-bg)}   /* CSS-5 · sin respaldo · ver tokens.css */
.expr-chk{flex:0 0 auto;margin:0;cursor:pointer}
/* 14.93 · J vio la tira con barra de desplazamiento horizontal y sin número ni nombre. MEDIDO
   en Chrome a 1280 sobre el render real: la casilla medía 255 px de ancho —el 100% del ancho
   de la fila— y empujaba `.expr-num` y `.expr-doc` fuera de la columna (el nombre arrancaba en
   x=382 con la tira terminando en x=350), con scrollWidth 407 contra clientWidth 299.
   NO era la regla de arriba: `.expr-chk` nunca declaró `width`. Es la GLOBAL
   `input,select,textarea{width:100%}` (~:75), que alcanza a TODA casilla de la app — la misma
   que rompió las condicionales del expediente el 4-sep (`.exp-cond-row`, ~:3362) y la casilla
   del IVA del libro (`table.lb .lb-alta-iva input`, ~:3125). Ésta es la TERCERA pantalla que la
   paga, y por eso el arreglo de raíz —sacar a `input[type="checkbox"]` de la global de una vez—
   queda NOMBRADO como pendiente: cambia el ancho de toda casilla de la app y eso no cabe en un
   arreglo acotado de dos pantallas (ficha de parking).
   `.expr-fila .expr-chk` es 0,2,0 contra 0,0,1 de la global, así que gana sin `!important`; el
   `padding:0` le quita los `9px 11px` que esa misma global le pone. */
.expr-fila .expr-chk{width:auto;flex:0 0 auto;padding:0;margin:0}
.expr-num{flex:0 0 22px;text-align:right;font-family:var(--font-mono);font-size:var(--fs-xs);
  color:var(--gray)}
.expr-doc{flex:1 1 auto;min-width:0;cursor:pointer;font-size:var(--fs-base)}
.expr-fila.on .expr-doc{font-weight:700;color:var(--navy)}
.expr-n{flex:0 0 auto;font-family:var(--font-mono);font-size:var(--fs-xs);color:var(--gray);
  border:1px solid var(--line);border-radius:10px;padding:0 6px}
.expr-bloque{margin-top:12px;padding-top:12px;border-top:1px solid var(--line);
  display:flex;flex-direction:column;gap:6px}
.expr-bloque input{width:100%}
.expr-paso{display:flex;flex-direction:column;gap:2px;align-items:flex-end;flex:0 0 auto;
  font-size:var(--fs-xs)}
.expr-barra{height:5px;border-radius:3px;background:var(--light);overflow:hidden;
  margin-top:4px;min-width:120px}
/* El relleno va por DECENAS y no por `style="width:N%"`: `estilosInline` es un trinquete que sólo
   puede bajar, y una barra es un vistazo — el número exacto ya está escrito con palabras al lado
   («3 de 17», «2 aceptados · 0 rechazados»). Once reglas, ni una interpolada. */
.expr-lleno{height:100%;background:var(--navy);width:0}
.expr-lleno[data-pct="10"]{width:10%}
.expr-lleno[data-pct="20"]{width:20%}
.expr-lleno[data-pct="30"]{width:30%}
.expr-lleno[data-pct="40"]{width:40%}
.expr-lleno[data-pct="50"]{width:50%}
.expr-lleno[data-pct="60"]{width:60%}
.expr-lleno[data-pct="70"]{width:70%}
.expr-lleno[data-pct="80"]{width:80%}
.expr-lleno[data-pct="90"]{width:90%}
.expr-lleno[data-pct="100"]{width:100%}
.expr-cont{font-weight:700;font-size:var(--fs-lg)}
/* 14.93 · «N archivos · N con veredicto en esta sesión» llevaba `expr-mini`, que es la clase de
   la MINIATURA de la tira (abajo: 52×38 con `object-fit`). MEDIDO a 1280: el texto se dibujaba
   dentro de una caja de 52×38 px y salía 87 px encima de la columna «Lo más nuevo» (terminaba en
   x=825 con la celda acabando en x=738). No es un `<img>`: es un renglón de texto, y lo único
   que necesita es ser más chico que el conteo de arriba. `--fs-sm` = 12px (tokens.css:57). */
.expr-archivos{font-size:var(--fs-sm);margin-top:3px}
/* 15.02 · EL INDICE DE EXPEDIENTES · `.dtable td` es `white-space:nowrap`, y esta tabla tiene
   SEIS columnas, dos de ellas de texto largo (la razon social y el nombre del documento por
   vencer). Medido en Chrome a 1280 px —la pantalla de la casa—: la tabla pedia 1115 px dentro de
   una caja de 994 y empujaba la pagina 94 px. Estas DOS celdas parten la linea; las de numeros y
   fechas siguen sin partirse, que es lo que hace legible una tabla. */
.dtable td.expidx-tx{ white-space:normal; min-width:150px; max-width:260px }
.expr-mini{display:block;width:52px;height:38px;object-fit:cover;border-radius:3px;
  border:1px solid var(--line);margin:0 auto 3px}
.expr-chip-t{display:block}
@media(max-width:640px){
  .expr-visor{height:min(44vh,340px);min-height:180px}
  .expr-cuerpo{padding:12px 18px 14px}
}
@media(max-width:900px){
  .expr-ses{flex-direction:column}
  .expr-ses-col{flex:1 1 auto;border-right:0;border-bottom:1px solid var(--line);max-height:none}
}
/* 11.4 · Guardar/Cerrar visibles mientras el modal scrollea (MOV-9; medido por J el 14-ago: la
   captura sola ya empuja los botones). Pegajosa al fondo del scrollport (.modal-ov es quien
   scrollea). Borrar NUNCA viaja aqui: su distancia es su proteccion contra el clic errado. */
.btns-fijos{ position:sticky; bottom:0; display:flex; gap:var(--sp-2); background:var(--surface);
  padding:var(--sp-3) 0 var(--sp-1); margin-top:var(--sp-3); border-top:1px solid var(--line) }
.opd-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px 18px;font-size:var(--fs-md)}
.opd-grid .opd-k{display:block;font-size:var(--fs-xs);color:var(--gray);text-transform:uppercase;letter-spacing:.03em;margin-bottom:1px}
.opfilters{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:10px;margin-bottom:10px}
.opfilters select{width:100%;box-sizing:border-box;padding:10px 12px;border:1px solid var(--line);border-radius:8px;font:inherit;font-size:var(--fs-md);background:var(--surface)}
@media(max-width:640px){.opfilters{grid-template-columns:1fr}}
.movfilters{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-bottom:6px}
.movfilters select,.movfilters input{width:100%;box-sizing:border-box;padding:10px 12px;border:1px solid var(--line);border-radius:8px;font:inherit;font-size:var(--fs-md);background:var(--surface)}
.refline{margin-top:6px;font-size:var(--fs-sm);color:var(--gray);display:flex;align-items:center;gap:6px}
.refedit{padding:4px 8px;border:1px solid var(--line);border-radius:6px;font:inherit;font-size:var(--fs-sm);max-width:190px}
.flab{display:block;font-size:var(--fs-xs);color:var(--gray);margin:0 0 4px;font-weight:600}
.cli-form .two .fld{flex:1;min-width:0}
.cli-form .fld input{margin-bottom:10px}

/* ==== Tablas de datos (Movimientos / Operaciones) ==== */
.tablewrap{overflow:auto;max-height:70vh;border:1px solid var(--line);border-radius:10px;background:var(--surface)}
.dtable thead th{position:sticky;top:0;z-index:2}
/* UI-3 · Se pega la columna que IDENTIFICA el renglón, no la casilla.
   Estaba en `td:first-child`, que es la casilla de seleccionar: al desplazarte a la derecha se
   quedaba pegado un cuadrito vacío de 34px y **la referencia se iba** — justo lo único que te dice
   de qué renglón estás leyendo los números. Ahora se pegan las dos, casilla y referencia, y la
   segunda arranca donde termina la primera.
   `.nochk` es para la tabla de operaciones cuando quien mira no puede editar: ahí no hay casilla,
   así que la referencia se pega en el borde. */
.dtable th.chkcol,.dtable td.chkcol{ position:sticky; left:0; width:34px; background:var(--surface); z-index:3 }
.dtable thead th.chkcol{ background:var(--light); z-index:4 }
.dtable th.refcol,.dtable td.refcol,
.dtable th.mc-fecha,.dtable td.mc-fecha{ position:sticky; left:34px; background:var(--surface); z-index:2 }
.dtable thead th.refcol,.dtable thead th.mc-fecha{ background:var(--light); z-index:4 }
.dtable.nochk th.refcol,.dtable.nochk td.refcol{ left:0 }
/* La sombra marca dónde termina lo fijo y empieza lo que se desliza. Sin ella, al desplazarte
   parece que los números pertenecen a otra columna. */
.dtable td.refcol,.dtable td.mc-fecha,.dtable.nochk td.refcol{ box-shadow:6px 0 8px -8px rgba(28,49,73,.35) }
.dtable tbody tr:hover td.chkcol,
.dtable tbody tr:hover td.refcol,
.dtable tbody tr:hover td.mc-fecha{ background:var(--light) }
.dtable{width:100%;border-collapse:collapse;font-size:var(--fs-md)}
.dtable th{text-align:left;padding:9px 12px;background:var(--light);color:var(--gray);font-weight:600;border-bottom:1px solid var(--line);cursor:pointer;white-space:nowrap;user-select:none}
.dtable th.sorted{color:var(--navy)}
.dtable th.num{text-align:right}
.dtable td{padding:8px 12px;border-bottom:1px solid var(--line);white-space:nowrap;vertical-align:middle}
.dtable td.num{text-align:right;font-variant-numeric:tabular-nums}
.dtable td.desc{max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dtable tbody tr:hover td{background:var(--light)}
.dtable tbody tr{cursor:default}
.dtable .refedit{max-width:140px;margin:0}
.dtable td.act{white-space:nowrap;text-align:right}
.dtable td.act .mini{margin-left:4px}
.dtable.rows-click tbody tr{cursor:pointer}
.opd-actions{display:flex;gap:8px;margin-bottom:16px;flex-wrap:wrap}
/* OPR-16 v3 casilla 4 · los dos documentos, a la DERECHA de la misma fila. `margin-left:auto` los
   empuja al otro extremo sin sacarlos del flujo, asi que a ancho de telefono envuelven en vez de
   salirse. Atenuados en operacion cerrada por `.apagado`, que ya existe: su regla de `input` no
   toca botones, asi que el clic sigue llegando y el toast sigue diciendo por que. */
.opd-docs{margin-left:auto;display:flex;gap:8px;flex-wrap:wrap}

/* Solo lectura: ocultar acciones de escritura */
.rol-lectura .cli-form,
.rol-lectura .hact,
.rol-lectura #empGuardar{display:none!important}
.rol-lectura .refedit{pointer-events:none;background:var(--light)}
.rol-lectura .dtable td.act{visibility:hidden}
.tscroll{overflow-x:auto;overflow-y:hidden;height:14px;margin-bottom:2px}
.tscroll>div{height:1px}
.tab-group{position:relative;display:inline-block}
.tab-menu{position:absolute;top:100%;right:0;margin-top:4px;background:var(--surface);border:1px solid var(--line);border-radius:8px;box-shadow:0 6px 20px rgba(0,0,0,.12);z-index:50;min-width:170px;padding:4px}
.tab-menu button{display:block;width:100%;text-align:left;padding:9px 12px;background:none;border:none;cursor:pointer;font:inherit;font-size:var(--fs-md);border-radius:6px;color:var(--text)}
.tab-menu button:hover{background:var(--light)}

.catedit{padding:4px 8px;border:1px solid var(--line);border-radius:6px;font:inherit;font-size:var(--fs-sm);max-width:170px}

/* ================= Móvil ================= */
@media(max-width:860px){
  .topbar{flex-wrap:wrap;gap:10px;padding:10px 14px}
  .tb-logo{height:26px}
  .tb-right{order:2;margin-left:auto}
  .userlabel{max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .tabs{order:3;width:100%;margin-left:0;overflow-x:auto;flex-wrap:nowrap;padding-bottom:2px;-webkit-overflow-scrolling:touch}
  .tab{padding:7px 10px;font-size:var(--fs-base);white-space:nowrap}
  #admin-group{margin-left:0!important;flex:0 0 auto}
  .tab-menu{right:auto;left:0}
  .histwrap{padding:0 14px;margin:16px auto}
  .subnav{overflow-x:auto;flex-wrap:nowrap;-webkit-overflow-scrolling:touch}
  .subtab{white-space:nowrap}
  .app{grid-template-columns:1fr;padding:0 14px;gap:16px}
  .two,.three{grid-template-columns:1fr}
  .modal-ov{padding:14px 8px;align-items:flex-start}
  .modal-card{padding:20px 16px}
  #doc{padding:26px 22px}
  h2{font-size:var(--fs-lg)}
}
@media(max-width:480px){
  .dashstats{grid-template-columns:1fr 1fr}
  .tab{font-size:var(--fs-sm);padding:6px 9px}
}

/* Importador: resumen + conciliación */
.imp-summary{margin:10px 0 4px;font-size:var(--fs-md)}
.imp-recon{margin-top:12px;border:1px solid var(--recon-line);border-radius:10px;padding:12px 14px;background:var(--recon-bg)}
.imp-recon .ir-row{display:flex;justify-content:space-between;align-items:center;gap:12px;font-size:var(--fs-md)}
.imp-recon .ir-row b{font-size:var(--fs-lg)}
.imp-recon .ir-ind{margin-top:8px;font-size:var(--fs-base);line-height:1.45}
.imp-recon .ir-ind.ok{color:var(--ok);font-weight:600}
.imp-recon .ir-ind.bad{color:var(--danger);font-weight:600}

/* Cuenta inactiva */
.cuerow.inactiva{opacity:.5}
.cuerow.inactiva .badge{background:var(--light);color:var(--gray)}

.imp-actions{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.btn.ghost{background:var(--surface);color:var(--navy);border:1px solid var(--line)}
.imp-force{font-size:var(--fs-sm);color:var(--danger);display:flex;align-items:center;gap:5px;cursor:pointer}

.imp-recon .ir-row.big{border-top:1px solid var(--recon-line);margin-top:4px;padding-top:6px}
.imp-recon .ir-row.big b{font-size:var(--fs-lg)}
.ir-input{width:150px;text-align:right;padding:5px 8px;border:1px solid var(--line);border-radius:6px;font-size:var(--fs-md)}
.imp-sel{margin:14px 0 6px;font-size:var(--fs-base)}
.imp-sel a{color:var(--warn)}
.imp-chk{width:16px;height:16px;cursor:pointer}

tr.imp-pos{background:var(--warn-bg)}
tr.imp-pos td{border-top:1px solid var(--warn-border)}

.movfilters label.fdate{display:inline-flex;align-items:center;gap:5px;font-size:var(--fs-sm);color:var(--gray)}
.movfilters label.fdate input{font-size:var(--fs-base)}

/* Formulario de cuenta compacto */
.cu-form{border:1px solid var(--line);border-radius:10px;padding:12px 14px;margin-bottom:14px;background:var(--light)}
.cu-grid{display:grid;grid-template-columns:2fr 1fr 1fr 1fr 1.6fr;gap:10px}
.cu-grid label{display:flex;flex-direction:column;gap:4px;font-size:var(--fs-sm);color:var(--gray);font-weight:600;min-width:0}
.cu-grid input,.cu-grid select{padding:8px 10px;border:1px solid var(--line);border-radius:8px;font:inherit;font-size:var(--fs-md);box-sizing:border-box;width:100%}
@media(max-width:820px){.cu-grid{grid-template-columns:1fr 1fr}}
/* Tabla compacta de catálogo: no estira columnas ni corta botones */
.dtable.compact{width:auto;min-width:0}
.dtable.compact td,.dtable.compact th{white-space:nowrap}
.dtable.compact td.act{position:static}

/* Catálogo compacto: sin recorte horizontal ni columna pegajosa (los botones ya no se cortan) */
.tablewrap.plain{overflow:visible;max-height:none}
/* El recuadro blanco abraza la tabla compacta (no se estira a todo el ancho) */
.tablewrap.plain:has(.dtable.compact){width:fit-content;max-width:100%}
/* CSS-1 (2-sep-2026) · EN PANTALLA ANGOSTA la tabla se desplaza DENTRO de su caja en vez de
   empujar la página entera. Medido con `tests/movil.test.js` sobre los 12 sitios `tablewrap
   plain`: a 390 px se desbordaban DIEZ de doce (hasta 1 099 px en el catálogo de conceptos) y a
   768 px seis; con esta regla, CERO en los dos. El usuario acababa paneando el documento
   completo —encabezado, menú y todo— para leer la última columna.
   ⚠ El `overflow:visible` de arriba NO se toca y es a propósito: su comentario dice para qué se
   puso —que en escritorio no se recorten los botones ni se pegue la primera columna— y eso sigue
   siendo cierto arriba de 900 px. Medido: a 1424 y a 1297 px no se movió NI UN PÍXEL en ninguno
   de los doce sitios. Por eso es un `@media` y no un cambio a secas.
   ⚠ Y lo que esta regla NO arregla, dicho en vez de escondido: `renderConceptos` se desborda
   también en ESCRITORIO (110 px en un monitor de 1920, 233 en una laptop de 1440) porque su
   tabla mide 1 463 px con `white-space:nowrap`. Ése es OTRO defecto —de columnas, no de caja— y
   tiene ficha aparte; aquí queda ANCLADO en la línea base para que no pueda empeorar callado. */
@media (max-width: 900px){ .tablewrap.plain{ overflow:auto } }
.dtable.compact th:first-child{position:static}
.dtable.compact td:first-child{position:static;background:transparent}
/* Utilidades de espaciado (reemplazan estilos inline repetidos del HTML) */
.mt8{margin-top:var(--e2)}
.ml-auto{margin-left:auto}
.mb12{margin-bottom:var(--e3)}
.sec-hint{margin:-2px 0 14px}
.sec-hint2{margin:-2px 0 12px}
.sec-hint3{margin:4px 0 12px}
/* El sello de version es el INSTRUMENTO con el que se verifica que un despliegue aterrizo
   (regla L: «comprobar que la base sea la que se cree»). Estaba en el tamano mas chico del
   sistema Y con `opacity:.55` encima, que baja el gris-500 a un contraste de ~2:1 — por debajo
   del minimo legible para texto secundario. El 7-ago costo un rato real: decia 2.4, no se leia,
   y hubo que adivinar que faltaba un Ctrl+F5. Un dato que existe para verificar tiene que
   poder leerse. `tabular-nums` para que los digitos queden alineados: este numero se compara
   de un vistazo contra otro, no se lee como prosa. */
.appver{display:block;font-size:var(--fs-sm);color:var(--gray);margin-top:6px;letter-spacing:.01em;font-variant-numeric:tabular-nums}
.appver.pruebas{color:var(--danger);font-weight:700;letter-spacing:.03em}
.rep-controls{display:flex;gap:22px;flex-wrap:wrap;align-items:center;margin:2px 0 16px;font-size:var(--fs-base);color:var(--gray)}
.rep-controls input[type=date]{margin-left:6px;padding:5px 8px;border:1px solid var(--line);border-radius:8px;font:inherit}
.dtable.compact td.act{padding-left:16px}
.dtable.compact td.act .mini{white-space:nowrap}

.admin-solo{display:none}
body.es-admin .admin-solo{display:inline-flex}
.conctog:hover{opacity:.7}

.rowx{cursor:pointer;color:var(--gray-soft);font-size:var(--fs-md);padding:2px 8px;border-radius:5px;user-select:none}
.rowx:hover{color:var(--danger);background:var(--danger-bg)}

.opfilters input{width:100%;box-sizing:border-box;padding:10px 12px;border:1px solid var(--line);border-radius:8px;font:inherit;font-size:var(--fs-md);background:var(--surface)}

/* Cotizador: vista previa como toggle; por default el formulario usa todo el ancho */
#cotizApp{grid-template-columns:1fr}
#cotizApp .pane.preview{display:none}
#cotizApp.with-preview{grid-template-columns:minmax(420px,480px) 1fr}
#cotizApp.with-preview .pane.preview{display:block}
@media(max-width:860px){#cotizApp.with-preview{grid-template-columns:1fr}}

/* Encabezado del cotizador congelado (sticky) */
.cotiz-sticky{position:sticky;top:0;z-index:30;background:var(--bg);display:flex;align-items:center;gap:12px;max-width:1280px;margin:0 auto;padding:12px 22px}

/* Números tabulares: los montos alinean y se comparan de un vistazo */
.hrow,.statcard,.op-saldo,.dtable,.dashstats,.opd-grid,.opd-k,#doc{font-variant-numeric:tabular-nums}

/* Escala de espaciado / utilidades (mata estilos inline repetidos) */
h2 + p.muted{margin:0 0 16px}
.row-btns{display:flex;gap:8px;flex-wrap:wrap}
/* AUD2 p3 · el cuadro de aplicar perdió su pista suelta a la izquierda, así que sus dos botones
   se van a la derecha. Es una clase y no un `style=` a propósito: el trinquete `estilosInline`
   sólo impide SUBIR, y subirlo por un margen sería gastar la única defensa que hay. */
.row-btns.der{justify-content:flex-end}

/* Inicio: bandeja de trabajo "Te toca hoy" */
.qlist{background:var(--surface);border:1px solid var(--line);border-radius:12px;overflow:hidden;margin-top:6px}
.qrow{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:13px 16px;border-bottom:1px solid var(--line);cursor:pointer;transition:background .12s}
.qrow:last-child{border-bottom:none}
.qrow:hover{background:var(--light)}
.qrow .qtxt{font-size:var(--fs-md);color:var(--text)}
.qrow .qarrow{color:var(--gray);font-size:var(--fs-lg);flex:none}
.qrow:hover .qarrow{color:var(--navy)}

/* Movimientos: selección múltiple */
/* El «+ devolución» dentro de su propio bloque (8-ago). Vivía en una banda que quedó huérfana
   cuando P-35b se llevó al ✎ del honorario junto al renglón que edita. */
/* Alta rapida de contraparte, sin salirse de la clasificacion (8-ago). Aparece SOLO cuando la
   busqueda no encontro nada — si apareciera siempre, invitaria a duplicar. */
.alta-cp{margin-top:var(--sp-2)}
.alta-cp-ask{display:flex;gap:var(--sp-2);align-items:center;flex-wrap:wrap;
  font-size:var(--fs-sm);color:var(--warn)}
.alta-cp-form{display:flex;gap:var(--sp-2);align-items:center;flex-wrap:wrap;
  margin-top:var(--sp-2);padding-top:var(--sp-2);border-top:1px solid var(--line)}
.alta-cp-form .impfield{flex:1 1 160px;min-width:0}
.fila-dev{display:flex;gap:var(--sp-2);align-items:center;flex-wrap:wrap;margin-top:var(--sp-3);
  padding-top:var(--sp-2);border-top:1px solid var(--line);font-size:var(--fs-base)}
.dtable .chkcol{width:34px;text-align:center;padding-left:8px;padding-right:4px}
.dtable .chkcol input{width:16px;height:16px;cursor:pointer;margin:0}
.mov-bulkbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;background:var(--ok-bg);border:1px solid var(--ok-border);border-radius:8px;padding:8px 12px;margin-bottom:10px;font-size:var(--fs-base)}
.mov-bulkbar #bulk_cat{width:190px;padding:6px 9px;border:1px solid var(--line);border-radius:6px;font-size:var(--fs-base)}
.mov-bulkbar .bulk-del{color:var(--danger);border-color:var(--danger-border)}

/* Detalle de operación: tabla de saldo alineada */
.saldo-tabla{border-collapse:collapse;font-size:var(--fs-md);min-width:300px}
.saldo-tabla td{padding:5px 0}
.saldo-tabla td.num{padding-left:36px;font-variant-numeric:tabular-nums;text-align:right;font-weight:500}
.saldo-tabla tr.sub td{border-top:1px solid var(--line);font-weight:600;padding-top:7px}
.saldo-tabla tr.tot td{border-top:2px solid var(--navy);font-weight:700;padding-top:7px}

/* Movimientos: categoría/referencia como texto editable al clic (fuera 686 inputs) */
.cat-cell,.ref-cell{cursor:text;display:inline-block;min-width:70px;border-bottom:1px dashed transparent;padding:1px 3px;border-radius:3px}
.cat-cell:hover,.ref-cell:hover{border-bottom-color:var(--line);background:var(--light)}
.cat-cell.editing,.ref-cell.editing{background:transparent;border-bottom-color:transparent;padding:0}
.cat-cell .catedit,.ref-cell .refedit{max-width:150px;margin:0;padding:5px 7px}

/* D-73 · la celda de categoría pide el CLIENTE cuando la cuenta rueda al 206 (13.6 · 18-ago).
   Va en la MISMA celda a propósito: no es un diálogo nuevo, es un dato más del mismo gesto. */
.cat-cell.editing{display:inline-flex;align-items:center;flex-wrap:wrap;gap:6px}
.cat-sufijo:empty{display:none}
.cat-cli{display:inline-flex;align-items:center;gap:5px}
.cat-cli-k{font-size:var(--fs-xs);color:var(--gray);white-space:nowrap}
.cat-cli-k .req{color:var(--danger)}
.cliedit{padding:5px 7px;border:1px solid var(--line);border-radius:6px;font:inherit;font-size:var(--fs-sm);max-width:190px;margin:0}
.cat-deriv{display:inline-flex;align-items:center;gap:6px;font-size:var(--fs-sm);background:var(--ok-bg);border:1px solid var(--ok-border);border-radius:6px;padding:3px 8px;white-space:nowrap}
.cat-deriv .linklike{font-size:var(--fs-sm)}
/* El respaldo del candado, en español y dentro de la celda: el Momento 3 del boceto. No es un
   toast porque aquí no interrumpe nada — se lee al lado de lo que lo provocó. */
.cat-cli-no{display:inline-block;max-width:420px;font-size:var(--fs-xs);color:var(--danger);background:var(--danger-bg);border:1px solid var(--danger-border);border-radius:6px;padding:4px 8px;white-space:normal;line-height:1.35}

/* Cotización: barra de guardar sticky abajo */
.toolbar-sticky{position:sticky;bottom:0;background:var(--surface);border-top:1px solid var(--line);padding:12px 0 4px;margin-top:16px;z-index:8}

/* Cotización: folio/fecha/vigencia en el encabezado sticky */
.cotiz-meta{display:flex;gap:12px;align-items:center;margin-left:18px;flex-wrap:wrap}
.cotiz-meta label{display:flex;align-items:center;gap:6px;font-size:var(--fs-sm);color:var(--gray);margin:0;white-space:nowrap}
.cotiz-meta input{width:auto;max-width:130px;padding:5px 8px;font-size:var(--fs-base);margin:0}
@media(max-width:900px){.cotiz-meta{margin-left:0;width:100%;order:3}}

/* Cotización: CSF como zona drag-and-drop */
.csf-drop{border:1.5px dashed var(--line);border-radius:8px;padding:14px 12px;text-align:center;font-size:var(--fs-base);color:var(--gray);background:var(--light);cursor:default}
.csf-drop.over{border-color:var(--navy);background:var(--light);color:var(--navy)}
.csf-pick{color:var(--navy);font-weight:600;cursor:pointer;text-decoration:underline}

/* Clasificador rápido — drawer lateral + teclado */
.clsf-back{position:fixed;inset:0;background:rgba(20,30,45,.28);z-index:60}
.clsf-drawer{position:fixed;top:0;right:0;height:100vh;width:380px;max-width:92vw;background:var(--surface);border-left:1px solid var(--line);box-shadow:-6px 0 24px rgba(0,0,0,.12);z-index:61;display:flex;flex-direction:column;padding:18px 18px 12px}
.clsf-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:14px}
.clsf-head b{font-size:var(--fs-md)}
.clsf-body{flex:1;overflow:auto}
.clsf-mov{background:var(--light);border-radius:10px;padding:14px;margin-bottom:16px}
.clsf-monto{font-size:var(--fs-xl);font-weight:700;font-variant-numeric:tabular-nums}
.clsf-meta{font-size:var(--fs-sm);color:var(--gray);margin:4px 0 8px}
.clsf-desc{font-size:var(--fs-md);font-weight:600;line-height:1.4;word-break:break-word}
.clsf-foot{display:flex;gap:8px;margin-top:10px}
.clsf-keys{font-size:var(--fs-xs);text-align:center;margin-top:8px}

/* Cotización: cliente compacto */
.cli-resumen{display:flex;justify-content:space-between;align-items:center;gap:10px;background:var(--light);border:1px solid var(--line);border-radius:8px;padding:10px 12px;margin-top:8px}
.cli-res-txt b{font-size:var(--fs-md)}
.linklike{background:none;border:none;color:var(--navy);font-weight:600;font-size:var(--fs-base);cursor:pointer;padding:0;text-decoration:underline;width:auto}

/* ===== Sidebar vertical (navegación agrupada por área de trabajo) ===== */
.shell{ display:flex; align-items:flex-start; min-height:100vh }
.sidebar{
  position:sticky; top:0; height:100vh; width:230px; flex-shrink:0;
  background:var(--navy); color:var(--surface);
  display:flex; flex-direction:column;
  padding:18px 14px; overflow-y:auto
}
.sb-logo{ height:34px; align-self:flex-start; margin:2px 6px 14px }
.sb-nav{ display:flex; flex-direction:column; gap:2px; flex:1 }
.sb-group{
  font-size:var(--fs-xs); text-transform:uppercase; letter-spacing:1.2px;
  color:var(--on-navy-dim); margin:14px 10px 4px; font-weight:600
}
.navitem{
  display:block; width:100%; text-align:left;
  background:transparent; border:none; color:var(--on-navy);
  font:inherit; font-size:var(--fs-md); font-weight:500;
  padding:9px 12px; border-radius:var(--r-md); cursor:pointer
}
.navitem:hover{ background:rgba(255,255,255,.08); color:var(--surface) }
.navitem.on{ background:rgba(255,255,255,.14); color:var(--surface) }
.sb-foot{
  border-top:1px solid rgba(255,255,255,.12); margin-top:10px; padding-top:14px;
  display:flex; flex-direction:column; gap:10px; align-items:flex-start
}
.sidebar .btn{ background:var(--surface); color:var(--navy); border-color:var(--surface) }
.sidebar .btn.ghost{ background:transparent; color:var(--surface); border-color:rgba(255,255,255,.45) }
.sidebar .btn.ghost:hover{ background:rgba(255,255,255,.12) }
.main{ flex:1; min-width:0 }

@media(max-width:980px){
  .shell{ flex-direction:column }
  .sidebar{
    position:static; height:auto; width:auto;
    flex-direction:row; align-items:center; gap:6px;
    padding:8px 12px; overflow-x:auto
  }
  .sb-logo{ height:24px; margin:0 8px 0 0 }
  .sb-nav{ flex-direction:row; flex:1; gap:4px; overflow-x:auto; -webkit-overflow-scrolling:touch }
  .sb-group{ display:none }
  .navitem{ white-space:nowrap; padding:7px 10px; font-size:var(--fs-base) }
  .sb-foot{ border-top:none; margin-top:0; padding-top:0; flex-direction:row; align-items:center; gap:8px }
  .sb-foot .userlabel{ display:none }
}

/* ══ PORTAL · SU PROPIA SHELL (14.95 · hoja «la forma del portal», acta de J del 12-sep) ══════
   NO se toca «.shell»/«.sidebar» de arriba: el portal DEJA DE VIVIR dentro de ellas —
   «#view-cliente» es hermano de «#shell-emp» en index.html — y trae su propio contenedor.
   Lo que SI se reusa es lo que vale: «.navitem», «.btn.ghost» y los tokens («--navy»,
   «--surface», «--on-navy»). Cero color nuevo, cero tamaño fuera de la escala.
   Por que aparte y no reusando «.sidebar»: la barra ABAJO del telefono no existe ahi y no puede
   existir sin romper a los 15 destinos de empleado que comparten su «@media» de 980. */
.portal-shell{ display:flex; align-items:stretch; min-height:100vh }
.portal-nav{ position:sticky; top:0; height:100vh; width:230px; flex-shrink:0;
  background:var(--navy); color:var(--surface); display:flex; flex-direction:column;
  padding:18px 14px; overflow-y:auto }
.portal-main{ flex:1; min-width:0 }
.portal-logo{ height:34px; align-self:flex-start; margin:2px 6px 14px; cursor:pointer;
  background:none; border:0; padding:0 }
.portal-logo img{ height:34px; display:block }
.portal-items{ display:flex; flex-direction:column; gap:2px; flex:1 }
.portal-foot{ border-top:1px solid rgba(255,255,255,.12); margin-top:10px; padding-top:14px;
  display:flex; flex-direction:column; gap:10px; align-items:flex-start }
.portal-ver{ font-size:var(--fs-xs); color:var(--on-navy-dim) }
.portal-nav .btn.ghost{ background:transparent; color:var(--surface);
  border-color:rgba(255,255,255,.45) }
.portal-nav .btn.ghost:hover{ background:rgba(255,255,255,.12) }
/* El icono SOLO existe en el telefono: en la columna el rotulo va solo, como en «.sidebar». */
.portal-ic{ display:none }
/* La cabecera angosta del telefono (forma P1, casilla 5 de J): el logo a 24 px —el MISMO alto que
   la app interna le da a «.sb-logo» bajo 980—, la version en gris tenue y «Cerrar sesión» como
   TEXTO, sin caja. El nombre de la empresa NO va aqui: se queda donde «.pe-barra» ya lo pinta, y
   asi se lee UNA vez. Va ANTES del @media a proposito: misma especificidad, gana el ULTIMO. */
.portal-top{ display:none; align-items:center; gap:var(--e3);
  padding:var(--e2) var(--e3); background:var(--navy); color:var(--surface) }
.portal-top .portal-top-logo{ background:none; border:0; padding:0; cursor:pointer; margin-right:auto }
.portal-top .portal-top-logo img{ height:24px; display:block }
.portal-top .portal-ver{ font-size:var(--fs-xs) }
/* Cuatro renglones, y hacen falta: la casa NO tiene un boton de texto sobre navy — «.btn.ghost»
   es el blanco con marco y «.linklike» es navy sobre blanco, que sobre navy no se ve. */
.portal-salir{ background:none; border:0; padding:4px 0; margin:0; font:inherit;
  font-size:var(--fs-sm); color:var(--on-navy); cursor:pointer }
.portal-salir:hover{ color:var(--surface); text-decoration:underline }

/* ── EL TELEFONO · la columna se vuelve una barra ABAJO (J, 9-sep · corte 980, casilla 4) ─────
   El corte es el MISMO que «.shell» usa arriba para esta misma decision: no se estrena numero.
   «position:fixed» y el «padding-bottom» del contenido van JUNTOS: una barra fija sin colchon
   tapa el ultimo renglon de la tabla, y esa es la rotura «colchon». */
@media(max-width:980px){
  .portal-shell{ flex-direction:column }
  .portal-nav{ position:fixed; top:auto; bottom:0; left:0; right:0; width:auto; height:auto;
    flex-direction:row; padding:0; overflow:visible; z-index:30;
    border-top:1px solid rgba(255,255,255,.18) }
  .portal-logo{ display:none }
  .portal-items{ flex-direction:row; flex:1; gap:0 }
  .portal-items .navitem{ flex:1; text-align:center; padding:6px 2px 7px; border-radius:0;
    font-size:var(--fs-xs); line-height:1.15 }
  .portal-ic{ display:block; font-size:var(--fs-md); line-height:18px }
  .portal-foot{ display:none }
  .portal-main{ padding-bottom:59px }
  .portal-top{ display:flex }
}

/* Fusionar categorías (depurar duplicados) */
.fus-box{ border:1px solid var(--line); border-radius:var(--r-md); padding:12px 14px; margin:16px 0 4px; background:var(--light) }
.fus-title{ font-size:var(--fs-base); font-weight:600; color:var(--text); margin-bottom:8px }
.fus-row{ display:flex; align-items:center; gap:8px; flex-wrap:wrap }
.fus-row select{ flex:1; min-width:150px }
.fus-arrow{ font-size:var(--fs-sm); color:var(--gray); white-space:nowrap }

/* Detalle de operación: encabezado fijo + input de costo (antes inline) */
.opd-head{ position:sticky; top:0; z-index:5; background:var(--surface); padding:8px 0 6px; margin-bottom:8px; border-bottom:1px solid var(--line); display:flex; flex-direction:column; gap:2px }
.opd-head h3{ font-size:var(--fs-lg) }
.opc-costo{ width:110px; padding:5px 8px; border:1px solid var(--line); border-radius:var(--r-sm); font:inherit; font-size:var(--fs-base) }

/* Detalle de operación: tabla única de conceptos */
.opc-table{ width:100%; border-collapse:collapse; font-size:var(--fs-base); font-variant-numeric:tabular-nums; margin-top:4px }
.opc-table th{ font-size:var(--fs-xs); text-transform:uppercase; letter-spacing:.5px; color:var(--gray); font-weight:600; text-align:left; padding:0 6px 6px }
.opc-table td{ padding:5px 6px; border-top:1px solid var(--line) }
.opc-table thead th{ border:none }
.opc-table .num{ text-align:right }
.opc-table td.num .opc-costo{ width:92px; text-align:right }
.opc-table .opc-hon td{ border-top:1px solid var(--line) }
.opc-table .opc-tot td{ border-top:2px solid var(--navy); font-weight:600 }
.opc-table .mini.del{ padding:2px 7px }
.opc-saldo{ display:flex; justify-content:space-between; align-items:center; gap:12px; flex-wrap:wrap; margin-top:8px; padding-top:8px; border-top:1px solid var(--line); font-size:var(--fs-base); font-variant-numeric:tabular-nums }

/* Movimientos: anchos de columnas + Monto como columna única */
.dtable th.mc-monto, .dtable td.mc-monto{ white-space:nowrap; font-variant-numeric:tabular-nums; font-weight:500 }
.dtable th.mc-fecha, .dtable td.mc-fecha{ white-space:nowrap }
.dtable th.mc-folio, .dtable td.mc-folio{ white-space:nowrap }
.dtable td.mc-cuenta{ white-space:nowrap; max-width:150px; overflow:hidden; text-overflow:ellipsis }
.dtable td.desc{ max-width:300px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
/* CSS-5 · sin respaldo a propósito: `--text-secondary` ya se declara en tokens.css, y un
   `var(--x, algo)` esconde que el token no exista. Si un día esto se ve negro, el token se cayó. */
.opc-table .opc-sub td{ border-top:1px solid var(--line); font-weight:500; color:var(--text-secondary) }

/* Realtime: aviso de cambios de otros usuarios */
.rt-banner{ background:var(--warn-bg); color:var(--warn); border:1px solid var(--warn-border); border-radius:var(--r-md); padding:8px 12px; margin:8px 0; font-size:var(--fs-base); cursor:pointer; text-align:center }
.rt-banner:hover{ filter:brightness(.97) }

/* Cotización: banner de estado (editando / bloqueada) */
.cotiz-estado{ padding:8px 12px; border-radius:var(--r-md); font-size:var(--fs-base); margin-bottom:14px }
.cotiz-estado.edit{ background:var(--warn-bg); color:var(--warn); border:1px solid var(--warn-border) }
.cotiz-estado.bloq{ background:var(--light); color:var(--text); border:1px solid var(--line) }

/* Cotizacion bloqueada: el aviso ya existia, pero los campos seguian viendose blancos y con cursor
   de texto, invitando a teclear en vano. Se apagan visualmente. Es CSS y no JS a proposito: la
   tabla de conceptos se vuelve a dibujar en cada refresh y una marca puesta elemento por elemento
   se perderia en el siguiente render. Todo con tokens (DISENO.md: nunca hex ni px sueltos). */
.locked-form input,
.locked-form select,
.locked-form textarea{
  background:var(--light);
  color:var(--gray);
  border-color:var(--line);
  cursor:not-allowed;
  pointer-events:none
}
.locked-form .btn{ pointer-events:auto }

/* Contador de la bandeja de Solicitudes en el menu lateral. */
/* Bloque con borde de acento. El patrón ya vivía suelto en tres lugares (dos INLINE en Diagnóstico
   y el aviso del catálogo), así que convertirlo en componente además de unificarlo BAJA los estilos
   inline que vigila el trinquete. Limpiar y embellecer resultaron ser el mismo trabajo.
   El estado limpio (.ok) deja el fondo neutro a propósito: si los quince chequeos de Diagnóstico
   salieran verdes, un muro de verde no comunica nada. El tinte se reserva para "mira aquí". */
.acc{ border-left:3px solid var(--line); background:var(--light);
      padding:var(--sp-2) var(--sp-3); margin-bottom:var(--sp-3);
      border-radius:0 var(--r-md) var(--r-md) 0 }
.acc > b{ color:var(--text) }
.acc.ok{ border-left-color:var(--ok) }
.acc.ok > b{ color:var(--ok) }
.acc.warn{ border-left-color:var(--warn); background:var(--warn-bg) }
.acc.warn > b{ color:var(--warn) }
.acc.danger{ border-left-color:var(--danger); background:var(--danger-bg) }
.acc.danger > b{ color:var(--danger) }
.acc.sm{ font-size:var(--fs-sm); padding:var(--sp-2) var(--sp-3); margin-bottom:var(--sp-2) }
.acc ul{ margin:var(--sp-1) 0 0; padding-left:18px; font-size:var(--fs-base) }
.acc .err-msg{ color:var(--danger) }

/* Catálogo de cuentas: pista bajo cada campo y aviso de coherencia código↔naturaleza. */
.cat-hint{ font-size:var(--fs-xs); color:var(--gray); margin-top:var(--sp-1) }
.cat-aviso{ display:none; margin-top:var(--sp-3); padding:var(--sp-2) var(--sp-3);
  border-radius:0 var(--r-sm) var(--r-sm) 0; font-size:var(--fs-sm) }
.cat-aviso.warn{ display:block; background:var(--warn-bg); color:var(--warn); border-left:3px solid var(--warn) }
.cat-aviso.ok{ display:block; background:var(--ok-bg); color:var(--ok); border-left:3px solid var(--ok) }
.navcount{
  display:inline-block;
  background:var(--danger);
  color:var(--surface);
  font-size:var(--fs-xs);
  font-weight:700;
  border-radius:var(--r-lg);
  padding:0 var(--sp-2);
  margin-left:var(--sp-1);
  vertical-align:middle
}

/* Cotización: columna de Costo interno (aislada visualmente del precio al cliente) */
.form th.col-costo, .form td.col-costo{ border-left:1px solid var(--line) }
.con-cat{ border:0.5px solid var(--line); border-radius:12px; overflow:hidden }
.con-row{ display:flex; align-items:center; justify-content:space-between; padding:10px 14px; border-bottom:0.5px solid var(--line); cursor:pointer; font-size:var(--fs-md) }
.con-cat > .con-row:last-child{ border-bottom:none }
.con-row:hover{ background:var(--light) }
.con-row-r{ display:flex; align-items:center; gap:8px; color:var(--gray) }
.con-tag{ font-size:var(--fs-xs); background:var(--light); color:var(--gray); padding:2px 8px; border-radius:6px }
.con-panel{ padding:10px 14px 14px; background:var(--light); border-bottom:0.5px solid var(--line); display:grid; grid-template-columns:1fr 1fr; gap:10px 16px }
.con-f{ display:flex; flex-direction:column; gap:3px; font-size:var(--fs-sm); color:var(--gray); margin:0 }
.con-f input, .con-f select{ font-size:var(--fs-md) }
.con-chk{ display:flex; align-items:center; gap:8px; font-size:var(--fs-base); color:var(--text); width:auto; margin:0 }
.con-chk input{ width:auto }
.con-btns{ grid-column:1/-1; display:flex; gap:8px; margin-top:2px }
.con-row-r{ display:flex; align-items:center; gap:8px }
.seg3{ display:grid; grid-template-columns:1fr 1fr 1fr; border:1px solid var(--line); border-radius:8px; overflow:hidden }
.seg3 button{ border:none; background:var(--surface); padding:9px 4px; font-size:var(--fs-base); color:var(--gray); cursor:pointer; border-right:1px solid var(--line) }
.seg3 button:last-child{ border-right:none }
.seg3 button.on{ background:var(--navy); color:var(--surface); font-weight:500 }
.seg3 button[data-v="favor"].on{ background:var(--ok) }
.seg3 button[data-v="contra"].on{ background:var(--danger) }
.cotiz-tot{ margin-top:10px }
.ct-box{ border:0.5px solid var(--line); border-radius:8px; padding:8px 12px; max-width:280px; margin-left:auto }
.ct-row{ display:flex; justify-content:space-between; font-size:var(--fs-base); padding:3px 0 }
.ct-row span{ color:var(--gray) }
.ct-tot{ border-top:1px solid var(--line); margin-top:2px; padding-top:6px; font-weight:500 }
.ct-tot b{ font-size:var(--fs-md) }
.col-costo-note{ font-weight:400; font-size:var(--fs-xs); color:var(--warn); text-transform:none; letter-spacing:0 }

/* Cotización: banner de borrador recuperable */
.cotiz-draft{ background:var(--ok-bg); color:var(--ok); border:1px solid var(--ok-border); padding:8px 12px; border-radius:var(--r-md); font-size:var(--fs-base); margin-bottom:14px }
.cotiz-draft .linklike{ color:var(--ok) }

/* Catálogos: condiciones por defecto */
.cfg-box{ border:1px solid var(--line); border-radius:var(--r-md); padding:14px; margin-top:18px; background:var(--light) }

/* Aviso de fallo dentro de un panel (p.ej. el historial que no se pudo consultar).
   Existe para no pintar el fallo como "no hay nada" — ver regla 2 de CLAUDE.md. */
.msg-error {
  font-size:var(--fs-sm);
  margin:4px 0 0;
  color:var(--danger)
}

/* Panel visible solo para admin. Distinto de .admin-solo (que es inline-flex, para botones). */
.admin-solo-bloque{display:none}
body.es-admin .admin-solo-bloque{display:block}

/* ── Fase 2 (3-ago): clases para Márgenes y la captura de conceptos ──────────
   Nacieron de mover estilos inline a CSS mientras se arreglaba Márgenes; el
   trinquete bajó de 181 a 178. No inventan colores: usan los tokens de siempre. */
/* La nota que explica una sección vacía, y la insignia de un borrador (12-ago). Van en CSS
   porque `estilosInline` es trinquete: cada `style=` nuevo sube un número que sólo puede bajar. */
.sec-nota{ margin:4px 0 0 }
.badge.es-borrador{ opacity:.7 }
.cm-nota{ margin-top:8px; font-size:var(--fs-sm) }
.cm-nota.tight{ margin-top:4px }
.opc-addrow{ display:flex; gap:var(--sp-2); margin-top:6px; align-items:center; flex-wrap:wrap }
/* El tick «IVA 16% incluido» de la fila de captura (IVA-1, 11-ago). Vive aquí y no en un
   `style=` porque `estilosInline` es trinquete y sólo puede bajar. */
.opc-iva{ display:flex; align-items:center; gap:6px; font-size:var(--fs-sm); white-space:nowrap }
.opc-iva input{ width:auto }
.w-auto{ width:auto }
.mg-cob{ color:var(--ok) }
.mg-cos{ color:var(--danger) }
.mg-tot td{ border-top:2px solid var(--line) }
/* De quién es el renglón. Verde = del cliente (pass-through, no es tu dinero);
   ámbar = de la agencia (costo propio, sí lleva margen); gris = sin clasificar. */
.cfdi-tag{ font-size:11px; padding:1px 7px; border-radius:20px; border:1px solid var(--line);
           background:var(--surface); color:var(--gray); cursor:pointer; white-space:nowrap }
.cfdi-tag.cli{ background:var(--ok-bg); color:var(--ok); border-color:var(--ok) }
.cfdi-tag.age{ background:var(--warn-bg); color:var(--warn); border-color:var(--warn) }
.opc-del{ border:none; background:none; color:var(--danger); cursor:pointer; font-size:11px }
/* El ✎ que corrige el importe de un renglón (12-ago). Gris y no rojo: el rojo de `.opc-del` dice
   «esto destruye», y corregir un importe no destruye nada — el renglón sigue ahí. Vive aquí y no
   en un `style=` porque `estilosInline` es trinquete y sólo puede bajar. */
.opc-ed{ border:none; background:none; color:var(--gray); cursor:pointer; font-size:11px }
.ren-inp{ width:110px }
.opc-del-x{ padding:0 2px; line-height:1; vertical-align:middle }
.c-warn{ color:var(--warn) }
.c-ok{ color:var(--ok) }
.c-danger{ color:var(--danger) }

/* ── D-6 · La columna «Diferencia» del detalle de la operación ──────────────────────────────
   No se llama «Margen» a propósito: lo que significa la diferencia entre lo cobrado y lo pagado
   depende de a nombre de quién está el CFDI del proveedor. Por eso la palabra va DENTRO de cada
   celda («margen», «a devolver», «falta cobrar») y no en el encabezado — el encabezado tendría
   que mentir en la mitad de los renglones. */
.dif-q{ font-style:normal; font-size:11px; opacity:.75 }   /* la palabra que califica al número */
.dif-sum{ font-size:12px; margin:6px 0 0; line-height:1.5 } /* el resumen por bolsa, debajo de la tabla */
.dif-nota{ margin-top:6px; font-size:12px }                 /* el recordatorio del importe exacto */
.st-nota{ font-size:10px }                                  /* la aclaración chica dentro de una tarjeta */

/* D-3: el título de la pantalla en la barra sticky, donde antes vivían las subpestañas. */
/* Título de pantalla + su acción, en una fila. Es el patrón que ya usaban Accesos › Empleados
   y Catálogos › Conceptos con estilos inline; ahora tiene nombre y se puede reusar. */
.pagehead{ display:flex; align-items:baseline; justify-content:space-between; gap:12px;
            flex-wrap:wrap; margin-bottom:14px }
/* El h2 cede su margen a la fila — si no, el margen se suma y el título queda flotando.
   Pero la fila SÍ conserva los 14px que tenía `.histwrap h2`: quitarlos dejaba el contenido
   pegado al título (lo cazó J el 3-ago). */
.pagehead h2{ margin-bottom:0 }
.mb10{ margin-bottom:var(--e3) }   /* era 10px · sube a 12 */
.chkline{ display:flex; align-items:center; gap:8px; font-size:var(--fs-md); color:var(--text); margin-bottom:8px }
.chkline input{ width:auto }
.m0{ margin:0 }

/* Acciones siempre visibles: la tabla se desplaza en horizontal y los botones del final quedaban
   cortados (lo marcó J el 3-ago). Es el espejo del `position:sticky` que la primera columna ya
   tenía a la izquierda — mismo patrón, no uno nuevo. */
.dtable thead th.act{ position:sticky; right:0; z-index:3; background:var(--light) }
.dtable tbody td.act{ position:sticky; right:0; z-index:1; background:var(--surface);
                      box-shadow:-6px 0 6px -6px rgba(0,0,0,.12) }
.dtable tbody tr:hover td.act{ background:var(--light) }
/* Utilidad para ocultar sin estilo inline — el trinquete cuenta los inline, y esconder un panel
   es de las cosas que más los generan. */
.is-hidden{ display:none !important }

/* D-12 · El ⋮ del renglón y su menú flotante. El menú va en `position:fixed` porque la tabla
   vive en un contenedor con overflow:auto que recortaría cualquier hijo posicionado. */
.rowmenu{ border:1px solid transparent; background:none; color:var(--gray); cursor:pointer;
          border-radius:7px; padding:4px 9px; font-size:var(--fs-lg); line-height:1 }
.rowmenu:hover{ background:var(--light); border-color:var(--line); color:var(--navy) }
.rowmenu-pop{ position:fixed; z-index:200; background:var(--surface); border:1px solid var(--line);
              border-radius:9px; box-shadow:0 8px 24px rgba(0,0,0,.14); padding:4px; min-width:150px }
.rowmenu-op{ display:block; width:100%; text-align:left; background:none; border:none; cursor:pointer;
             font:inherit; font-size:var(--fs-base); color:var(--text); padding:8px 12px; border-radius:6px }
.rowmenu-op:hover{ background:var(--light) }
.rowmenu-op.danger{ color:var(--danger) }
.rowmenu-op.danger:hover{ background:var(--danger-bg) }

/* ── Detalle de operación en pestañas (D-3/D-11, 3-ago) ──────────────────────
   El encabezado NO va detrás de una pestaña: referencia, cliente, estado y saldo
   son los datos que se consultan de pasada, y esconderlos obligaría a recordar en
   qué pestaña viven. El saldo va por moneda: nunca un número que mezcle cubetas. */
.opd-badges{ display:flex; gap:6px; flex-wrap:wrap; margin-bottom:6px }
.opd-saldo{ display:flex; align-items:baseline; gap:14px; flex-wrap:wrap;
            background:var(--light); border-radius:9px; padding:7px 13px; margin-bottom:12px }
.opd-saldo .l{ font-size:var(--fs-xs); letter-spacing:.08em; text-transform:uppercase;
               color:var(--gray); font-weight:700 }
.opd-saldo .v{ font-family:ui-monospace,Menlo,monospace; font-size:var(--fs-lg); font-weight:700 }
.opd-saldo .v em{ font-style:normal; font-family:inherit; font-size:var(--fs-xs);
                  color:var(--gray); font-weight:400; margin-left:3px }
.opd-saldo .v.neg{ color:var(--danger) }
.opd-saldo .v.pos{ color:var(--warn) }
.opd-saldo .v.ok{ color:var(--ok) }

/* Las pestañas se perdían (J, 3-ago): texto gris claro, sin fondo, y con cinco cosas encima.
   Ahora tienen su propia barra blanca, las inactivas están en color de texto —no en gris— y la
   activa se marca con fondo Y subrayado grueso, no solo con una línea fina. */
.opd-tabs{ display:flex; gap:4px; background:var(--surface); border:1px solid var(--line);
           border-radius:10px; padding:4px; margin-bottom:18px; flex-wrap:wrap }
.opd-tab{ font:inherit; font-size:var(--fs-md); font-weight:600; background:none; border:none;
          cursor:pointer; padding:9px 18px; color:var(--text); border-radius:7px;
          border-bottom:3px solid transparent; transition:.12s }
.opd-tab:hover{ background:var(--light); color:var(--navy) }
.opd-tab.on{ color:var(--navy); font-weight:700; background:var(--light);
             border-bottom-color:var(--navy) }

/* Campos de la pestaña Operativa. Lo que falta se ve — no se esconde. */
.opd-grid3{ display:grid; grid-template-columns:repeat(auto-fit,minmax(190px,1fr)); gap:12px 18px }
.opd-f .k{ font-size:var(--fs-xs); letter-spacing:.05em; text-transform:uppercase;
           color:var(--gray); font-weight:700; margin-bottom:1px }
.opd-f .v{ font-size:var(--fs-md) }
.opd-f .v.empty{ color:var(--gray-soft); font-style:italic; font-size:var(--fs-base) }
.opd-notas{ font-size:var(--fs-md); background:var(--light); border-radius:8px; padding:10px 13px }

/* ══════════════════════════════════════════════════════════════════════════════════════════════
   1d · LA PESTAÑA OPERACIÓN (28-ago) · la franja, el embarque, la línea de tiempo y el café
   Cero hex y cero px fuera de la escala: todo pasa por token (DISEÑO.md · regla de oro).

   LAS CUATRO AUSENCIAS SE VEN DISTINTO PORQUE SON CUATRO COSAS DISTINTAS (maqueta §4):
     ◌ falta  ámbar   → existe y no se ha capturado. Pide acción.
     n/a      gris    → no aplica aquí. No pide nada. (Un ETA de BL en una aérea.)
     ?        gris    → depende de algo que tampoco se sabe. Ni «falta» ni «n/a» serían ciertos.
     (vacío)  —       → el `.v.empty` de siempre, que se conserva para los campos de identidad.
   Hasta hoy las cuatro se dibujaban igual («—») y por eso la pantalla pedía buque en 36 aéreas.
   ══════════════════════════════════════════════════════════════════════════════════════════════ */
.opd-f .v.falta{ color:var(--warn); font-weight:600; font-size:var(--fs-base) }
.opd-f .v.na{    color:var(--gray-soft); font-size:var(--fs-base) }
.opd-f .v.indef{ color:var(--gray-soft); font-size:var(--fs-base) }
.opd-porque{ display:block; font-size:var(--fs-xs); color:var(--gray); font-weight:400 }
/* Un campo con dato que NO pertenece al modo declarado es un defecto de datos, y se ve. */
.opd-ajeno{ display:inline-block; margin-left:var(--sp-2); font-size:var(--fs-xs); font-weight:700;
            color:var(--warn); background:var(--warn-bg); border:1px solid var(--warn-border);
            border-radius:var(--r-pill); padding:0 var(--sp-2) }

/* La franja: lo accionable no puede caer debajo del pliegue (condición del orden que pidió J). */
.opd-franja{ display:flex; flex-wrap:wrap; gap:var(--e3); align-items:center;
             background:var(--light); border:1px solid var(--line); border-radius:var(--r-caja);
             padding:var(--sp-2) var(--e3); margin-bottom:var(--e3); font-size:var(--fs-base) }
.opd-fr-dias b{ font-variant-numeric:tabular-nums }
.opd-fr-mal{ color:var(--danger); font-weight:600 }
.opd-fr-mal b{ font-variant-numeric:tabular-nums }
.opd-fr-falta{ color:var(--warn) }
.opd-fr-clv{ font-family:var(--font-mono); font-size:var(--fs-sm); color:var(--navy);
             background:var(--surface); border:1px solid var(--line);
             border-radius:var(--r-pill); padding:0 var(--sp-2) }
.opd-fr-pend{ margin-left:auto }

/* La línea de tiempo. El orden es el del PROCESO, no el de las fechas: ordenar por fecha volvería
   invisible justo lo que esto existe para ver (las 2 operaciones con las fechas al revés). */
.opd-tl{ margin-top:var(--sp-1) }
.opd-ev{ display:grid; grid-template-columns:84px 18px 1fr; align-items:start }
.opd-ev .fe{ font-size:var(--fs-sm); color:var(--gray); text-align:right;
             padding:var(--sp-1) var(--sp-2) 0 0; white-space:nowrap;
             font-variant-numeric:tabular-nums }
.opd-ev .ln{ position:relative }
.opd-ev .ln:before{ content:""; position:absolute; left:7px; top:0; bottom:0; width:2px;
                    background:var(--line) }
.opd-ev:first-child .ln:before{ top:10px }
.opd-ev:last-child .ln:before{ bottom:auto; height:10px }
.opd-ev .dot{ position:absolute; left:var(--sp-1); top:var(--sp-1); width:var(--e3); height:var(--e3);
              border-radius:var(--r-pill); background:var(--surface);
              border:2px solid var(--gray-soft) }
.opd-ev.ok    .dot{ background:var(--ok);   border-color:var(--ok) }
.opd-ev.falta .dot{ background:var(--surface); border-color:var(--warn); border-style:dashed }
.opd-ev.na    .dot{ background:var(--light); border-color:var(--line) }
.opd-ev.indef .dot{ background:var(--light); border-color:var(--gray-soft); border-style:dotted }
.opd-ev .cu{ padding:2px 0 var(--e3) var(--e3) }
.opd-ev .ti{ font-size:var(--fs-md); font-weight:600 }
.opd-ev.na .ti, .opd-ev.indef .ti{ color:var(--gray-soft); font-weight:400 }
.opd-ev .de{ font-size:var(--fs-sm); color:var(--gray) }
.opd-tl-falta{ color:var(--warn); font-weight:700; font-size:var(--fs-sm) }
.opd-tl-na{    color:var(--gray-soft); font-weight:700; font-size:var(--fs-sm) }
.opd-tl-indef{ color:var(--gray-soft); font-weight:700; font-size:var(--fs-sm) }
.opd-tl-mal{ color:var(--danger) }
/* El folio de la factura NO va en café aunque sea un folio: J decidió (26-ago) que QUE EXISTA la
   factura es del cliente —su folio y su método fiscal son suyos— y lo único que baja al café es el
   IMPORTE. Un chip café aquí diría con el color lo contrario de lo que dice la decisión. */
.opd-tl-folio{ font-family:var(--font-mono); font-size:var(--fs-sm); color:var(--navy);
               background:var(--light); border:1px solid var(--line);
               border-radius:var(--r-pill); padding:0 var(--sp-2) }

/* EL MUEBLE CAFÉ · la familia `--interno-*` de tokens.css (sembrada el 24-ago), en su primera
   PANTALLA. Medido antes de escribir esto, porque «sin estrenar» era falso: `.me-sello-doc` ya la
   usaba —3 usos, `-bg`, `-ink` y `-line`, un chip dentro del modal del movimiento—. Lo que de
   verdad estaba a CERO es `--interno-row`, y lo estrena `.opd-int-notas` aquí abajo.
   Café = lo interno de la agencia, lo que el portal jamás recibe: alias, notas y el IMPORTE del
   honorario (J, 26-ago). Es VOCABULARIO, no decoración: navy+blanco dice «del cliente». */
.opd-interno{ background:var(--interno-bg); border:1px solid var(--interno-line);
              border-radius:var(--r-caja); padding:var(--sp-2) var(--e3);
              margin-top:var(--e5); color:var(--interno-ink) }
.opd-interno .et{ display:block; font-size:var(--fs-rotulo); text-transform:uppercase;
                  letter-spacing:.06em; font-weight:800; color:var(--interno-ink);
                  margin-bottom:var(--sp-2) }
.opd-interno .opd-f .k{ color:var(--interno-ink) }
.opd-int-notas{ margin-top:var(--sp-2); background:var(--interno-row);
                border-radius:var(--r-md); padding:var(--sp-2) var(--e3); font-size:var(--fs-base) }
.opd-danger{ margin-top:26px; border-top:1px solid var(--line); padding-top:12px }
.btn-danger{ color:var(--danger); border-color:var(--danger-border) }
.mt16{ margin-top:var(--e4) }
/* El estatus de la cotización, visual: el propio selector lleva el color de su estado. Se sigue
   pudiendo cambiar en la lista — no se pierde nada por hacerlo legible de un vistazo. */
.hstatus.est-pend{ background:var(--warn-bg); color:var(--warn); border-color:var(--warn-border) }
.hstatus.est-apro{ background:var(--ok-bg);   color:var(--ok);   border-color:var(--ok-border) }
.hstatus.est-rech{ background:var(--danger-bg);color:var(--danger);border-color:var(--danger-border) }
.hstatus{ font-weight:600 }

/* D-4 · Bloque de cambios entre versiones, en el documento del cliente. */
.cotcam{ border-radius:8px; padding:12px 14px; margin:0 0 16px; font-size:var(--fs-base) }
.cotcam.up{   background:var(--warn-bg);   border:1px solid var(--warn-border) }
.cotcam.down{ background:var(--ok-bg);     border:1px solid var(--ok-border) }
.cotcam.mix{  background:var(--light);     border:1px solid var(--line) }
.cotcam h4{ margin:0 0 7px; font-size:var(--fs-sm); letter-spacing:.02em }
.cotcam.up h4{ color:var(--warn) } .cotcam.down h4{ color:var(--ok) } .cotcam.mix h4{ color:var(--navy) }
.cotcam-cur{ font-size:var(--fs-xs); letter-spacing:.07em; text-transform:uppercase; font-weight:700;
             color:var(--gray); margin:9px 0 3px; padding-bottom:2px; border-bottom:1px solid rgba(0,0,0,.09) }
.cotcam-l{ display:flex; justify-content:space-between; gap:12px; padding:2px 0 }
.cotcam-l .v{ font-family:ui-monospace,Menlo,monospace; font-size:var(--fs-sm); white-space:nowrap }
.cotcam-l .v em{ font-style:normal; color:var(--gray); font-size:var(--fs-xs) }
.cotcam-l.tot{ border-top:1px solid rgba(0,0,0,.12); margin-top:6px; padding-top:6px; font-weight:700 }
.cotcam .up-v{ color:var(--warn); font-weight:700 }
.cotcam .down-v{ color:var(--ok); font-weight:700 }
.cotcam-why{ margin-top:9px; padding-top:8px; border-top:1px dashed rgba(0,0,0,.12);
             font-size:var(--fs-sm); color:var(--gray); font-style:italic }
/* Cada opción del ⋮ puede llevar su renglón de explicación: «poco intuitivo» casi siempre se
   arregla con una línea, no buscando una palabra mejor. */
.rowmenu-op em{ display:block; font-style:normal; font-size:var(--fs-xs); color:var(--gray);
                margin-top:1px; line-height:1.35; white-space:normal }
.rowmenu-pop{ min-width:230px; max-width:280px }

/* El puente del saldo: la app calculaba el número y no decía qué significaba. Sobre todo el
   renglón final — ese remanente no es ganancia, es dinero del cliente (D-6). */
.opd-puente{ background:var(--light); border-radius:9px; padding:12px 15px; margin:10px 0 4px }
.opd-puente .pr{ display:flex; justify-content:space-between; gap:12px; padding:3px 0;
                 font-size:var(--fs-base) }
.opd-puente .pr .n{ font-family:ui-monospace,Menlo,monospace }
.opd-puente .pr.tot{ border-top:1px solid var(--line); margin-top:6px; padding-top:7px;
                     font-weight:700; color:var(--navy) }
/* Campo fijo al corregir una cotización: se ve, se lee, no se toca. Esconderlo sería peor —
   parecería que el dato no existe en vez de que está fijo a propósito. */
.campo-fijo{ background:var(--light); color:var(--gray); cursor:not-allowed }
/* D-15 · La comisión del renglón: verde si te la pagan, rojo si la pagas. */
/* A4 · el fondo de color se fue: decía «lo pagas tú» y se leía «está bloqueado». Lo dice la
   etiqueta `.com-eti`, con palabras. El campo se ve como cualquier otro campo, porque lo es. */
.com-favor, .com-contra{ background:#fff }
.com-eti{ display:block; font-size:10px; line-height:1.3; letter-spacing:.3px;
          text-transform:uppercase; color:var(--gray); margin-bottom:2px; white-space:nowrap }
.com-eti.favor{ color:var(--ok) }
.com-eti.contra{ color:var(--danger) }
.com-eti.sin{ text-transform:none; letter-spacing:0; font-size:11px; font-style:italic }
.com-hint{ font-size:var(--fs-xs); color:var(--gray); margin-top:2px; white-space:nowrap }

/* D-15 · Comisiones dentro de la operación. Aparte del puente del saldo a propósito: el puente
   habla del dinero del cliente y esto es lo contrario — ingreso propio que no sale de él. */
.opd-com{ border:1px solid var(--line); border-left:3px solid var(--navy); border-radius:0 9px 9px 0;
          padding:11px 14px; margin:12px 0 4px; background:var(--surface) }
.opd-com .t{ font-size:var(--fs-xs); letter-spacing:.06em; text-transform:uppercase;
             font-weight:700; color:var(--navy) }
.opd-com .sub{ font-size:var(--fs-sm); color:var(--gray); margin:2px 0 7px }
.com-row{ display:flex; justify-content:space-between; gap:12px; padding:4px 0;
          border-top:1px solid var(--line); font-size:var(--fs-base) }
.com-row .n{ font-family:ui-monospace,Menlo,monospace; white-space:nowrap }
.cf-falta{ color:var(--warn); font-size:var(--fs-xs) }

/* ── Contrapartes: los chips de las dos listas ──────────────────────────────
   `dual` marca que la MISMA identidad tiene los dos sombreros. Se pinta gris,
   no de color de alerta: no es un problema que resolver, es información —
   evita que alguien vea el mismo nombre en Clientes y en Proveedores y
   «limpie el duplicado». `portal` sí va en verde: es un estado, hay acceso
   vivo del otro lado. */
.chip{ display:inline-block; font-size:var(--fs-xs); font-weight:700;
       padding:1px 7px; border-radius:20px; margin-left:6px;
       border:1px solid var(--line); vertical-align:middle; white-space:nowrap }
.chip.dual  { background:var(--light);  color:var(--navy); border-color:var(--line) }
.chip.portal{ background:var(--ok-bg);  color:var(--ok);   border-color:var(--ok-border) }
/* Vista oculta al cargar. NO usa `.is-hidden` porque ésa lleva `!important` y le
   ganaría al `display:block` que `tab()` pone en línea. Sin clase, la única
   alternativa era `style="display:none"` — un inline más, que es justo lo que la
   métrica `estilosInline` está frenando. */
.view-oculta{ display:none }
.cp-aviso{ margin-bottom:10px }
/* CSF en el alta de proveedores. Clases en vez de `style="…"` en línea: la métrica
   `estilosInline` es un trinquete y copiar el marcaje de Clientes tal cual habría
   subido la cuenta en tres. Las de Clientes siguen en línea porque cambiarlas ahí
   es un refactor aparte, no parte de esta pantalla (regla 12). */
.csf-label{ font-size:var(--fs-sm); color:var(--gray); margin:0 0 4px; display:block }
.csf-file{ display:none }
.csf-oculto{ display:none; margin-bottom:8px }
/* Renglón que se abre al hacerle clic, como en Operaciones y Movimientos.
   Más específico que `.dtable tbody tr{cursor:default}`, que es la regla general. */
.dtable tbody tr.cprow{ cursor:pointer }
/* El motivo del cambio, al corregir una cotización. Marco propio para que no se
   lea como un campo más: es lo único que explica los importes que se movieron. */
.motivo-box{ border-left:3px solid var(--warn); background:var(--warn-bg);
             border-radius:0 8px 8px 0; padding:10px 12px; margin:0 0 12px }
.motivo-box .flab{ color:var(--warn) }
.motivo-box input{ margin-bottom:0 }
/* COT-R1 · una cotización que ya tiene una versión más nueva. Se atenúa en vez de
   esconderse: lo que se aprobó en su día sigue siendo parte de la historia, y el
   cliente puede querer verlo. El puntero dice cuál manda HOY, no cuál sigue. */
.sust-note{ font-size:var(--fs-xs); color:var(--warn); font-weight:600; white-space:nowrap }
.es-sustituida td, .es-sustituida .hinfo{ opacity:.62 }
.es-sustituida .sust-note{ opacity:1 }
/* Ficha de contraparte. Reusa .opd-* del detalle de operación para que se vea y se
   comporte igual que el resto de la app. */
.fi-crumb{ font-size:var(--fs-xs) }
/* La pestaña que no aplica no se muestra vacía: se OFRECE (D-1). Por eso se ve como
   acción y no como lugar. */
.opd-tab.fi-add{ color:var(--navy); font-weight:600; opacity:.8 }
.opd-tab.fi-add:hover{ opacity:1 }
/* Anunciada y apagada. Mostrar «0/11» acusaría de incumplir a quien tiene su
   expediente completo en papel. */
.opd-tab.fi-off{ color:var(--gray); opacity:.55; font-weight:600 }
.opd-f input, .opd-f select{ width:100%; margin:0 }
/* La columna de portal se alinea al centro y no crece: es un sí/no, no un texto. */
.dtable th.cp-portal, .dtable td.cp-portal{ text-align:center; width:1%; white-space:nowrap }
/* Encabezado de bloque dentro de la tabla de conceptos. Los renglones se agrupan por
   a nombre de quién va el CFDI, y cada bloque nombra sus columnas: significan cosas
   distintas según el grupo (D-6). */
.opc-table tr.opc-grp td{ font-size:var(--fs-xs); letter-spacing:.05em; text-transform:uppercase;
  color:var(--gray); font-weight:700; background:var(--light); border-bottom:1px solid var(--line);
  padding-top:9px; padding-bottom:5px }
.opc-table tr.opc-grp td:first-child{ color:var(--navy) }

/* ════════════════════════════════════════════════════════════════════════════════════
   LA SUBPANTALLA BLANCA · «.screen»                                        (4-ago-2026)
   ────────────────────────────────────────────────────────────────────────────────────
   J, comparando la app contra las maquetas: «el UI que tenemos a la derecha es mil más
   bonito… nuestras pantallas son grises, pero no puede haber una subpantalla blanca
   encuadrada en la gris?».

   Lo interesante del hallazgo: **la paleta ya era la misma**. Las maquetas usan los
   mismos tokens —navy #1C3149, fondo #EEF1F4, línea #E4E8ED— que `tokens.css`. No
   faltaba color: faltaba el MARCO. El contenido se pintaba suelto sobre el gris, sin
   borde y sin sombra, y sin un límite visible todo queda al mismo nivel: no hay
   «adentro» y «afuera», y por eso se lee como una lista larga en vez de una pantalla.

   Tres piezas, y ninguna toca un cálculo:
     · `.screen`    — la tarjeta blanca sobre el gris
     · `.scr-head`  — la banda navy con referencia y cliente
     · `.scr-body`  — el cuerpo, con su propio aire

   ── POR QUÉ NO LLEVA `overflow:hidden` ──────────────────────────────────────────────
   Es lo natural para recortar las esquinas, y está MAL aquí. Un ancestro con overflow
   oculto crea un contenedor de scroll propio, y un hijo `position:sticky` se pega al
   tope de ESE contenedor —que no se desplaza— en vez de al de la ventana. Se ve idéntico
   hasta que haces scroll: ahí el encabezado se va con la página y pierdes la referencia
   y el saldo, que son justo los datos que se consultan de pasada. Las esquinas se
   redondean en los hijos.
   ════════════════════════════════════════════════════════════════════════════════════ */
.screen{ background:var(--surface); border:1px solid var(--line); border-radius:12px;
         box-shadow:0 4px 18px rgba(0,0,0,.06) }

.scr-head{ background:var(--navy); color:var(--surface); padding:13px 20px 12px;
           border-radius:11px 11px 0 0; position:sticky; top:0; z-index:6 }
.scr-head .crumb{ font-size:var(--fs-xs); letter-spacing:.06em; text-transform:uppercase;
                  color:rgba(255,255,255,.55); font-weight:700; margin-bottom:5px }
.scr-head .ref{ font-size:var(--fs-lg); font-weight:700; line-height:1.25 }
/* La maqueta traía la referencia a 20px. Se queda en `--fs-lg` (17px) a propósito: 20 no
   está en la escala de tokens, y el trinquete de métricas cuenta tamaños fuera de escala
   justo para que no se cuelen de uno en uno. La jerarquía aquí la da la inversión de
   color sobre navy, no el tamaño. Si hace falta más peso, se sube el token, no el caso. */
.scr-head .sub{ font-size:var(--fs-sm); color:rgba(255,255,255,.72); margin-top:3px }
/* El segundo renglon —el despacho— va un punto mas tenue que el primero: los dos son datos, pero
   el de arriba dice DE QUIEN es la operacion y ese se busca primero. Y los separadores tambien
   bajan, para que lo que resalte sean los datos y no los puntos. */
.scr-head .sub-op{ color:rgba(255,255,255,.58); margin-top:2px }
.scr-head .tenue{ color:rgba(255,255,255,.45) }
/* Las insignias sobre navy · UN COLOR, UN SIGNIFICADO (13-ago).
   Antes el verde decia tres cosas distintas (facturada, expediente completo, cerrada) y el ambar
   dos («Abierta» y «Lista para facturar»), asi que ninguno decia nada. Ahora:
     · verde        = CERRADA. La unica que significa «no hay nada que hacer aqui», y va sola.
     · contorno     = lo que FALTA. Es trabajo, no alarma: no lleva color porque no esta mal,
                      esta pendiente — y quien mira la pantalla ya sabe distinguirlo (regla P).
     · relleno tenue = una CLASIFICACION («No aduanal»), que no opina sobre nada.
   El fondo oscuro obliga a la version translucida: la paleta de estado no contrasta sobre navy. */
.scr-head .opd-badges{ margin:9px 0 0 }
.scr-head .badge{ background:rgba(255,255,255,.13); color:rgba(255,255,255,.92);
                  border:1px solid rgba(255,255,255,.18) }
.scr-head .badge.cerr{ background:rgba(160,235,196,.18); color:#A8E9C6;
                       border-color:rgba(160,235,196,.38) }
.scr-head .badge.falta{ background:transparent; color:rgba(255,255,255,.80);
                        border-color:rgba(255,255,255,.30) }
/* `apro` y `pend` siguen definidas: las usa la lista de operaciones, que es otra pantalla. */
.scr-head .badge.apro{ background:rgba(160,235,196,.16); color:#A8E9C6;
                       border-color:rgba(160,235,196,.3) }
.scr-head .badge.pend{ background:rgba(247,206,143,.15); color:#F5CE93;
                       border-color:rgba(247,206,143,.3) }
/* AUD2 p2 · la paleta de estado no contrasta sobre navy: la versión translúcida, como sus dos
   hermanas de arriba. */
.scr-head .badge.lista{ background:rgba(195,218,236,.22); color:#C3DAEC;
                        border-color:rgba(195,218,236,.45) }

/* Las pestañas, cuando viven DENTRO de la tarjeta, dejan de ser una barra flotante: se
   pegan al borde y se apoyan en la franja clara, como una carpeta. Pierden el borde y el
   radio propios — dos marcos redondeados, uno dentro del otro, es el ruido que hacía ver
   «improvisado» el recuadro del portal. */
.opd-tabs.in-screen{ border:none; border-bottom:1px solid var(--line); border-radius:0;
                     background:var(--light); margin:0; padding:0 10px; gap:0 }
.opd-tabs.in-screen .opd-tab{ border-radius:0; background:none; padding:11px 16px;
                              color:var(--gray); border-bottom:2.5px solid transparent }
.opd-tabs.in-screen .opd-tab:hover{ background:rgba(255,255,255,.6); color:var(--navy) }
.opd-tabs.in-screen .opd-tab.on{ background:var(--surface); color:var(--navy);
                                 border-bottom-color:var(--navy) }

/* El cuerpo redondea las esquinas de abajo él mismo, ya que `.screen` no recorta. */
.scr-body{ padding:16px 20px 20px; border-radius:0 0 11px 11px }

/* En pantalla chica la tarjeta pierde el aire lateral: el contenido vale más que el marco. */
@media(max-width:640px){
  .scr-head{ padding:11px 14px 10px }
  .scr-head .ref{ font-size:var(--fs-lg) }
  .scr-body{ padding:13px 14px 16px }
  .opd-tabs.in-screen{ padding:0 4px }
  .opd-tabs.in-screen .opd-tab{ padding:10px 11px; font-size:var(--fs-base) }
}

/* ── (2) LAS SECCIONES SE ANUNCIAN ──────────────────────────────────────────────────
   Lo que hace que la maqueta se lea «ordenada» no es que tenga menos información: es
   que la información está AGRUPADA y cada grupo dice su nombre. La etiqueta ya existía;
   lo que le faltaba era aire arriba. Con 8px de margen, la etiqueta quedaba pegada al
   bloque anterior y se leía como parte de él — o sea, no separaba nada. */
/* TANDA 9 · CIERRE: esta segunda definición (deuda vieja de la regla 1) queda ALINEADA con la de
   arriba — micro-rótulo global. Fundirlas en una es limpieza aparte; divergir, ya no pueden. */
.seclabel{ font-size:var(--fs-rotulo); letter-spacing:.06em; font-weight:800; color:var(--navy);
           margin:22px 0 6px }
.seclabel:first-child{ margin-top:2px }
/* Cuando la sección quiere una aclaración, va en el mismo renglón, en minúsculas y sin
   espaciado: es una nota, no un segundo encabezado compitiendo con el primero.
   TANDA 9 · REBOTE R1 (J: «check», 24-ago): el selector se EXTIENDE a `.muted`, que es la clase que
   app.js si escribe (los tres rotulos de `fichaResumenHTML`) — `.n` no se emite ni una vez en el
   repo, asi que esta regla
   llevaba tiempo sin morder y la nota heredaba MAYUSCULAS + .06em + peso 800 del titulo, a 13px
   contra los 10.5 del rotulo: la aclaracion media mas que el encabezado que la manda. La fusion
   global no lo creo, lo destapo. Se extiende en vez de escribir una regla nueva al lado (regla 1:
   una casa) y `.n` queda vivo como vocabulario declarado, no como codigo muerto. */
.seclabel .n, .seclabel .muted{ text-transform:none; letter-spacing:0; font-weight:400;
                               color:var(--gray); font-size:var(--fs-sm) }

/* ── (3) LA TABLA DE CONCEPTOS RESPIRA ──────────────────────────────────────────────
   Tres cambios, ninguno de contenido:
     · el encabezado tiene fondo propio y una línea gruesa abajo → deja de flotar
     · los renglones pasan de 5px a 8px de alto → se pueden seguir con el ojo
     · los importes van en monoespaciado → los pesos y los centavos se alinean solos,
       que es la única razón por la que una columna de dinero se puede comparar de un
       vistazo sin leer cada número. */
.opc-table th{ background:var(--light); border-bottom:2px solid var(--line);
               padding:7px 8px; font-weight:700; letter-spacing:.05em; white-space:nowrap }
.opc-table thead th{ border-bottom:2px solid var(--line) }
.opc-table td{ padding:8px 8px; vertical-align:middle }
.opc-table td.num{ font-family:ui-monospace,Menlo,Consolas,monospace; white-space:nowrap }
.opc-table .opc-sub td, .opc-table .opc-tot td{ background:var(--light) }

/* Las tablas de datos (Movimientos / Operaciones) siguen la misma regla del encabezado:
   mayúsculas pequeñas y línea gruesa. Aquí ya había fondo, faltaba el peso y el remate. */
.dtable th{ font-size:var(--fs-sm); text-transform:uppercase; letter-spacing:.05em;
            font-weight:700; border-bottom:2px solid var(--line) }

/* ── EL EMPUJÓN · «sobra dinero y no hay nada pendiente» ─────────────────────────────
   Se ve como una pregunta, no como una alerta: el fondo es claro y el borde izquierdo
   navy, no ámbar. No hay nada roto — hay una decisión pendiente, y confundir las dos
   cosas es lo que hace que la gente deje de leer los avisos ámbar de verdad. */
.nudge{ background:var(--light); border-left:3px solid var(--navy); border-radius:0 9px 9px 0;
        padding:12px 15px; margin:10px 0 4px }
.nudge .q{ font-size:var(--fs-md); font-weight:700; color:var(--navy) }
.nudge .a{ font-size:var(--fs-base); color:var(--text); margin:3px 0 10px }
.nudge .b{ display:flex; gap:8px; flex-wrap:wrap }
.nudge .n{ font-size:var(--fs-sm); color:var(--gray); margin-top:9px }
/* La pregunta que contesta cada total. Va debajo del nombre, en gris y chiquito: es una
   aclaración, no un segundo encabezado. */
.opc-table .tot-q{ font-size:var(--fs-xs); font-weight:400; letter-spacing:0 }
/* El bloque de «Honorario para factura». Sale un renglón por moneda (F-1): sumar pesos con
   dólares en un solo total es SEM-1, y este número se copia a una factura. */
.hon-factura{ font-size:var(--fs-sm); padding:0 0 10px; margin-bottom:10px;
              border-bottom:1px solid var(--line) }
/* El aviso de «cuántos conceptos no tienen cuenta contable», arriba de la lista. */
.acc.con-falta{ margin-bottom:10px }

/* ── Estados de una lista · cargando ≠ vacío ≠ filtrado ────────────────────────────────────────
   Antes las tres se veían igual: «Sin operaciones con esos filtros», dijera lo que dijera la
   realidad. El de «cargando» son renglones fantasma y no una frase, porque lo que hay que decir
   es «esto se está llenando» — y una frase se lee, se entiende, y no vuelve a comunicar nada. */
.listhead{ display:flex; align-items:center; gap:10px; flex-wrap:wrap;
           padding-bottom:10px; margin-bottom:10px; border-bottom:1px solid var(--line) }
.lh-cuenta{ font-size:var(--fs-base); font-weight:600; font-variant-numeric:tabular-nums }
.lh-cuenta span{ font-weight:400; color:var(--gray) }
.lh-limpiar{ margin-left:auto }
.chip{ display:inline-flex; align-items:center; gap:6px; background:var(--light);
       border:1px solid var(--line); border-radius:999px; padding:3px 10px;
       font-size:var(--fs-sm); color:var(--text) }
.chip b{ font-weight:600 }
.filtbtn{ border:1px solid var(--line); background:var(--surface); border-radius:var(--r-md);
          padding:6px 11px; font:inherit; font-size:var(--fs-sm); color:var(--text); cursor:pointer }
.filtbtn:hover{ background:var(--light) }

/* El botón de filtro ACTIVO. `.btn.primary` se alternaba desde el código —«Sin clasificar»,
   «Sin referencia», «Ver solo abiertas»— pero la clase NO EXISTÍA en la hoja de estilos, así que
   el botón encendido se veía idéntico al apagado. Estaba puesto desde hacía semanas. */
.btn.primary,.filtbtn.primary{ background:var(--navy); border-color:var(--navy); color:var(--surface); font-weight:500 }
.btn.primary:hover,.filtbtn.primary:hover{ background:var(--navy-hover) }

.lst-cargando{ padding:4px 2px }
.lst-cargando-t{ font-size:var(--fs-sm); margin-bottom:10px }
.skelrow{ display:grid; grid-template-columns:1.1fr 1.6fr .8fr 1fr; gap:12px;
          padding:10px 0; border-bottom:1px solid var(--line) }
.skel{ height:13px; border-radius:4px; background:var(--light);
       background-image:linear-gradient(90deg,var(--light) 25%,var(--bg) 50%,var(--light) 75%);
       background-size:200% 100%; animation:skelbrilla 1.1s infinite }
@keyframes skelbrilla{ 0%{background-position:200% 0} 100%{background-position:-200% 0} }

.lst-vacio{ text-align:center; padding:30px 16px }
.lst-vacio .big{ font-size:var(--fs-md); font-weight:600; margin-bottom:4px }
.lst-vacio .small{ font-size:var(--fs-base); color:var(--gray); margin-bottom:14px }


/* ── D-24 · el selector de rama del movimiento ─────────────────────────────────────────────────
   «¿Qué es este movimiento?» va arriba de la categoría, porque la categoría SE DERIVA de la
   respuesta. Las tres tarjetas son grandes a propósito: es la decisión que ordena todo lo demás
   del panel, y esconderla en un menú la volvería un campo más. */
.rama-sel{display:flex;gap:10px;flex-wrap:wrap;margin:4px 0 14px}
.rama-op{flex:1;min-width:180px;border:1.5px solid var(--line);border-radius:9px;padding:11px 12px;
  cursor:pointer;background:var(--surface);position:relative;transition:border-color .12s,background .12s}
.rama-op:hover{border-color:var(--c-gray-300)}
/* La tarjeta que se pidio y espera confirmacion. Sin esto el clic no acusaba recibo y se leia
   como un boton roto — reporte de J del 8-ago sobre el #399. */
.rama-op.pide{border-color:var(--warn-border);background:var(--warn-bg);box-shadow:0 0 0 3px var(--warn-bg)}
.rama-op.on{border-color:var(--navy);background:var(--light);box-shadow:0 0 0 3px rgba(28,49,73,.06);cursor:default}
/* «SIN GUARDAR» en ambar: la rama elegida todavia no esta en la base. Sin esta distincion, el
   panel se veria igual antes y despues de guardar, que es como se pierde un cambio sin notarlo. */
.rama-now.pend{background:var(--c-amber-700)}
.rama-now{position:absolute;top:-9px;right:10px;background:var(--navy);color:#fff;font-size:10px;
  padding:1px 7px;border-radius:20px;letter-spacing:.3px}
.rama-t{font-weight:600;margin-bottom:2px;font-size:13px}
.rama-d{font-size:11px;color:var(--gray);line-height:1.4}
.rama-c{font-size:11px;color:var(--navy);margin-top:6px}
/* Lo que cuelga de la rama actual. Es lo que hoy no se ve — y por eso cambiarla da miedo. */
.rama-built{margin-top:8px;padding-top:7px;border-top:1px dashed var(--line);font-size:11px;line-height:1.5}
.rama-aviso{margin:8px 0}

/* La confirmación. Nunca se destruye en silencio: dice qué se va, con nombres y montos. */
.rama-dlg{border:1.5px solid var(--c-amber-700);border-radius:9px;overflow:hidden;margin:10px 0 14px;background:var(--surface)}
.rama-dlg.stop{border-color:var(--c-red-700)}
.rama-dlg-h{background:var(--c-amber-50);color:var(--c-amber-700);padding:9px 13px;font-weight:600;
  font-size:13px;border-bottom:1px solid var(--c-amber-200)}
.rama-dlg.stop .rama-dlg-h{background:var(--c-red-50);color:var(--c-red-700);border-bottom-color:var(--c-red-200)}
.rama-dlg-b{padding:12px 13px;font-size:12px}
.rd-p{margin:0 0 10px}
.rd-p0{margin:0}
.rd-p2{margin:8px 0}
.rd-nota{margin:0 0 12px;font-size:12px}
.rd-nota2{margin:8px 0 0;font-size:12px}
.rd-btns{margin-top:12px;display:flex;gap:8px;flex-wrap:wrap}
.rama-tb{width:100%;border-collapse:collapse;font-size:12px}
.rama-tb th{text-align:left;font-size:11px;text-transform:uppercase;letter-spacing:.4px;color:var(--gray);
  border-bottom:1px solid var(--line);padding:5px 7px}
.rama-tb td{padding:6px 7px;border-bottom:1px solid var(--line)}
.rama-tb .num{text-align:right;font-variant-numeric:tabular-nums}
/* El botón destructivo dice QUÉ destruye, no «aceptar». Un botón que dice «aceptar» no informa de
   nada: se aprieta por costumbre. */
/* ⚠ Este boton estuvo INVISIBLE desde que se escribio: usaba `var(--red-700)`, que NO EXISTE —el
   token se llama `--c-red-700`—, asi que el fondo se caia al del boton normal y el `color:#fff`
   quedaba BLANCO SOBRE BLANCO. J lo vio en el #399: el boton de confirmar salia como una caja
   vacia junto a «Cancelar», y por eso la confirmacion de CAN-1 nunca se pudo probar en pantalla.
   Eran 11 tokens inventados en este bloque, todos con el mismo error de prefijo. */
.btn.rama-danger{background:var(--c-red-700);color:#fff}

/* Deshacer, diez segundos. El confirm evita el error de dedo; esto evita el arrepentimiento. */
.toast-undo{position:fixed;left:50%;transform:translateX(-50%);bottom:22px;z-index:80;
  background:var(--c-slate-700,#2B3440);color:#fff;border-radius:9px;padding:10px 14px;font-size:12px;
  display:flex;align-items:center;gap:14px;box-shadow:0 6px 20px rgba(28,49,73,.22);max-width:min(560px,92vw)}
/* ⚠ HALLAZGO MEDIDO (7-sep, OPR-16 v3) · el botón «Deshacer» era BLANCO SOBRE BLANCO.
   Esta regla ponía el texto en #fff para el acuse oscuro y se olvidó del fondo: `.btn.ghost`
   trae `background:var(--surface)` y era el ÚNICO que declaraba `background` para ese botón, así
   que no hubo cascada que resolver — no había con qué competir. ⚠ CORRECCIÓN H-5 (auditoría
   7-sep): aquí decía «gana porque vive más abajo en el archivo», y es al revés — `.btn.ghost`
   está en la 1198 y esta regla en la 2155. El orden habría hecho ganar a ésta; lo que perdió fue
   la propiedad que nunca se escribió. Medido en Chrome de verdad: texto rgb(255,255,255) sobre
   rgb(255,255,255) — contraste 1:1, o sea invisible; no «poco contraste», invisible.
   NO lo rompió esta tanda: la misma medición sobre la copia de la 14.85 da lo mismo. Se
   arregla ahora porque J decidió el 7-sep que el «Aplicar» de un clic sale «con el toast de
   deshacer», y un botón de deshacer que no se ve es exactamente lo que el propio código llama
   «peor que no tenerlo». El arreglo va en ESTA regla, que es la que ya existía para este
   trabajo, y por eso cubre de una vez a los tres acuses que usan `toastDeshacer`.
   ⚠ NINGUNA RED COBRA ESTO: se midió a mano con puppeteer. Lo que lo cazaría es un chequeo de
   contraste sobre las parejas texto/fondo de styles.css, y eso es una red nueva — se dice y no
   se finge (regla S, tercer matiz). */
.toast-undo .btn{border-color:rgba(255,255,255,.35);color:#fff;background:transparent}

/* El campo de IVA apagado cuando no hay CFDI. Se ve, pero no se puede llenar: esconderlo dejaria
   la duda de si existe; apagarlo explica que la respuesta depende de la pregunta de arriba. */
.apagado{opacity:.5}
.apagado input{background:var(--c-light-100,#F1F4F7);cursor:not-allowed}
.mini-hint{font-size:11px;margin:3px 0 10px;line-height:1.45}

/* ══════════════════════════════════════════════════════════════════════════════════════════════
   D-30 · CAPTURA DE MOVIMIENTOS EN PASOS
   Maqueta de referencia: disenos/captura-de-movimientos.html. Los valores están copiados de ahí
   a propósito — J: «luego aquí me muestras obras de arte y después me implementas dibujos de
   niños de kínder». Si esto y la maqueta divergen, gana la maqueta o se actualiza la maqueta.
   ══════════════════════════════════════════════════════════════════════════════════════════════ */

/* El número de paso. No es decoración: es lo que convierte un formulario en una secuencia. */
/* TANDA 9 · CIERRE: el paso también habla en micro-rótulo (misma gramática que seclabel). */
.paso{ font-size:var(--fs-rotulo); letter-spacing:.06em; text-transform:uppercase; color:var(--navy);
       font-weight:800; margin:15px 0 6px; }
.paso span{ color:var(--gray); font-weight:400; text-transform:none; letter-spacing:0; }
.paso-nota{ font-size:var(--fs-sm); margin:6px 0 0; }

/* La binaria del paso 4. Radio de verdad —no un select— porque son DOS opciones y las dos tienen
   que poder leerse sin abrir nada: la decisión que separa un gasto de una devolución es la única
   que ningún importe puede tomar. */
.opt{ display:flex; gap:var(--sp-2); align-items:flex-start; border:1px solid var(--line);
      border-radius:var(--r-md); padding:8px 10px; margin-bottom:6px; font-size:var(--fs-base);
      background:var(--surface); cursor:pointer; width:auto; }
.opt:hover{ border-color:var(--gray); }
.opt.on{ border:2px solid var(--navy); padding:7px 9px; }
.opt .rad{ flex:none; width:13px; height:13px; border-radius:50%; border:1.5px solid var(--gray); margin-top:3px; }
.opt.on .rad{ border:4px solid var(--navy); }
.opt .tx b{ display:block; color:var(--text); font-weight:600; }
.opt .tx span{ color:var(--gray); font-size:var(--fs-sm); }

/* La sugerencia. NO va en ámbar: en esta app el ámbar es advertencia, y una sugerencia no
   advierte de nada. Superficie clara con acento navy, como el resto. */
.sug{ background:var(--light); border:1px solid var(--line); border-left:3px solid var(--navy);
      border-radius:0 var(--r-md) var(--r-md) 0; padding:11px 13px; margin-top:6px; }
.sug .qq{ font-size:var(--fs-base); margin-bottom:9px; }
.sug .qq b{ color:var(--navy); }
.sug .btns{ display:flex; gap:7px; flex-wrap:wrap; }

/* El renglón gris que dice dónde va a caer el dinero. Es lo que hace que DERIVAR no sea adivinar
   a espaldas de nadie: si algún día sale un rubro inesperado, se ve ANTES de guardar. */
.res{ background:var(--light); border-left:3px solid var(--navy); border-radius:0 var(--r-md) var(--r-md) 0;
      padding:8px 11px; font-size:var(--fs-sm); margin-top:12px; color:var(--gray); }
.res b{ color:var(--navy); font-variant-numeric:tabular-nums; }
.res.falta{ border-left-color:var(--warn); color:var(--warn); }

.rubro{ margin-top:8px; }
.rubro-op{ font-size:var(--fs-xs); color:var(--gray); font-weight:700; letter-spacing:.03em;
           text-transform:uppercase; margin-bottom:4px; }

/* La lista de renglones candidatos. */
.lista{ border:1px solid var(--line); border-radius:var(--r-md); overflow:hidden; margin-top:6px; }
.lista .li{ padding:6px 10px; font-size:var(--fs-sm); border-bottom:1px solid var(--line);
            display:flex; justify-content:space-between; gap:10px; }
.lista .li:last-child{ border-bottom:0; }
.lista .li.sep{ background:var(--light); color:var(--gray); font-size:var(--fs-xs);
                font-weight:700; letter-spacing:.04em; }
.lista .li.pick{ cursor:pointer; }
.lista .li.pick:hover{ background:var(--light); }
.lista .li.vacio{ color:var(--gray); font-style:italic; }
.lista .li .m{ color:var(--gray); font-size:var(--fs-xs); flex:none; }

/* El cliente ya no se captura: sale de la operación y aquí solo se muestra. */
.cli-out{ font-size:var(--fs-sm); margin-top:2px; }
/* LISTA-1 · los tres grados de «le falta algo». Un renglon por grado, con su numero, su dinero y
   su salida. El grado vacio se atenua en vez de desaparecer: que un grado este en cero es
   informacion, y esconderlo haria que la pantalla cambiara de forma segun el dia. */
.pend-banda{ display:grid; gap:6px; margin:0 0 14px; }
.pend-g{ display:grid; grid-template-columns:44px 1fr auto auto; gap:10px; align-items:center;
  padding:8px 12px; border:1px solid var(--line); border-radius:var(--r-md); background:var(--light); }
.pend-g.vacio{ opacity:.5 }
.pend-n{ font-size:var(--fs-lg); font-weight:700; color:var(--navy); text-align:right }
.pend-t b{ display:block; font-size:var(--fs-sm) }
.pend-t span{ display:block; font-size:var(--fs-xs); color:var(--gray) }
.pend-m{ font-size:var(--fs-sm); color:var(--gray); white-space:nowrap }
/* El dueño del dinero. Vivia como nota al pie del bloque de reparto y era lo mas chico de la
   pantalla; en esta rama es la mitad del hecho, asi que se lee sin buscarlo. */
.cli-eti{ font-size:var(--fs-md); font-weight:600; color:var(--navy); }
/* «no es eso» · la salida de emergencia del acuse. Discreta a proposito: no compite con Guardar,
   pero tiene que estar SIEMPRE a la vista, porque el dia que las tres tarjetas dejen de salir por
   omision es la unica puerta para corregir una clasificacion. */
/* La salida del acuse. Nacio como un <span> gris de fs-xs pegado al final de la linea y J no lo
   encontro: «no veo lo de no es eso». Desde que las tarjetas no salen por omision este boton es
   el UNICO camino para corregir una clasificacion, asi que se ve como lo que es. Mismo tipo que
   el de repartir, que si se encontro y se uso. */
.acuse-btn{ margin-top:8px; }
/* Lo que el acuse no puede contestar y por que. Va en el acuse y no en un panel aparte porque un
   pendiente que vive en un documento que nadie abre es el error mas caro de este proyecto. */
.acuse-ojo{ color:var(--warn); font-size:var(--fs-xs); }
/* Campo 1 cuando el dinero esta repartido: no hay UNA respuesta, asi que el campo la dice en vez
   de fingir que se puede contestar (el «-Split-» de QB). Misma geometria que `.impfield` para que
   se lea como el campo que sustituye, y borde punteado porque no se escribe en el. */
.campo-fijo{ width:100%; box-sizing:border-box; padding:10px 12px; border:1px dashed var(--line);
  border-radius:8px; font-size:var(--fs-md); color:var(--gray); background:var(--light);
  margin-bottom:12px; }

/* La sugerencia cuelga de «un gasto…», no es una tercera opción. Sin esta sangría se lee como
   si fuera otra cosa que elegir — y la decisión de arriba es binaria, no un menú de tres. */
.bajo-opt{ margin-left:21px; }

/* La insignia «ASÍ ESTÁ HOY» se encimaba con el rótulo del paso: dos textos peleando el mismo
   renglón. La tarjeta se baja lo justo para que quepan los dos. */
.paso + div > .rama-sel, .paso + .rama-sel{ margin-top:11px; }

/* El renglón del resultado quedaba pegado al comentario, y ahí es donde hay que detenerse a
   mirarlo antes de guardar. */
#me_cuenta_out .res{ margin-bottom:16px; }

/* ── TANDA 9 (b) · el modal de clasificar habla el lenguaje de las maquetas ────────────────────
   La PRIMERA pantalla migrada al vocabulario de disenos/lenguaje-visual-v1.html (aprobada tal
   cual por J, 24-ago). Scoped a #mov_modal a propósito: las pantallas migran UNA POR UNA, cada
   una con su rebote de J — cuando todas hablen igual, estos scopes se funden con la regla base.
   Cero hex y cero px sueltos: todo token (DIS-1).
   · El micro-rótulo nació aquí SCOPED y en el CIERRE de la tanda (mismo día, noche 2) se fundió
     a la regla base de .seclabel/.paso — la fusión que la hoja preveía. El scope murió con la
     fusión (regla 1: una casa); sólo quedan las piezas propias del modal (.me-*) y su radio.
   · La caja de la descripción del banco se vuelve pieza (.me-desc) con --r-caja y la
     respiración de tarjeta (12/16) — antes era un estilo en línea con radio fuera de escala.
   · El importe del encabezado va en tabular-nums (gramática 3).                              */
#mov_modal .modal-card{ border-radius:var(--r-lg); }
.me-cabeza{ margin:0 0 var(--e1); }
.me-sub{ margin:0 0 var(--e4); }
.me-monto{ font-variant-numeric:tabular-nums; }
.me-desc{ background:var(--light); border-radius:var(--r-caja); padding:var(--e3) var(--e4);
          font-size:var(--fs-base); margin-bottom:var(--e4); }

/* ══════════════════════════════════════════════════════════════════════════════════════════════
   UX-1 · EL SELLO DEL PAR VIGENTE, dentro del modal del movimiento (27-ago)
   Va ARRIBA de todo lo editable y por eso pesa: es lo que cambia el significado de lo de abajo
   —el reparto está congelado por el par— y un sello que llega después de cinco preguntas confirma
   lo que la pantalla acaba de contradecir. La banda izquierda gruesa y la cifra en --fs-xl son
   la gramática 1 de la hoja de lenguaje visual: el título susurra, el dato habla.
   El folio del documento va en la familia MONO + café (--interno-*): es plomería interna de la
   agencia, no algo que el cliente reciba.
   Cero hex y cero px sueltos: todo pasa por token (DISEÑO.md · regla de oro).
   ══════════════════════════════════════════════════════════════════════════════════════════════ */
.me-sello{ background:var(--ok-bg); border:1px solid var(--ok-border);
           border-left:var(--sp-1) solid var(--ok); border-radius:var(--r-caja);
           padding:var(--e3) var(--e4); margin:var(--e2) 0 var(--e4); }
.me-sello-tit{ font-size:var(--fs-lg); font-weight:800; color:var(--ok); line-height:1.2; }
.me-sello-cifra{ font-size:var(--fs-xl); font-weight:800; color:var(--ok);
                 font-variant-numeric:tabular-nums; line-height:1.1; }
.me-sello-sub{ font-size:var(--fs-sm); margin-top:var(--sp-1); }
.me-sello-doc{ display:inline-block; font-family:var(--font-mono); font-size:var(--fs-sm);
               color:var(--interno-ink); background:var(--interno-bg);
               border:1px solid var(--interno-line); border-radius:var(--r-pill);
               padding:0 var(--sp-2); }
.me-sello-lista{ margin:var(--sp-2) 0 0; padding-left:var(--e4); font-size:var(--fs-sm); }
.me-sello-btns{ margin-top:var(--e3); padding-top:var(--sp-2); border-top:1px solid var(--ok-border); }
.me-sello-nota{ font-size:var(--fs-xs); margin-left:var(--sp-2); }
/* El pliegue del paso de elegir. Es un <details> a propósito: abre y cierra sin una global, sin
   manejador y sin ruta en el portero, y así «arranca plegado siempre» (P5) se cumple porque no hay
   nada que recordar. Borde punteado y fondo cálido: la misma señal de «detente a verificar» de la
   caja de conciliación, no un bloque más de captura. */
.me-pliegue{ border:1px dashed var(--gray-soft); border-radius:var(--r-md); background:var(--recon-bg);
             padding:var(--sp-2) var(--e3); margin-top:var(--e3); font-size:var(--fs-sm);
             color:var(--gray); }
.me-pliegue summary{ cursor:pointer; }
.me-pliegue b{ color:var(--text); }
.me-pliegue-filas{ margin-top:var(--sp-2); padding-top:var(--sp-2); border-top:1px solid var(--recon-line); }
.me-pliegue-fila{ display:flex; justify-content:space-between; gap:var(--sp-2); color:var(--text); }
.me-pliegue-fila .num{ font-variant-numeric:tabular-nums; }
/* El chip del sello en la lista de movimientos de la operación: la MISMA verdad con la forma más
   chica que cabe. Ahí no hay botón — la fila entera ya abre el modal, y el Deshacer vive dentro. */
.sello-chip{ font-variant-numeric:tabular-nums; }

/* ══════════════════════════════════════════════════════════════════════════════════════════════
   D-31 · REPASO EN LOTE
   Se agrupa por DESTINO, no por movimiento: 126 renglones son 126 decisiones, y cuatro grupos son
   cuatro. La lista de cada grupo se abre para revisar antes de aceptar — nunca hay un botón que
   escriba a ciegas.
   ══════════════════════════════════════════════════════════════════════════════════════════════ */
.rep-grupo{ border:1px solid var(--line); border-radius:var(--r-md); margin-bottom:8px; background:var(--surface); overflow:hidden; }
.rep-grupo.dud{ background:var(--light); }
.rep-cab{ display:flex; align-items:center; gap:var(--sp-3); padding:10px 12px; }
.rep-n{ flex:none; min-width:38px; text-align:right; font-weight:700; font-size:var(--fs-lg);
        color:var(--navy); font-variant-numeric:tabular-nums; }
.rep-dest{ flex:1; font-size:var(--fs-base); }
.rep-btns{ flex:none; display:flex; gap:6px; }

.rep-lista{ border-top:1px solid var(--line); max-height:360px; overflow:auto; }
.rep-fila{ display:flex; align-items:center; gap:var(--sp-2); padding:6px 12px;
           border-bottom:1px solid var(--line); font-size:var(--fs-sm); cursor:pointer; width:auto; margin:0; }
.rep-fila:last-child{ border-bottom:0; }
.rep-fila:hover{ background:var(--light); }
/* Desmarcado NO se esconde: se atenúa. Esconder lo que quitaste te deja sin saber qué quitaste. */
.rep-fila.off{ opacity:.45; }
.rep-chk{ flex:none; width:16px; height:16px; border:1.5px solid var(--navy); border-radius:4px;
          display:flex; align-items:center; justify-content:center; font-size:11px; color:var(--navy); font-weight:700; }
.rep-fila.off .rep-chk{ border-color:var(--gray); }
.rep-ref{ flex:none; width:110px; font-weight:600; }
.rep-desc{ flex:1; color:var(--gray); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.rep-lin{ flex:none; width:150px; color:var(--navy); }
.rep-monto{ flex:none; width:110px; text-align:right; font-variant-numeric:tabular-nums; }
.rep-intro{ margin:0 0 14px; }
.rep-sec2{ margin-top:22px; }
/* Con el lote apagado la fila no se marca: lleva. La flecha lo dice sin texto. */
.rep-ir{ flex:none; width:16px; text-align:center; color:var(--navy); font-weight:700; }

/* ── D-32 · La tabla del dinero: Por cobrar · Pagado · Falta · Comprobante ───────────────────
   `Pagado` sale del banco, no de un campo. Estas clases existen para que las tres columnas
   nuevas no lleguen con estilo en linea — la metrica `estilosInline` solo puede bajar. */
/* .mt14 retirada el 20-ago: definida y usada cero veces. */
.fs11{ font-size:11px }
/* El renglon que impide mentir: dinero que salio del banco sin decir que paga. Se marca en frio
   —no en ambar— porque no es un error: es trabajo pendiente, y va a estar ahi un rato. */
.opc-table tr.opc-sinclas td{ background:#F5F9FF }
.opc-vacio{ padding:8px 0 }

/* La respuesta de `movPagaEnOp`, en la lista de movimientos ligados. «Falta» no es rojo de error:
   es ambar de pendiente. El movimiento no esta mal — le falta que alguien diga que paga. */
.mov-paga{ font-size:11px; color:var(--ok); font-weight:600; }
.mov-falta{ font-size:11px; color:var(--warn); font-weight:600; }

/* ── D-32 · el marco de la tabla del dinero ───────────────────────────────────────────────────
   La maqueta encuadra la tabla; la app la pintaba suelta sobre el fondo. Es el mismo hallazgo
   del 4-ago con la subpantalla blanca: la paleta ya era la correcta, lo que faltaba era el MARCO. */
.tblbox{ border:1px solid var(--line); border-radius:8px; overflow:hidden; background:var(--surface) }
.tblbox .opc-table{ margin-top:0 }
.tblbox .opc-table th{ padding:8px }
.tblbox .opc-table tr:first-child td{ border-top:0 }
/* El codigo de moneda vive en el titulo de la seccion, no en cada celda. */
.sec-cur{ font-size:var(--fs-xs); font-weight:600; letter-spacing:.06em; color:var(--gray);
          background:var(--light); border:1px solid var(--line); border-radius:99px; padding:1px 8px; margin-left:8px }
/* Los importes en la fuente del resto de la app, alineados por `tabular-nums`. El monoespaciado
   los hacia ver como salida de terminal — es la diferencia mas visible contra la maqueta. */
.opc-table td.num{ font-family:inherit; white-space:nowrap; font-variant-numeric:tabular-nums }
.opd-puente{ font-variant-numeric:tabular-nums }

/* Los importes del puente y de la banda de saldo, en la fuente del resto de la app. `tabular-nums`
   ya los alinea; el monoespaciado solo los hacia ver como consola. Es la ultima diferencia visible
   contra la maqueta, y la mas barata de cerrar. */
.opd-puente .pr .n{ font-family:inherit; font-variant-numeric:tabular-nums }
.opd-saldo .v{ font-family:inherit; font-variant-numeric:tabular-nums }
.mt12{ margin-top:var(--e3) }
.mb14{ margin:0 0 var(--e4) }   /* era 14px · sube a 16. ⚠ pone el shorthand ENTERO, no sólo el bottom: el nombre miente y por eso no se reusa. */

/* ── D-32d · la franja de un vistazo ──────────────────────────────────────────────────────────
   Las tarjetas viejas competían con el puente porque pesaban lo mismo. Esta franja no compite:
   es del tamaño de una línea de texto. Un vistazo arriba, la explicación abajo. */
.vistazo{ display:flex; flex-wrap:wrap; gap:6px; margin:2px 0 12px }
.vz{ display:inline-flex; align-items:baseline; gap:6px; background:var(--light);
     border:1px solid var(--line); border-radius:99px; padding:3px 11px; font-size:var(--fs-sm,12px);
     font-variant-numeric:tabular-nums }
.vz em{ font-style:normal; color:var(--gray); font-size:var(--fs-xs); letter-spacing:.03em }
.vz b{ font-weight:600; color:var(--navy) }
.vz.tot{ background:var(--surface); border-color:var(--navy) }
.vz.tot em{ color:var(--navy); font-weight:600 }

/* ── D-32e · jerarquía tipográfica de la pestaña Dinero (feedback de J, 7-ago) ────────────────
   Cada pieza pesaba lo que le tocó, no lo que vale: la banda del saldo tenía tres tamaños y tres
   colores en un renglón, los títulos de sección desaparecían contra el fondo, y el honorario
   —que es TU ingreso— se leía más chico que la descripción de un movimiento.
   Todo con los tokens de `--fs-*`: la métrica `tamanosDeFuente` cuenta los px sueltos y sólo
   puede bajar, que es lo que impide que la escala se desarme un ajuste a la vez. */

/* SALDO en negritas, y su «por cobrar» del MISMO color que el número: son una frase partida en
   dos. Antes el número gritaba en rojo y su etiqueta susurraba en gris, como cosas distintas. */
.opd-saldo .l{ font-weight:800; color:var(--navy); font-size:var(--fs-sm) }
.opd-saldo .v{ font-size:var(--fs-xl); letter-spacing:-.01em }
.opd-saldo .v em{ font-size:var(--fs-sm); font-weight:600; color:inherit; opacity:.75; margin-left:5px }

/* Los títulos de sección se perdían: el mismo gris chico de una nota al pie. Ahora navy y con
   peso de encabezado — el trato que ya tenía «Cotización», que sí se veía. */
/* TANDA 9 · REBOTE R2 (J, 24-ago: «si lo alineamos»). Este override existió a propósito: al cerrar
   la 14.27 se dejó FUERA de la fusión global porque se leyó como jerarquía propia del libro. J lo
   tumbó viéndolo en la hoja `disenos/lenguaje-rebote-1-hoy-vs-deberia-v1.html`, sabiendo que era
   una decisión y no un residuo. El libro pasa a hablar el micro-rótulo como TODA la app, y con eso
   `seccionHTML` — la misma pieza pintada en Reportes y dentro del libro — deja de verse de dos
   tamaños según dónde caiga, que era el precio que la excepción cobraba.
   Se quita el override entero en vez de igualarlo valor por valor: una casa (regla 1). Devolverlo
   es volver a pegar estas dos declaraciones. Lo que se pierde al quitarlo, dicho para el que
   revierta: el libro tenía además `margin-bottom:5px` (la base da 6px) y espaciado .04em. */

/* El honorario es tu ingreso: se lee, no se busca. */
.opd-acciones{ display:flex; gap:16px; align-items:center; flex-wrap:wrap; margin:0 0 12px;
               font-size:var(--fs-md) }
.opd-acciones b{ font-size:var(--fs-lg); color:var(--navy) }

/* La franja, con cuerpo. Sigue sin competir con «Cómo va» —no lleva la relación entre los
   números, sólo los números— pero ya no hay que acercarse para leerla. */
.vistazo{ gap:8px; margin:4px 0 14px }
.vz{ padding:5px 13px; font-size:var(--fs-base) }
.vz em{ font-size:var(--fs-xs) }
.vz b{ font-size:var(--fs-md) }
.vz.tot{ border-width:1.5px }
.vz.tot b{ font-size:var(--fs-lg) }

/* ── D-33 · la zona de pendientes ─────────────────────────────────────────────────────────────
   Un solo lugar para todo lo que pide una decisión o una captura. Antes estaban en cuatro: un
   banner arriba, una celda dentro de una tabla, un empujón a media pantalla y una caja más abajo.
   Cuatro formas, cuatro alturas, cuatro colores — y para saber si algo requería atención había que
   recorrer la pantalla entera y reconocer los cuatro.
   Se llama `pendbox` y no `pend` porque `.pend` ya existe en `.badge.pend` y `.rama-now.pend`:
   reusar un nombre de clase es la versión CSS de la regla 1. */
.pendbox{ border:1.5px solid var(--warn); background:var(--warn-bg,#FDF3E7); border-radius:10px;
          padding:11px 14px; margin:0 0 14px }
.pendbox .t{ font-size:var(--fs-xs); letter-spacing:.08em; text-transform:uppercase; font-weight:800;
             color:var(--warn); margin-bottom:8px }
.pendbox .prow{ display:flex; align-items:center; gap:10px; padding:7px 0; border-top:1px solid var(--line);
                font-variant-numeric:tabular-nums; font-size:var(--fs-base) }
.pendbox .prow:first-of-type{ border-top:0 }
.pendbox .prow .q{ flex:1 }
.pendbox .prow .why{ display:block; color:var(--gray); font-size:var(--fs-xs); margin-top:1px }
.pendbox .prow .pb{ flex:none; display:flex; gap:6px; flex-wrap:wrap; justify-content:flex-end }
/* La versión sana. «Nada pendiente» es una afirmación fuerte: se ve distinta a propósito. */
.pendbox.ok{ border-color:var(--ok); background:var(--ok-bg,#E8F5EE) }
.pendbox.ok .t{ color:var(--ok) }

/* ── v5 (21-ago) · la franja de UNA linea, y la linea pasiva ──────────────────────────────────
   `.linea` es el caso verde: 65 de 111 operaciones gastaban DOS cajas apiladas para decir «no
   pasa nada». El titulo y su frase van en el mismo renglon, no en dos alturas.
   `.pasivo` es lo que resuelve D2: cuando la franja esta en ambar, lo que SI esta bien no se
   dice con una palomita verde al lado del adeudo — baja aqui, en gris, sin competir. */
.pendbox.linea{ display:flex; align-items:baseline; gap:8px; flex-wrap:wrap }
.pendbox.linea .t{ margin-bottom:0 }
.pendbox.linea .okline{ flex:1; min-width:12ch; color:var(--gray); font-size:var(--fs-sm,var(--fs-xs)) }
.pendbox .pasivo{ color:var(--gray); font-size:var(--fs-xs); margin-top:8px; padding-top:8px;
                  border-top:1px solid var(--line) }

/* ── D-35 · Secciones con tres estados ────────────────────────────────────────────────────────
   `<details>` nativo. El abierto/cerrado se deriva de los datos en cada render, así que no hay
   una variable de interfaz que pueda quedar desincronizada del número que explica. */
/* Las secciones ENMARCADAS (8-ago, pedido de J). Antes lo unico que las separaba era
   `margin-top:14px`: sin fondo ni borde, dos secciones seguidas se leian como una sola y el
   parrafo de ayuda de la primera parecia pertenecer a la segunda. Ahora cada una es una
   tarjeta con su propio limite, como la maqueta. */
.secbox{ margin-top:var(--sp-3); background:var(--surface); border:1px solid var(--line);
  border-radius:var(--r-lg); overflow:hidden }
.secbox>summary{ padding:var(--sp-3) var(--sp-4) }
.secbox[open]>summary{ border-bottom:1px solid var(--line) }
.secbox>:not(summary){ padding:0 var(--sp-4) }
.secbox>:not(summary):last-child{ padding-bottom:var(--sp-3) }
.secbox>.tblbox{ padding:0 }
/* La ayuda larga deja de vivir en el flujo. El texto es util LA PRIMERA VEZ y ruido la
   centesima, asi que se pliega: se conserva entero, a un clic. */
.sec-info{ margin:var(--sp-3) 0 var(--sp-2) }
.sec-info>summary{ cursor:pointer; list-style:none; display:inline-flex; align-items:center;
  gap:var(--sp-1); font-size:var(--fs-sm); color:var(--gray); border:1px solid var(--line);
  border-radius:20px; padding:2px var(--sp-2) }
.sec-info>summary::-webkit-details-marker{ display:none }
.sec-info>summary:hover{ color:var(--navy); border-color:var(--navy) }
.sec-info>p{ margin:var(--sp-2) 0 0; font-size:var(--fs-sm); color:var(--gray); max-width:70ch }
/* ── LA BANDA DE SECCIÓN NO SE PARTE POR LA MITAD (12-ago) ─────────────────────────────────
   Medido sobre las 399 bandas que pintan las 111 operaciones reales: con `nowrap`, por debajo
   de 820 px de panel el título Y el resumen se parten LOS DOS en dos renglones y se encabalgan
   (25 bandas a 780 px, 34 a 700). No es que falte espacio: es que `nowrap` obliga a los dos a
   caber en una línea, así que los dos se encogen hasta romperse.
   Con `wrap` el que no cabe BAJA ENTERO — y `margin-left:auto` + `text-align:right` lo dejan
   pegado a la derecha, que es donde el ojo ya lo busca. El `gap` de dos valores es lo que
   separa esos dos renglones: 2px verticales, 8px horizontales. Con el 8px de antes la banda
   crecía 8px al bajar el resumen; así crece 2.
   Verificado en las 1,995 mediciones (399 bandas × 5 anchos): a 820 px y arriba CERO bandas se
   mueven un pixel; abajo se mueven exactamente las 25 y las 34 que estaban rotas, y ninguna
   queda rota. La foto y el careo: `_maestro/foto-bandas.js`. */
.secbox>summary{ cursor:pointer; list-style:none; display:flex; align-items:baseline;
  flex-wrap:wrap; gap:2px 8px; padding:2px 0; border-radius:6px }
.secbox>summary::-webkit-details-marker{ display:none }
/* La flecha la ponemos nosotros para que gire: el marcador nativo no se puede animar ni alinear. */
.secbox>summary::before{ content:'\25B8'; color:var(--gray); font-size:11px; line-height:1;
  transition:transform .12s ease; flex:none; transform:translateY(-1px) }
.secbox[open]>summary::before{ transform:translateY(-1px) rotate(90deg) }
.secbox>summary:hover{ background:var(--hover-bg) }   /* CSS-5 · sin respaldo · ver tokens.css */
/* El resumen vive en el encabezado: es lo que permite dejar la sección cerrada sin esconder nada. */
.sec-res{ margin-left:auto; font-weight:400; font-size:12px; color:var(--gray); text-align:right }
.sec-res.pend{ color:var(--warn); font-weight:600 }

/* ── COMBO (combo.js) · el desplegable con la clase de cada renglón ────────────────────────── */
/* Sólo tokens: el trinquete cuenta `font-size` en px y hex sueltos, y esta hoja no tiene ni uno. */
.cmb{ position:relative; }
.cmb-lista{ position:absolute; left:0; right:0; top:100%; z-index:40; background:var(--surface);
  border:1px solid var(--navy); border-top:none; border-radius:0 0 var(--r-sm) var(--r-sm);
  max-height:260px; overflow-y:auto; box-shadow:0 6px 18px rgba(0,0,0,.10); }
.cmb-it{ display:flex; justify-content:space-between; gap:var(--sp-3); align-items:baseline;
  padding:var(--sp-2) var(--sp-3); border-top:1px solid var(--line); cursor:pointer;
  font-size:var(--fs-base); }
.cmb-it:first-child{ border-top:none; }
.cmb-it:hover, .cmb-it.on{ background:var(--light); }
.cmb-v{ color:var(--text); }
.cmb-k{ color:var(--gray); font-size:var(--fs-xs); white-space:nowrap; }
.cmb-vacio{ padding:var(--sp-2) var(--sp-3); color:var(--gray); font-size:var(--fs-sm); }

/* ══ PIEZAS · INICIO ══ */
/* ══════════════════════════════════════════════════════════════════════════════════════════════
   LAS PIEZAS · propuesta para `styles.css`
   Convención del repo: BASE + VARIANTE (como `.aviso.warn` y `.chip.dual`), hijos con nombre
   corto (`.hd`, `.bd`). Ni un hex ni un px fuera de la escala.
   ══════════════════════════════════════════════════════════════════════════════════════════════ */
:root{
  /* Tercera capa de tokens: los de COMPONENTE. Sin esto, el aire de una caja se repite a mano en
     cada regla y cambiarlo es una cacería. Con esto es una línea. */
  --caja-pad:  var(--sp-4);
  --banda-pad: var(--sp-2) var(--sp-4);
  --caja-gap:  var(--sp-3);
  --par-gap:   var(--sp-6);
}

/* ── 1 · LA CAJA ───────────────────────────────────────────────────────────────────────────── */
.caja{ background:var(--surface); border:1px solid var(--line); border-radius:var(--r-lg);
  overflow:hidden; margin-bottom:var(--caja-gap); box-shadow:0 1px 2px rgb(28 49 73 / .06) }
.caja > .hd{ padding:var(--banda-pad); background:var(--light); color:var(--navy);
  border-bottom:1px solid var(--line);
  font-size:var(--fs-xs); font-weight:700; text-transform:uppercase; letter-spacing:.06em;
  display:flex; justify-content:space-between; align-items:baseline; gap:var(--sp-3) }
.caja > .hd .hd-r{ font-weight:400; text-transform:none; letter-spacing:0; color:var(--gray);
  font-size:var(--fs-xs); font-variant-numeric:tabular-nums }
.caja > .bd{ padding:var(--caja-pad) }
.caja > .bd > :last-child{ margin-bottom:0 }

/* ⚠ LOS DOS MUNDOS VAN EN NEUTROS, NO EN LA PALETA SEMÁNTICA. Medido el 12-ago: `--ok` ya
   significa 23 cosas en `styles.css` (aprobado, conciliado, cuadra, a favor, cobrado, pagado…).
   Pintar «banco» de verde le agrega una vigesimocuarta y deja a la pantalla sin color para avisar.
   La identidad la dan el NOMBRE y un par FRÍO/CÁLIDO; el color queda libre para el estado. */
.caja.banco   > .hd{ background:var(--recon-bg);  color:var(--navy); border-bottom-color:var(--recon-line) }
.caja.banco   { border-color:var(--recon-line) }
.caja.banco   > .hd .hd-r{ color:var(--navy) }
.caja.cliente > .hd{ background:var(--light);     color:var(--navy) }
.caja.cliente > .hd .hd-r{ color:var(--navy) }
.caja.pend    > .hd{ background:var(--warn-bg);   color:var(--warn);   border-bottom-color:var(--warn-border) }
.caja.pend    { border-color:var(--warn-border) }
.caja.pend    > .hd .hd-r{ color:var(--warn) }
.caja.mal     > .hd{ background:var(--danger-bg); color:var(--danger); border-bottom-color:var(--danger-border) }
.caja.mal     { border-color:var(--danger-border) }
.caja.mal     > .hd .hd-r{ color:var(--danger) }

/* ── 2 · EL PAR DIVIDIDO ───────────────────────────────────────────────────────────────────── */
.two.dividido{ gap:var(--par-gap); position:relative; align-items:stretch }
.two.dividido > .caja{ display:flex; flex-direction:column; margin-bottom:0 }
.two.dividido > .caja > .bd{ flex:1 }
@media(min-width:760px){
  .two.dividido::before{ content:''; position:absolute; left:50%; top:var(--sp-4); bottom:var(--sp-4);
    width:1px; background:var(--line); transform:translateX(-50%) }
}

/* ── 3 · EL BLOQUE TEÑIDO ──────────────────────────────────────────────────────────────────── */
.cubeta{ border:1px solid var(--line); border-radius:var(--r-sm);
  padding:var(--sp-1) var(--sp-3);
  margin-bottom:var(--sp-2) }
.cubeta .ct-row{ padding:2px 0 }
.cubeta-h{ font-size:var(--fs-xs); color:var(--gray); margin-bottom:var(--sp-1) }
.cubeta-f{ font-size:var(--fs-xs); color:var(--gray); border-top:1px dashed var(--line);
  padding-top:var(--sp-1); margin-top:var(--sp-1) }
/* Mismo criterio adentro: los dos bloques se distinguen por su etiqueta, no por semáforo. */
.cubeta.ajeno { background:var(--recon-bg); border-color:var(--recon-line) }
.cubeta.propio{ background:var(--light);    border-color:var(--line) }

/* ── 4 · LOS ESTADOS ───────────────────────────────────────────────────────────────────────── */
.chip.est{ background:var(--warn-bg); color:var(--warn); border-color:var(--warn-border) }
.hueco{ color:var(--warn); font-style:italic }

/* ── 5 · BOTÓN SECUNDARIO ──────────────────────────────────────────────────────────────────── */
.btn.mini{ padding:var(--sp-1) var(--sp-3); font-size:var(--fs-sm); border-radius:var(--r-sm);
  background:var(--surface); color:var(--navy); border:1px solid var(--navy) }
.btn.mini:hover{ background:var(--light) }
.btn.mini[disabled]{ opacity:.4; cursor:not-allowed }



/* Alias de la hoja para enseñar el antes/después. NO viajan: `banco` y `cliente` YA son neutros. */
.caja.frio   > .hd{ background:var(--light);     color:var(--navy) }
.caja.calido > .hd{ background:var(--recon-bg);  color:var(--navy); border-bottom-color:var(--recon-line) }
.caja.calido { border-color:var(--recon-line) }
.cubeta.neutro  { background:var(--light); border-color:var(--line) }

/* ── 9 · UNA ACCIÓN PRINCIPAL POR ESTADO ───────────────────────────────────────────────────── */
/* `.btn` sólido = LA acción de este estado. `.btn.mini` = todas las demás. Si una pantalla tiene
   cuatro botones sólidos no tiene ninguno: el ojo no sabe cuál. Medido: hoy NADA en la app usa
   `btn mini` junta, así que esta regla no cambia una sola pantalla existente. */

/* ── 10 · LOS OTROS TRES ESTADOS DE UNA CAJA ───────────────────────────────────────────────── */
.caja .vacio{ color:var(--gray); font-size:var(--fs-sm); padding:var(--sp-4) 0; text-align:center }
.caja .cargando{ height:var(--sp-4); background:var(--light); border-radius:var(--r-sm);
  margin-bottom:var(--sp-2) }
.caja .cargando:nth-child(2){ width:70% } .caja .cargando:nth-child(3){ width:45% }
.caja .roto{ color:var(--danger); font-size:var(--fs-sm) }
.caja .demas{ font-size:var(--fs-xs); color:var(--gray); text-align:center;
  padding-top:var(--sp-2); border-top:1px solid var(--line) }

/* ── 11 · IMPRESIÓN · la relación de gastos acaba en PDF ───────────────────────────────────── */
@media print{
  .caja{ box-shadow:none; break-inside:avoid }
  .caja > .hd{ background:transparent !important; border-bottom:1px solid var(--text);
    color:var(--text) !important }
  .two.dividido::before{ display:none }
  .cubeta{ background:transparent !important }
}
/* ── 12 · EL TITULAR · el saldo se dice UNA vez, y su fórmula debajo ───────────────────────── */
/* El número grande y los renglones que lo producen viven JUNTOS. Separarlos es como nace un
   titular que nadie puede verificar de un vistazo — y por qué «Saldo en tu poder» pudo decir
   $2,415.00 donde el saldo era $95.00 sin que la pantalla se contradijera a sí misma. */
/* ⚠ EL TITULAR NO ES NAVY, y es lo primero que hay que entender de esta pieza.
   Nació navy y J lo cazó a la primera: «se sigue viendo como doble encabezado». Tenía razón —
   la banda de la operación (`.scr-head`) también es navy, así que dos bloques navy apilados se
   leen como UN encabezado partido en dos, aunque digan cosas distintas y haya pestañas en medio.
   En este sistema el navy sólido es el MARCO —el menú, la banda de la pantalla, el botón
   principal—, no «esto es importante». Un titular de CONTENIDO va sobre superficie; lo que le da
   peso es el tamaño del número y su color de estado, que además ya es el vocabulario del resto
   de la pantalla (`c-ok` / `c-warn` / `c-danger`). */
/* ── 12 · aqui vivio EL TITULAR (`.hero`) ─────────────────────────────────────────────────── */
/* Se retiro el 13-ago con la pieza. Su trabajo —decir el saldo y de que renglones sale— lo hacen
   dos cosas que ya existian: la franja `.opd-saldo` del encabezado dice el numero, y la tarjeta
   `.caja.banco` dice los renglones. El titular era una tercera vista del mismo hecho. */

/* ── 13 · EL AVISO ACCIONABLE · amarillo SÓLO cuando hay algo que apretar ──────────────────── */
/* Un aviso que también sale amarillo por captura pendiente está midiendo la cola de trabajo, no
   la salud del dato (regla P, matiz · J), y se pone amarillo en 45 de 111 operaciones — que es
   como se deja de mirar un aviso. Lo informativo vive en la tabla, que es su sitio. */
.alerta{ background:var(--warn-bg); border:1px solid var(--warn-border);
  border-left:var(--sp-1) solid var(--warn); border-radius:var(--r-md);
  padding:var(--sp-3) var(--sp-4); margin-bottom:var(--sp-3);
  display:flex; gap:var(--sp-4); align-items:center; justify-content:space-between;
  flex-wrap:wrap; font-size:var(--fs-base) }
.alerta b{ display:block; color:var(--warn); font-variant-numeric:tabular-nums; margin-bottom:2px }
.alerta .why{ display:block; color:var(--gray); font-size:var(--fs-sm) }
.alerta .ab{ display:flex; gap:var(--sp-1); flex-wrap:wrap }

/* ── 14 · EL PAR ARRIBA, Y LA FRASE QUE DICE POR QUÉ NO SE SUMAN ───────────────────────────── */
.two.dividido.arriba{ align-items:start }
.ct-row em{ font-style:normal; color:var(--gray); font-size:var(--fs-sm) }
.ct-tot.vok b{ color:var(--ok) }
.ct-tot.vdeb b{ color:var(--danger) }
.ct-tot .vd{ font-size:var(--fs-sm); font-weight:600; opacity:.85 }
/* Un SUBTOTAL dentro de la caja: cierra una parte y la lista sigue. No es el total —ese lleva
   `ct-tot` y es el ultimo— y por eso no puede verse igual: linea punteada y peso normal. */
.ct-sub{ border-top:1px dashed var(--line); margin-top:2px; padding-top:var(--sp-1); font-weight:600 }
.ct-sub span{ color:var(--text) }
.cubeta-f.cf-der{ text-align:right }
/* Lo que la tabla NO puede explicar, debajo de la tabla. No es un renglon: un renglon entraria al
   subtotal y volveria a mezclar dos poblaciones — que es de donde salio esta pieza. */
.warnf{ color:var(--warn); font-size:var(--fs-sm); margin-top:var(--sp-2);
  padding-top:var(--sp-2); border-top:1px solid var(--warn-border) }

/* ══ PIEZAS · FIN ══ */

/* ══ RITMO · INICIO ══ */
/* ⚠ ESTE BLOQUE SÍ CAMBIA LO QUE YA SE VE, y por eso vive aparte del de PIEZAS —que es puramente
   aditivo—. Son cuatro reglas globales y una accesibilidad; ninguna es una pieza nueva:
   se aplican a pantallas que ya existen y hay que recorrerlas antes de darlas por buenas.
   Mientras se recorren van bajo `body.ritmo`: se prende con un atributo en `index.html` y se
   apaga igual. Cuando pasen las pantallas, se le quita el prefijo y se borra esta nota.

   Qué cambia cada una, medido:
     · `line-height:1.5`  · el cuerpo no la declaraba y heredaba ~1.2. Es el cambio más visible
                            de todo: TODA la app respira. Es también el que más arregla.
     · `.two`             · 1 regla previa en styles.css; las columnas dejan de estirarse a la
                            altura de la más alta. Afecta a cada par de columnas de la app.
     · `.opc-table`       · 5 reglas previas; las tablas de conceptos ganan aire vertical.
     · `:focus-visible`   · aparece un anillo al navegar con teclado. No se ve con el ratón.  */
body.ritmo{ line-height:1.5 }
body.ritmo .two{ align-items:start }
body.ritmo .opc-table td{ padding:var(--sp-2) var(--sp-2) }
body.ritmo .opc-table thead th{ padding:0 var(--sp-2) var(--sp-2); border-bottom:1px solid var(--line) }
body.ritmo .btn:focus-visible, body.ritmo .btn.mini:focus-visible, body.ritmo a:focus-visible,
body.ritmo input:focus-visible, body.ritmo select:focus-visible, body.ritmo textarea:focus-visible,
body.ritmo [tabindex]:focus-visible{
  outline:2px solid var(--navy); outline-offset:2px; border-radius:var(--r-sm) }
/* ══ RITMO · FIN ══ */


/* NOAD-3 · el bloque «Registrar cobro» del modal de la comisión (D-57) */
.cm-cobro{background:var(--light);border-radius:8px;padding:10px 12px;margin-top:10px}
.cm-cobro .sel-wrap{margin-top:8px}
.cm-cobro .row-btns{margin-top:8px}
.cm-cobro .pie{font-size:12px;margin-top:8px}
.cm-cobro .oculto{display:none}

/* B2 · los dos documentos (D-10 · D-56) y su pie de botones */
.accs{display:flex;justify-content:space-between;align-items:center;gap:10px;margin-top:10px;flex-wrap:wrap}
.accs .mut{color:var(--gray);font-size:var(--fs-sm)}
.accs-nota{color:var(--gray);font-size:var(--fs-sm);margin-top:6px}
.d-sec{font-size:var(--fs-xs);letter-spacing:1.2px;text-transform:uppercase;color:var(--navy);border-bottom:1.5px solid var(--navy);padding-bottom:3px;margin:14px 0 6px;font-weight:600}
.d-table tr.d-sub td{color:var(--gray);border-top:1px solid #cfd5de;font-size:var(--fs-sm)}
.d-table td.d-vacio{color:var(--gray)}
.d-saldo{margin-top:12px;background:var(--light);border:1.5px solid var(--navy);border-radius:8px;padding:10px 14px;display:flex;justify-content:space-between;align-items:baseline;flex-wrap:wrap}
.d-saldo b{font-size:var(--fs-lg)}
.d-saldo .n{flex-basis:100%;font-size:var(--fs-xs);color:var(--gray);margin-top:3px}

/* ══ PAP-1 · los dos papeles del pass-through (solicitud de fondos + relación de gastos) ══════
   Todo va bajo .doc-pt, que es el envoltorio que escriben docSolicitudHTML y docRelacionHTML.
   Motivo: #doc y las clases d-* las COMPARTE la cotización (renderDocData), y ninguna de estas
   decisiones se tomó para ella. Scoping en vez de estado: no hay clase que poner ni que limpiar,
   así que no se puede quedar pegada de un documento al siguiente.
   Medido al 14-ago con el styles.css, los tokens, la fuente Inter y el logo reales, sobre carta
   (816x1056 px, @page margin:0, #doc con sus 14mm INTACTOS): las 89 operaciones con gastos por
   cuenta de terceros caben en UNA hoja. KG26A-0034 usa 980 px; el peor de la base —KG26A-0014,
   4 depósitos + 2 gastos + 2 devoluciones, 9 filas— usa 1040. Sin esto, sólo 2 de 89 cabían. */
.doc-pt .d-head{padding-bottom:8px;margin-bottom:var(--sp-2)}
.doc-pt .d-head img{height:81px}
/* 81px es el máximo que NO empuja: el bloque del título mide 81, así que hasta ahí el logo crece
   gratis y a 82 el encabezado ya cuesta un pixel. Medido, no elegido a ojo (J, 14-ago). */
.doc-pt .d-head h1{font-size:var(--fs-xl);letter-spacing:1.5px}
.doc-pt .d-meta{margin-top:var(--sp-1);line-height:1.35}
.doc-pt .d-parties{margin-bottom:8px;gap:var(--sp-5)}
.doc-pt .d-parties h4{margin-bottom:3px}
.doc-pt .d-parties .blk{line-height:1.4}
/* E-2 (J, 14-ago): el importador pegado a la esquina derecha pero con el texto alineado a la
   izquierda, para que se lea igual que el bloque «Emisor». La E-1 —texto a la derecha— empujaba
   el RFC 107 px con una razón social larga, y de ésas hay 20 operaciones. */
.doc-pt .d-parties .imp2{display:flex;justify-content:flex-end}
.doc-pt .d-sec{margin:8px 0 2px;padding-bottom:2px}
.doc-pt table.d-table{margin-bottom:var(--sp-1)}
.doc-pt table.d-table td{padding:3px 10px}
.doc-pt table.d-table th{padding:5px 10px;font-size:var(--fs-xs)}
/* Punto 3 · la jerarquía estaba INVERTIDA: el total (tr.d-sub) salía en --fs-sm y gris, o sea
   pesando menos que los renglones que suma. Se invierte. */
.doc-pt .d-table tr.d-sub td{color:var(--navy);font-size:var(--fs-md);font-weight:700;border-top:1.5px solid var(--navy)}
/* Punto 2 · la cascada, en tabla propia y no en la letra chica del pie. */
.doc-pt .casc{width:100%;border-collapse:collapse;margin:2px 0 0}
.doc-pt .casc td{padding:2px 10px;font-size:var(--fs-sm);border-bottom:1px solid var(--line)}
.doc-pt .casc td.r{text-align:right;font-variant-numeric:tabular-nums}
.doc-pt .casc tr.men td{color:var(--gray)}
.doc-pt .casc tr:last-child td{border-bottom:none}
/* Punto 1 · la banda de cierre. Es lo último y lo más pesado de la hoja, y la usan LOS DOS
   papeles —el saldo de la relación y el «Total a depositar» de la solicitud— para que el cliente
   los reconozca como pareja. */
.doc-pt .cierre{margin-top:6px;background:var(--navy);color:var(--surface);border-radius:6px;padding:9px var(--sp-4);display:flex;justify-content:space-between;align-items:baseline;flex-wrap:wrap;print-color-adjust:exact;-webkit-print-color-adjust:exact}
.doc-pt .cierre .q{font-size:var(--fs-md);font-weight:700;letter-spacing:.3px}
.doc-pt .cierre b{font-size:var(--fs-xl);font-variant-numeric:tabular-nums}
.doc-pt .cierre .n{flex-basis:100%;font-size:var(--fs-xs);color:var(--line);margin-top:2px}
.doc-pt .ivanote{padding:0 10px 2px;font-size:var(--fs-xs);color:var(--gray)}
.doc-pt .d-notes{padding-top:6px;margin-top:2px}
.doc-pt .d-notes .txt{font-size:var(--fs-xs);line-height:1.4}
/* El rastro del corte impreso y la firma comparten renglón: dos cosas cortas, un bloque. */
.doc-pt .pie1{margin-top:5px;padding-top:4px;border-top:1px dotted var(--line);display:flex;justify-content:space-between;gap:var(--sp-3);font-size:var(--fs-xs);color:var(--gray)}

/* ══ El fondo de los encabezados de tabla NO es decorativo: sin él, texto blanco sobre blanco ══
   print-color-adjust no aparecía ni una vez en esta hoja, así que al imprimir sin marcar
   «Gráficos de fondo» los encabezados de columna de CUALQUIER documento salían ilegibles.
   Es un defecto de hoy, no del rediseño, y por eso esta regla NO va scoped a .doc-pt. */
@media print {
  .d-table th { print-color-adjust:exact; -webkit-print-color-adjust:exact }
}

/* ══ EL BALANCE EN FORMATO QB · FIN-2 piezas 3+4 · 16-ago · D-64 ══════════════════════════════
   Portadas UNA a una desde la maqueta aprobada `disenos/maqueta-FIN-2-balance-qb.html`, que ya
   estaba escrita con ESTOS tokens: no hay un color, un tamaño ni un radio nuevos aquí — sólo
   `var(--…)` de `tokens.css`. Todas llevan prefijo `bce-` porque `bal-` es de la Balanza y las
   dos pantallas conviven a propósito (D-64.7).
   Lo que NO se pudo reusar y por eso existe este bloque: la banda de clase, el renglón de grupo,
   el subtotal del árbol, el pie de totales, los cuatro niveles de sangría y el triángulo. Ninguno
   tiene equivalente en `.dtable`, que es una tabla de lista y no un estado financiero. */
.bce-tabla td, .bce-tabla th { padding:6px 10px }
.bce-clase > td { background:var(--navy); color:#fff; font-weight:700; font-size:var(--fs-sm);
  letter-spacing:.06em; text-transform:uppercase }
.bce-grupo > td { background:var(--light); font-weight:600 }
.bce-grupo { cursor:pointer }
.bce-subtot > td { font-weight:600; background:var(--light) }
.bce-subtot > td:first-child { color:var(--gray) }
.bce-sub > td { font-weight:600; background:var(--light) }
.bce-total > td { font-weight:700; background:var(--light);
  border-top:2px solid var(--navy); border-bottom:2px solid var(--navy) }
.bce-n0 { padding-left:16px } .bce-n1 { padding-left:34px }
.bce-n2 { padding-left:52px } .bce-n3 { padding-left:70px }
.bce-tw { display:inline-block; width:12px; color:var(--gray-soft) }
.bce-xs { font-size:var(--fs-xs) }
.bce-cod { font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:var(--fs-xs); color:var(--gray) }
/* El rojo es del saldo que va CONTRA la naturaleza de su clase — nunca de un número grande. */
.bce-neg { color:var(--danger) }
.bce-cel { cursor:pointer }
.bce-cel:hover { background:var(--warn-bg); outline:1px solid var(--warn-border) }
.badge.bce-contra { background:var(--warn-bg); border-color:var(--warn-border); color:var(--warn) }
.badge.bce-cola   { background:var(--danger-bg); border-color:var(--danger-border); color:var(--danger) }
.badge.bce-clasep { background:var(--light); border-color:var(--line); color:var(--gray) }
/* La línea de estado. Roja SÓLO cuando no cuadra: un 0.00 siempre en verde deja de leerse
   (descarte D de D-64 — sustituyó a la tira de seis tarjetas). */
.bce-estado { border-radius:8px; padding:12px 16px; margin:12px 0; border:1px solid }
.bce-estado.ok  { background:var(--ok-bg);     border-color:var(--ok-border) }
.bce-estado.mal { background:var(--danger-bg); border-color:var(--danger-border) }
.bce-estado .l1 { font-size:var(--fs-md); font-variant-numeric:tabular-nums }
.bce-estado.ok  .l1 { color:var(--ok) }
.bce-estado.mal .l1 { color:var(--danger) }
.bce-estado .l2 { font-size:var(--fs-xs); color:var(--gray); margin-top:4px }
.bce-drill { background:var(--surface); border:2px solid var(--navy); border-radius:12px; padding:16px; margin:12px 0 }
.bce-cerrar { float:right }
.bce-ini > td, .bce-fin > td { background:var(--light); font-weight:700 }
.bce-fin > td { border-top:2px solid var(--navy) }

/* El cuadro de la igualdad · FIN-2, rebote de J (16-ago): el activo de un lado y del otro el
   pasivo con el capital debajo, con los dos totales ENFRENTADOS. Sustituye a «cuadra al medio
   centavo», que era jerga. Sin colores ni tamaños nuevos: hereda los de `.bce-estado`. */
.bce-t { width:auto; margin:10px 0 2px; border-collapse:collapse; font-size:var(--fs-base) }
.bce-t td { border:0; padding:2px 10px 2px 0; white-space:nowrap }
.bce-t td.num { text-align:right; font-variant-numeric:tabular-nums; padding-right:26px; min-width:120px }
.bce-t td.bce-t-sep { border-left:1px solid var(--line); padding-left:26px }
.bce-t tr.bce-t-fin td { border-top:1px solid currentColor; font-weight:700; padding-top:4px }

/* ══ CLI-4 · EL HUB DE CLIENTES (tanda 4 · 18-ago) ═══════════════════════════════════════════
   Acuerdo de diseño: `disenos/CLI-4-v3-lista-y-ficha-producto.html`, cerrado por J tras tres
   rebotes. Lo que NO nace aquí, y es la mitad del trabajo: la barra de dinero y la fila de
   contadores usan `.dashstats`/`.statcard`, que ya existían (Home las usa desde el primer día),
   y los colores salen de `.c-ok`/`.c-warn`/`.c-danger`, que también. Sólo nacen las clases que
   ningún patrón de la app sabía hacer. Cero hex y cero px fuera de la escala de `tokens.css`. */
/* El tile que ACTÚA: la barra de dinero filtra al picarle, y eso tiene que verse antes de
   picarlo. `on` es el tile activo — picarlo otra vez lo apaga. */
.statcard.pick{ cursor:pointer }
.statcard.pick:hover{ border-color:var(--gray-soft) }
.statcard.on{ border-color:var(--navy); box-shadow:inset 0 0 0 1px var(--navy) }
/* Anunciado y apagado, como la pestaña del Expediente: enseñar «0» acusaría de incumplir a quien
   tiene su expediente completo en papel. */
.statcard.off{ opacity:.5 }
.statcard.off .n{ color:var(--gray) }
/* ── OPR-16 v3 (18-sep) · las cinco fichas del dinero de una operación ────────────────────────
   Reúsan el mueble de las tarjetas de comisión y no aduanal; lo propio es sólo el CERO EN GRIS,
   que es lo que deja que la hilera se lea de un vistazo sin que un cero compita con una cifra.
   Sin `fixed`: «Financiado por la agencia» no cabe en los 210 px con `nowrap` y se saldría de
   la tarjeta — aquí la etiqueta envuelve y las cinco reparten el ancho. */
.dashstats.historia{ margin:0 0 10px }
.dashstats.historia .statcard.cero .n{ color:var(--pend); font-weight:500 }
.dashstats.historia .statcard.cero .l{ color:var(--pend) }
/* El renglón gris: los tres gestos que sólo tenían casa en la caja ámbar. Gris y sin caja a
   propósito — es una puerta que sigue abierta, no un aviso que reclama atención. Cada gesto va
   con su sujeto (el texto que ya escribe `pendientesDeOpMon`), porque un verbo sin sujeto deja
   dos botones idénticos cuando hay dos ofrecimientos. */
.opd-gestos{ display:flex; flex-direction:column; gap:6px; margin:0 0 16px }
.opd-gesto{ display:flex; align-items:center; flex-wrap:wrap; gap:8px; font-size:var(--fs-sm) }
/* El renglón de la lista dice el nombre y, debajo, cuántas operaciones y cuándo fue la última
   actividad. Dos datos de distinta jerarquía en una celda, no dos columnas más. */
.cli-sub{ display:block; font-size:var(--fs-xs); color:var(--gray) }
/* La ficha en dos columnas: el dinero a la izquierda, la actividad a la derecha. A una sola
   columna cuando no cabe — el orden de lectura se conserva. */
.hub-cols{ display:grid; grid-template-columns:1.4fr 1fr; gap:var(--sp-4); align-items:start }
@media(max-width:900px){ .hub-cols{ grid-template-columns:1fr } }
/* La actividad, lo último primero. Una lista, no una tabla: no hay nada que comparar entre
   renglones, sólo que leer en orden. */
.hub-feed{ list-style:none; margin:0; padding:0 }
.hub-feed li{ display:flex; gap:var(--sp-3); padding:var(--sp-2) 0; border-bottom:1px solid var(--line);
  font-size:var(--fs-sm) }
.hub-feed li .f{ color:var(--gray); white-space:nowrap; font-variant-numeric:tabular-nums }
/* La referencia fiscal va PLEGADA hasta abajo: es lo que hoy ocupa toda la ficha y lo que menos
   veces al día se necesita. Se consulta, no se vigila. */
.hub-ref{ border:1px solid var(--line); border-radius:var(--r-md); padding:var(--sp-3) var(--sp-4);
  margin-top:var(--sp-4) }
.hub-ref summary{ cursor:pointer; font-weight:600; font-size:var(--fs-sm); color:var(--gray) }
/* De dónde sale el número. No es un párrafo explicativo dentro de la pantalla (J: «a prueba de
   tontos pero no para tontos»): es una nota corta con el origen en su `title`. */
.hub-fuente{ font-size:var(--fs-xs); color:var(--gray); cursor:help; border-bottom:1px dotted var(--line) }
/* El renglón de totales de una tabla del hub. */
.dtable tr.hub-tot td{ border-top:2px solid var(--line); font-weight:700 }
/* El renglón «Sin aplicar a una operación» del hub (parcial, 21-ago): fijo arriba y con el
   fondo de «a favor», porque es dinero del cliente que pide acción. */
.dtable tr.hub-sinaplicar td{ background:var(--ok-bg); border-top:2px solid var(--ok-border); border-bottom:2px solid var(--ok-border) }
/* Las dos direcciones del dinero, en el vocabulario de cobranza que J escogió: «por cobrar» y
   «saldo a favor». Variantes de `.chip`, que ya existía — la convención del repo es BASE +
   VARIANTE, como `.aviso.warn` y `.chip.dual`. */
.chip.favor { background:var(--ok-bg);     color:var(--ok);     border-color:var(--ok-border) }
.chip.cobrar{ background:var(--danger-bg); color:var(--danger); border-color:var(--danger-border) }
/* El chip que está ACTIVO como filtro (el «Todos» de la lista). */
.chip.on{ background:var(--navy); color:var(--surface); border-color:var(--navy) }

/* T4 (20-ago) · los cuatro racimos de `style=` que eran SEMANTICA, no estilo: un texto de error,
   uno de exito, uno de aviso, y «esto se puede clicar». Eran 65 sitios repitiendo el mismo valor
   dentro de cadenas de HTML, donde ningun selector los alcanza. Aqui si.
   NO se movieron los 49 `display:none`: 84 puertas de JS tocan .style.display y SEIS le asignan
   '' esperando destapar el elemento. Con el ocultamiento en una clase, esas seis lo dejarian
   oculto sin avisar (regla H: se miran TODAS las puertas, y estas no pasan). */
.t-danger{ color:var(--danger); }
.t-ok    { color:var(--ok); }
.t-warn  { color:var(--warn); }
.clic    { cursor:pointer; }

/* ── Los escalones que faltaban de la escala (20-ago) ──────────────────────────────────
   Van al final porque son NUEVOS: ninguna regla existente compite con ellos, así que su
   posición no puede cambiar nada de lo que ya se ve. Los que ya existían NO se movieron. */
.mt4 { margin-top:var(--e1) }
.mt24{ margin-top:var(--e5) }
.mt32{ margin-top:var(--e6) }
.mb4 { margin-bottom:var(--e1) }
.mb8 { margin-bottom:var(--e2) }
.mb16{ margin-bottom:var(--e4) }
.mb24{ margin-bottom:var(--e5) }
.p4  { padding:var(--e1) }
.pl24{ padding-left:var(--e5) }

/* ── EL P&L POR MES (tanda #6 · maqueta pl-formato-qb-v1) ─────────────────────────────────
   Cuenta + Año van PEGADAS a la izquierda (decisión de J, 21-ago): con 6–12 columnas de mes
   hay scroll horizontal y el número que no puede desaparecer es el del año. Se reusa el patrón
   sticky de refcol —la app nunca ha pegado a la derecha— con su misma sombra al borde de lo
   fijo (.plfixend marca la última columna pegada: la del año, o la de cuenta en Acumulado). */
.plqb th{ cursor:default }
.plqb th.plc1,.plqb td.plc1{ position:sticky; left:0; background:var(--surface); z-index:2;
  width:325px; min-width:325px; max-width:325px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
.plqb th.plc2,.plqb td.plc2{ position:sticky; left:325px; background:var(--surface); z-index:2; min-width:112px }
.plqb thead th.plc1,.plqb thead th.plc2{ background:var(--light); z-index:4 }
.plqb td.plfixend,.plqb th.plfixend{ box-shadow:6px 0 8px -8px rgba(28,49,73,.35) }
.plqb tbody tr:hover td.plc1,.plqb tbody tr:hover td.plc2{ background:var(--light) }
.plqb tr.plgrp td{ font-weight:600; padding-top:12px; color:var(--navy); border-bottom:none }
.plqb tr.plsub td{ font-weight:600; border-top:1px solid var(--line) }
.plqb tr.plres td{ font-weight:700; border-top:1px solid var(--line) }
.plqb tr.plneta td{ font-weight:700; border-top:2px solid var(--navy) }
.plqb tr.pltenue td{ color:var(--gray); font-style:italic }
.plqb .plcod{ color:var(--gray); font-size:11px; margin-right:6px }
.plfranja{ display:flex; justify-content:space-between; align-items:center; gap:10px;
  margin:4px 0 8px; font-size:13px; color:var(--gray) }
.plfuera{ margin-top:10px; border-top:1px solid var(--line); padding-top:8px; font-size:13px }
.plfuera-t{ font-weight:600; color:var(--gray); margin-bottom:4px }
.plfuera-r{ display:flex; justify-content:space-between; gap:16px; padding:3px 0 }
.plfuera-r .muted{ display:block; font-size:12px }

/* ── LA PESTAÑA EXPEDIENTE (tanda #5 · P-34/C3): el checkbox en su casa ─────────────────── */
/* 15.03 · casilla 5 A. Medido en Chrome sobre el HOY: 12 px en gris con una caja de 13 — el
   control con más consecuencias de la pestaña, pintado como una nota. Pasa al tamaño del dato,
   en el color del texto, con caja de 16.
   ⚠ `.expd-chk` lo COMPARTE el Previo («Exigir foto aun sin observación», app.js): la tipografía
   se comparte a propósito —una casilla es una casilla en las dos pantallas— pero el MARGEN se
   acota al renglón del rótulo, que es quien pone su propio hueco. */
.expd-chk{ display:flex; align-items:center; gap:var(--e2); margin:4px 0 8px;
           font-size:var(--fs-base); color:var(--text) }
.expd-chk b{ font-weight:600 }
.expd-chk input{ width:16px; height:16px; accent-color:var(--navy); margin:0 }
/* El renglón del rótulo «Los documentos»: el título a la izquierda y la casilla a la derecha
   (casilla 4-bis A). `.expd-row-r` existe para que el chip del Diagnóstico quepa al lado de la
   casilla el día que ESA tanda lo traiga — hoy sólo lleva la casilla. */
.expd-row{ display:flex; justify-content:space-between; align-items:center; gap:var(--e3);
           flex-wrap:wrap; margin:0 0 var(--e2) }
.expd-row .seclabel{ margin:0 }
.expd-row .expd-chk{ margin:0 }
.expd-row-r{ display:flex; align-items:center; gap:var(--e3); flex-wrap:wrap }

/* ── APLICAR EL ANTICIPO (tanda #2 · maqueta d66): el renglon bloqueado y los montos ────── */
.za-lock td{ color:var(--gray) }
.za-fila{ display:inline-flex; align-items:center; gap:8px }
.za-fila input{ width:auto }
.za-monto{ width:112px; text-align:right }
.za-monto.err{ border-color:var(--danger) }

/* ── DIN-32 · LO QUE EL DIALOGO DE PARES MARCA Y HOY NO SE VE (rebote con J, 5-sep) ──────────
   H-5 de la auditoria del 5-sep, medido sobre esta hoja: `.ze-monto` tenia **0** reglas, `.err`
   sobre la celda del presupuesto **0**, y `.btn[disabled]` **0** (existian `.pdf-modal
   button:disabled` y `.btn.mini[disabled]`, que son otros botones). O sea que el campo en rojo,
   el presupuesto rebasado y el boton apagado NO cambiaban de aspecto. Nada se rompia —el boton
   quedaba `disabled` de verdad y la pista decia el porque— pero una clase que nadie pinta es,
   en pantalla, exactamente nada.

   POR QUE GENERALES Y NO ENUMERADAS (regla O, tercer matiz): una lista `.za-monto, .ze-monto`
   deja fuera al TERCER dialogo el dia que nazca, y una copia por tamaño (`.btn.sm[disabled]`)
   deja fuera a `ghost`, a `primary` y al `.btn` pelon. La casa comun ya existe: `.opc-table` y
   `.btn`.

   ALCANCE, MEDIDO HOY y no recordado (`din32-ajuste/medir-blast.js`, con su testigo):
     · `<input>` en todo `app-web/`: 160. Dentro de un `<td class="num">`: **2**, y son
       `za-monto` y `ze-monto`. Los valores de la primera regla son EXACTAMENTE los que
       `.za-monto` ya tenia dos renglones arriba, asi que el dialogo de abonos no cambia ni un
       pixel: no se estrena un aspecto, se le da al gemelo el que le faltaba. Y sin ella el
       campo se estira a `width:100%` por la regla global de `input` (:75).
     · `.disabled =` en `app.js`: 21 sitios. `.btn[disabled]` los toca a TODOS los que sean
       `.btn` — eso NO es un efecto lateral de esta tanda: va dibujado en el rebote y J lo vio.
       No pisa a los dos que ya contestaban: `.pdf-modal button:disabled` es (0,2,1) y
       `.btn.mini[disabled]` (0,3,0), las dos por encima de `.btn[disabled]` (0,2,0).

   ⚠ LA ULTIMA ES LA QUE CAMBIO DE PUNTERIA respecto de la propuesta. Decia
     `.opc-table td.num.err` porque el presupuesto era una CELDA; desde el rebote es un `<span>`
     dentro de la celda del total, y una regla sobre la celda haria lo contrario de lo que el
     marcado quiere decir: pintaria de rojo el `<b>` del total (que hereda el color) y dejaria
     el presupuesto gris (que trae el suyo por `.muted`). Se apunta al span.
     Y se escribe `span.err` y NO `.err` a secas a proposito: `.opc-table td.num .err` tambien
     empataria `input.err` y le meteria un `color` rojo al importe tecleado — incluido el del
     dialogo de abonos, que es justo lo que esta tanda promete no tocar.
     Especificidad leida: `.opc-table td.num span.err` = (0,3,2), por encima de `.muted`
     (0,1,0) y de `.opc-table .opc-sub td` (0,2,1), que es quien da el color de la fila. */
.opc-table td.num input     { width:112px; text-align:right }
.opc-table td.num input.err { border-color:var(--danger) }
.btn[disabled]              { opacity:.4; cursor:not-allowed }
.opc-table td.num span.err  { color:var(--danger) }

/* Casa D (rebote del ⋮, 22-ago): la fila de documentos dentro de Dinero */
.opd-docsrow{display:flex;gap:8px;flex-wrap:wrap;margin:4px 0 6px}

/* SEG-16 mitad 2: la fila de subida con tipo y fecha de emision */
/* El pie de subida en UN renglon (J, 12-sep, casilla 6). Eran CUATRO (172 px) porque el campo de
   fecha y el de archivo se estiraban al 100 %: los tres traen su ancho aqui, y el
   `style="max-width:100%"` que vivia en la plantilla se murio con ellos (`estilosInline` es
   trinquete y solo puede bajar). */
/* 15.03 · casilla 5 A. Medido en Chrome sobre el HOY: CUATRO controles con TRES tamaños de letra
   (14/14/12/13 px) y el botón 5 px más bajo que sus campos (33 contra 38). Quedan DOS —el
   selector y «Subir…»— al mismo tamaño y a la misma altura, la de `.btn.sm`, alineados por su
   centro; y el pie se separa de la tabla con el hueco de la escala en vez de con 7 px sueltos.
   La regla de `input[type=date]` muere con el campo (casilla 3b) y la de `input[type=file]` con
   su escondite: el <input> lleva `.ex-file`, que ya lo esconde para las otras dos pantallas. */
.expsube{display:flex;gap:var(--e2);flex-wrap:wrap;align-items:center;
         margin-top:var(--e3);padding-top:var(--e3);border-top:1px solid var(--line)}
.expsube select{width:250px;max-width:280px;font-size:var(--fs-base);padding:0 10px;height:33px;
                box-sizing:border-box}

/* ── «LOS DOCUMENTOS» de la ficha de la operacion (J, 12-sep · acta REBOTE-J-FICHA-OP) ───────
   Cuatro columnas fijas. El TIPO manda —numero en gris, nombre en negrita— y el archivo va
   debajo, en chico: J pidio «no mostrar el nombre del doc sino que documento es». Medido en la
   maqueta con los mismos 13 archivos: el bloque baja de 939 px a 592 y el renglon de 63 a 41. */
/* 15.03 · casilla 5 A · J: «creo que la pantalla del expediente es buena inspiración». La voz de
   `.ex-ficha .ex-tabla` (abajo, en este mismo archivo) calcada: cabecera 10.5/700/.08em gris sobre
   claro, renglón con el aire de esa pantalla (e3), archivo en mono chico gris y acciones como
   ligas de texto. Medido en el HOY: CUATRO estilos de mayúsculas chicas en menos de 300 px;
   quedan las DOS voces de esa pantalla y ninguna más (rótulo/banda 10.5/800/.06em y cabecera
   10.5/700/.08em). */
.expdoc-t{width:100%;border-collapse:collapse;font-size:var(--fs-base)}
.expdoc-t th{text-align:left;font-size:var(--fs-rotulo);text-transform:uppercase;letter-spacing:.08em;
             color:var(--gray);background:var(--light);font-weight:700;white-space:nowrap;
             padding:var(--e2) var(--e3);border-bottom:1px solid var(--line)}
.expdoc-t th.expdoc-acch{text-align:right}
/* La banda de grupo. Desde `20260923170000` `expediente_tipo.grupo` existe, asi que esto YA se
   ve; el rotulo lo pone `expOpBandaRotulo` con la lista de `portal.js` (14.98). Va en VERSALITAS,
   como los demas encabezados de esta app: en el DOM el texto sigue siendo «Pedimento y anexos». */
.expdoc-banda td{background:var(--light);color:var(--navy);font-weight:800;font-size:var(--fs-rotulo);
                 letter-spacing:.06em;text-transform:uppercase;padding:7px 10px;
                 border-bottom:1px solid var(--line)}
.expdoc-cuenta{float:right;font-weight:600;font-size:var(--fs-sm);color:var(--gray);letter-spacing:0;
               text-transform:none}
.expdoc-tr td{padding:var(--e3);border-bottom:1px solid var(--line);vertical-align:middle}
.expdoc-tr:hover td{background:var(--light)}
.expdoc-num{display:inline-block;min-width:26px;color:var(--gray);font-variant-numeric:tabular-nums;
            font-size:var(--fs-sm);font-weight:600}
.expdoc-tipo b{font-weight:600;color:var(--text)}
.expdoc-doc{color:var(--gray)}
.expdoc-arch{font-family:var(--font-mono);font-size:var(--fs-xs);color:var(--gray);word-break:normal}
.expdoc-peso{font-size:var(--fs-xs);color:var(--gray);margin-left:10px;
             font-variant-numeric:tabular-nums;white-space:nowrap}
.expdoc-fecha{color:var(--gray);font-size:var(--fs-sm);white-space:nowrap;
              font-variant-numeric:tabular-nums}
/* Las acciones, como en la pantalla de Expediente: ligas de texto y no cajas de botón. Medido en
   el HOY: «PDF» medía 33 px y el ⋮ 27, sin centro común. El ⋮ se acota AQUÍ y la regla global
   `.rowmenu` (arriba) NO se toca: la comparten el libro, los movimientos y el catálogo. */
.expdoc-acc{text-align:right;white-space:nowrap}
.expdoc-acc .btn{background:none;border:none;padding:0;height:auto;margin-left:12px;
                 color:var(--navy);font-size:var(--fs-sm);font-weight:600;text-decoration:underline;
                 text-decoration-color:var(--line);text-underline-offset:3px;cursor:pointer}
.expdoc-acc .btn:hover{text-decoration-color:var(--navy);background:none}
.expdoc-acc .btn, .expdoc-acc .rowmenu{vertical-align:middle}
.expdoc-acc .rowmenu{height:24px;width:24px;padding:0;margin-left:12px;font-size:var(--fs-md);
                     line-height:22px;text-align:center}

/* ══ LIBRO_V6 · el libro (bocado 1, 24-ago) — vive detras de la bandera ═══════════════════════
   La pestaña Dinero como UN estado de cuenta (disenos/dinero-v6-libro.html + rebote 2).
   Todo en tokens (tokens.css); el mueble cafe usa el par calido NOMBRADO (--recon-*), que es
   la decision de J del 1-ago: lo calido señala donde te detienes a verificar. */
.libro{ background:var(--surface); border:1px solid var(--line); border-radius:var(--r-md);
  overflow:hidden; margin:var(--e3) 0 }
table.lb{ width:100%; border-collapse:collapse; font-size:var(--fs-base);
  font-variant-numeric:tabular-nums }
table.lb th{ background:var(--light); border-bottom:2px solid var(--line);
  font-size:var(--fs-xs); text-transform:uppercase; letter-spacing:.05em; color:var(--gray);
  font-weight:700; text-align:left; padding:var(--e2) var(--e3) }
table.lb th.num, table.lb td.num{ text-align:right; white-space:nowrap }
table.lb td{ padding:var(--e2) var(--e3); border-top:1px solid var(--line) }
table.lb tr.grp td{ background:var(--light); font-size:var(--fs-xs); letter-spacing:.06em;
  text-transform:uppercase; font-weight:800; color:var(--navy) }
table.lb tr.grp td .n{ font-weight:500; text-transform:none; letter-spacing:0;
  color:var(--gray); margin-left:var(--e2) }
/* Las celdas NUMERICAS de la fila del grupo son importes, no rotulos: tipografia normal. */
table.lb tr.grp td.num{ text-transform:none; letter-spacing:0; font-size:var(--fs-base) }
table.lb tr.grp td .mut{ text-transform:none; letter-spacing:0 }
table.lb tr.sub td{ font-weight:700; background:var(--light) }
table.lb tr.lb-tgl{ cursor:pointer }
table.lb tr.lb-tgl:hover td{ background:var(--bg) }
.lb-caret{ display:inline-block; width:14px; color:var(--gray); font-size:var(--fs-xs) }
table.lb tr.saldo td{ border-top:2px solid var(--navy); font-weight:700 }
table.lb tr.saldo td.lb-pend{ font-weight:400 }
/* D-139 T1 · la celda del TOTAL DE CARGOS de la fila del saldo. Es un total, pero NO es EL
   número —el número es el saldo estimado, a su derecha— así que no compite con él. */
table.lb tr.saldo td.tot{ color:var(--text); font-weight:600 }
/* D-139 T1 · LOS DOS SUMANDOS DEL SALDO ESTIMADO, debajo y en chico: la misma forma que el portal
   ya usa en su héroe desde la 15.22 (D-135). Sin borde arriba: cuelga de la fila del saldo, no es
   otra fila de datos — y eso es también lo que le dice al ojo que no suma. */
table.lb tr.lb-sumandos td{ border-top:0; padding-top:0; text-align:right;
  font-size:var(--fs-sm); color:var(--gray) }
table.lb tr.sinclas td{ background:var(--light) }
table.lb .mut{ color:var(--gray); font-weight:400; font-size:var(--fs-sm) }
table.lb .okc{ color:var(--ok) } table.lb .warnc{ color:var(--warn) } table.lb .malc{ color:var(--danger) }
table.lb .hueco{ color:var(--warn); font-style:italic }
.lb-fo{ display:inline-block; font-size:var(--fs-xs); color:var(--navy); background:var(--light);
  border:1px solid var(--line); border-radius:99px; padding:0 var(--e2); margin-left:var(--e2);
  white-space:nowrap; cursor:pointer; text-decoration:underline }
.lb-parte{ display:inline-block; font-size:var(--fs-xs); color:var(--text);
  background:var(--recon-bg); border:1px solid var(--recon-line); border-radius:99px;
  padding:0 var(--e2); margin-left:var(--e2); white-space:nowrap }
.lb-rev{ display:inline-block; font-size:var(--fs-xs); color:var(--warn); background:var(--warn-bg);
  border:1px solid var(--warn-border); border-radius:99px; padding:0 var(--e2); margin-left:var(--e2);
  white-space:nowrap }
/* ══ OPR-16 · LAS PUERTAS DE ALTA DENTRO DE LA TABLA ═════════════════════════════════════════
   El «+» de la fila del grupo y el «+ Agregar …» del ultimo renglon. Sin un solo hex ni un solo
   tamaño de fuente crudo: todo en tokens (el trinquete cuenta hexEnJS y tamanosDeFuente). */
.lb-mas{ display:inline-block; width:18px; height:18px; line-height:1; text-align:center;
  border:1px solid var(--line); border-radius:var(--r-sm); background:var(--surface);
  color:var(--navy); font:inherit; font-size:var(--fs-sm); font-weight:800; cursor:pointer;
  margin-left:var(--e2); text-transform:none; letter-spacing:0; padding:0 }
.lb-mas:hover{ background:var(--light) }
/* El «+ Add line» de siempre: ULTIMO renglon de su grupo y ancho de tabla, para que se lea como
   parte de la tabla y no como un control suelto flotando debajo. */
table.lb tr.lb-add td{ color:var(--navy); font-weight:600; cursor:pointer;
  border-top:1px dashed var(--line) }
table.lb tr.lb-add:hover td{ background:var(--light) }
/* La fila EN CAPTURA. El selector NO ocupa la celda entera: un desplegable de 450 px para
   «Almacenajes» se lee como un formulario, que es justo lo que esta fila viene a matar. */
table.lb tr.lb-alta td{ background:var(--ok-bg) }
table.lb tr.lb-alta select, table.lb tr.lb-alta input{ font:inherit; font-size:var(--fs-base);
  border:1px solid var(--ok-border); border-radius:var(--r-sm); padding:1px var(--e1);
  background:var(--surface); color:var(--text) }
/* MEDIDO en Chrome a 1440 (foto-1): sin tope el <select> se estira al ancho de su opcion mas
   larga —770 px en el catalogo real— y ocupa la celda entera, que es justo lo que la hoja pide
   evitar: un desplegable asi se lee como un formulario, y el formulario es lo que esta fila viene
   a matar. `width:auto` para que no herede el 100% de arriba. */
table.lb tr.lb-alta .lb-alta-con{ width:auto; min-width:180px; max-width:340px }
table.lb tr.lb-alta .lb-alta-monto{ width:110px; text-align:right }
/* OPR-16 H-4 (7-sep) · el selector se ABRE de `tr.lb-alta` a toda la tabla porque la casilla ya
   no vive en una sola fila: la pinta `ivaCasillaHTML`, y desde la corrección de la auditoría
   también la fila ↯. Sin esto la casilla del ↯ salía SIN estilo, y eso no es «se ve distinto»:
   la regla global `input,select,textarea{width:100%}` (~:75) alcanza a las casillas, que es el
   defecto exacto que J cazó el 4-sep en el expediente («esto se ve horrible»). El segundo renglón
   —`width:auto`— es el que la saca de esa regla, y tiene que alcanzar a las dos filas o la
   segunda nace rota. Mismo control, mismo trato. */
table.lb .lb-alta-iva{ margin-left:var(--e3); font-size:var(--fs-sm); color:var(--gray);
  white-space:nowrap }
table.lb .lb-alta-iva input{ width:auto; border:0 }
.lb-alta-pista{ font-size:var(--fs-xs); color:var(--gray) }
/* Los botones de la fila ↯ van en la celda de SALDO, no pegados al texto: medido en Chrome a
   1024, 1280, 1440 y 1920 — pegados no caben a NINGUN ancho y parten la fila en dos renglones.
   OPR-16 v3 (7-sep) · `.lb-decir` murio con la etiqueta «Decir qué paga…»: el boton de esa celda
   ahora es «Aplicar» y debajo cuelga el enlace del caso raro. */
.lb-aplicar{ white-space:nowrap }
.lb-enl{ font-size:var(--fs-sm) }   /* sobre .linklike, que es quien lo pinta como enlace */
/* En forma B el enlace del 5% va EN LA MISMA LINEA que «Aplicar» y ya no debajo: el `<br>` murio
   en `renderOpDetalle`. Susurra —paso chico y gris— porque es el caso raro. */
table.lb tr.sinclas .lb-enl{ margin-left:var(--e2); font-size:var(--fs-xs) }
/* ══ LA FILA ↯ EN FORMA B (acta de J, 7-sep) · UNA SOLA LINEA ══════════════════════════════
   Todo lo de abajo sale de UNA medicion en Chrome de verdad sobre el HTML que `render-real.js`
   pinta de KG26M-0101 y KG26M-0036, a 1024/1280/1440 — 60 combinaciones, y ninguna desborda la
   tabla ni el documento. Los numeros, para que nadie los tenga que volver a adivinar:
     · fila ↯ APILADA (hasta el 6-sep) 67 px · en FORMA B 38 px
     · una fila NORMAL del libro: 32 px (KG26M-0101) y 39 px (KG26M-0036)
     · el desplegable mide 21 px y una linea de texto 16: por eso 38 no baja mas mientras la fila
       tenga un `<select>`, y por eso «la misma altura» se cumple como RANGO y no como identidad.
   El tope del desplegable NO se toca: son los mismos 180-340 px de `.lb-alta-con`, ya medidos. */
table.lb tr.sinclas .lb-pick{ font:inherit; font-size:var(--fs-base); width:auto;
  min-width:180px; max-width:340px; margin-top:0; vertical-align:middle;
  border:1px solid var(--c-gray-300); border-radius:var(--r-sm);
  padding:0 var(--e1); line-height:1.3;
  background:var(--surface); color:var(--text) }
/* LA PLOMERIA DEL BANCO · fecha y descripcion, en el paso chico y en gris — la gramatica de la
   casa: el dato habla, la plomeria susurra. Se corta con ELIPSIS y no contando caracteres,
   porque lo que desborda son pixeles: 40 caracteres de «PAGO POR PEDIMTO AD 38126000013» no
   miden lo mismo que 40 de «Cargo diverso». El tope son los 150 px que `.userlabel` ya usa para
   esto mismo (~:1164) — no se estrena un numero; medido, cabe en los tres anchos (el barrido dio
   que a 1024 el maximo es 160). */
table.lb tr.sinclas .lb-plom{ display:inline-block; max-width:150px; overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap; vertical-align:middle;
  font-size:var(--fs-xs); color:var(--gray); margin-left:var(--e2) }
/* EL BOTON DE ESTA FILA · `.btn.sm` lleva 8 px de relleno vertical y mide 35 px: era EL que hacia
   la fila mas alta que las demas (52 px contra 38). Aqui usa el relleno de la maqueta que J
   aprobo —2 px— y solo aqui: el boton no cambia de tamaño en ninguna otra pantalla. */
table.lb tr.sinclas .lb-aplicar{ padding-top:2px; padding-bottom:2px; line-height:1.3 }
.lb-pie{ display:flex; gap:var(--e2); padding:var(--e2) var(--e3);
  border-top:1px solid var(--line); flex-wrap:wrap; align-items:center }
/* AQUI VIVIO `.lb-docstop`, el contenedor de los dos documentos en el pie del libro. Murio con
   `ctx.docsHTML` en OPR-16 v3 casilla 4: los documentos subieron a `.opd-actions`. */
/* Las herramientas de fila (✕ ✎) solo al pasar el mouse o al enfocar: el rebote de J
   (24-ago, «lista muy larga») — el renglon lee limpio y la herramienta esta cuando se busca. */
table.lb .opc-del, table.lb .opc-ed{ opacity:0; transition:opacity .12s }
table.lb tr:hover .opc-del, table.lb tr:hover .opc-ed,
table.lb tr:focus-within .opc-del, table.lb tr:focus-within .opc-ed{ opacity:1 }
.lb-cafe{ margin-top:var(--e3); background:var(--recon-bg); border:1px solid var(--recon-line);
  border-radius:var(--r-md); overflow:hidden }
.lb-cafe .gt{ display:flex; align-items:center; gap:var(--e2); padding:var(--e2) var(--e3);
  border-bottom:1px solid var(--recon-line); font-size:var(--fs-xs); letter-spacing:.06em;
  text-transform:uppercase; font-weight:800; color:var(--gray) }
.lb-cafe .gt .lock{ margin-left:auto; font-weight:600; font-size:var(--fs-xs); color:var(--warn);
  background:var(--warn-bg); border:1px solid var(--warn-border); border-radius:99px;
  padding:0 var(--e2); text-transform:none; letter-spacing:0 }
.lb-cafe table{ width:100%; border-collapse:collapse; font-size:var(--fs-base);
  font-variant-numeric:tabular-nums }
.lb-cafe td{ padding:var(--e2) var(--e3); border-top:1px solid var(--recon-line) }
.lb-cafe td.num{ text-align:right; white-space:nowrap }
.lb-cafe tr:first-child td{ border-top:0 }
.lb-cafe tr.marg td{ border-top:2px solid var(--text); font-weight:700 }

/* ══ EL ESTADO DE CUENTA DEL PORTAL · E2|2 ══════════════════════════════════════════════════
   Las clases de `app-web/portal.js`. Todas con tokens: `hexEnJS` y `tamanosDeFuente` son
   trinquetes que sólo pueden bajar, así que aquí no hay ni un color literal ni un `font-size`
   en píxeles fuera de la escala. Va FUERA del bloque de PIEZAS a propósito: `piezas.test.js`
   exige que toda pieza de ese bloque esté documentada en `disenos/piezas.html`, y esto no es una
   pieza del sistema — es la hoja de UNA pantalla. */
.pe-barra{ display:flex; justify-content:space-between; align-items:baseline; gap:var(--e3);
  margin-bottom:var(--e3) }
/* CASILLA 1 de E2|3 (J, 3-sep) · el NOMBRE DE LA EMPRESA debajo del título; el correo se queda
   donde estaba, a la derecha. El correo identifica a la persona que entró, no a la empresa cuyo
   libro se está leyendo. El nombre sólo se pinta cuando llega —hoy ninguna vista lo trae, y por
   qué está medido en `portal.js`—, así que `.pe-titulo` con un solo hijo se ve idéntico a la
   barra de antes: esta hoja no cambia un píxel mientras `s.empresa` no exista. */
.pe-titulo{ display:flex; flex-direction:column; gap:var(--e1) }
.pe-empresa{ font-weight:600; color:var(--navy); font-size:var(--fs-sm) }
/* `.pe-tabs`/`.pe-tab` MURIERON en la 14.95: la navegacion del portal es `.portal-items`. Las
   reglas se van con ellas — dejarlas invita a repintar la tira vieja al lado del menu nuevo. */
.pe-hero{ background:var(--surface); border:1px solid var(--line); border-radius:var(--r-caja);
  padding:var(--e4); margin-bottom:var(--e4) }
.pe-hero-top{ display:flex; justify-content:space-between; align-items:flex-end; gap:var(--e3);
  flex-wrap:wrap }
.pe-hero-etq{ font-size:var(--fs-rotulo); letter-spacing:.08em; text-transform:uppercase;
  color:var(--gray); font-weight:700 }
.pe-hero-sub{ color:var(--gray); font-size:var(--fs-sm) }
.pe-hero-cifra{ font-size:var(--fs-xl); font-weight:700; font-variant-numeric:tabular-nums;
  white-space:nowrap }
.pe-reparto{ display:flex; gap:var(--e5); flex-wrap:wrap; margin-top:var(--e3);
  padding-top:var(--e3); border-top:1px solid var(--line) }
.pe-rp-n{ font-size:var(--fs-lg); font-weight:700; font-variant-numeric:tabular-nums }
.pe-rp-l{ color:var(--gray); font-size:var(--fs-sm) }
.pe-rp-l em{ display:block; font-style:normal; color:var(--gray-soft); font-size:var(--fs-xs) }
.pe-periodo{ display:flex; justify-content:space-between; align-items:center; gap:var(--e3);
  flex-wrap:wrap; margin-bottom:var(--e2); color:var(--gray); font-size:var(--fs-sm) }
.pe-select{ font:inherit; padding:var(--e1) var(--e2); border:1px solid var(--line);
  border-radius:var(--r-sm); background:var(--surface); color:var(--text) }
.pe-aviso{ background:var(--surface); border:1px solid var(--line); border-radius:var(--r-caja);
  padding:var(--e4) }
.pe-tabla{ width:100%; border-collapse:collapse; font-size:var(--fs-sm) }
.pe-tabla th{ text-align:left; font-size:var(--fs-rotulo); letter-spacing:.08em;
  text-transform:uppercase; color:var(--gray); background:var(--light); font-weight:700;
  border-bottom:1px solid var(--line); padding:var(--e2); white-space:nowrap }
.pe-tabla td{ padding:var(--e2); border-bottom:1px solid var(--line); vertical-align:top }
.pe-tabla td.num,.pe-tabla th.num{ text-align:right; white-space:nowrap;
  font-variant-numeric:tabular-nums }
.pe-tabla td.ref,.pe-tabla td.pe-fecha{ font-family:var(--font-mono); white-space:nowrap }
.pe-tabla th.pe-c-fecha{ width:9% }
.pe-tabla th.pe-c-op{ width:16% }
.pe-tabla th.pe-c-saldo{ width:18% }
/* J-14 + casilla 1 de E2|2 · el color va ADEMÁS de la palabra, nunca en su lugar: un estado de
   cuenta se imprime y se manda por correo, y en gris «a favor» y «por cobrar» serían el mismo
   renglón. La palabra la pinta `portalPildoraHTML`; esto sólo la acompaña. */
.pe-pos{ color:var(--ok) }
.pe-neg{ color:var(--danger) }
.pe-dir{ display:block; font-size:var(--fs-xs); color:var(--gray); font-weight:400 }
.pe-tabla tr.pe-libre td{ background:var(--light); font-weight:700 }
.pe-tabla tr.pe-apertura td{ background:var(--light); color:var(--gray); font-weight:700 }
.pe-tabla tr.pe-plegado td{ background:var(--light); color:var(--gray) }
/* CASILLAS 3 y 8 de E2|3 (J, 3-sep) · EL CONTROL DEL PLEGABLE, uno solo para los dos bloques.
   Es un `<button>` de verdad —tabulable, con su `aria-expanded`— vestido como el texto que
   acompaña: un botón con marco al lado de cada concepto sería ruido en un estado de cuenta, y el
   conteo («3 conceptos») ya dice que hay algo que abrir. El foco SÍ se ve, o quien navegue con
   el teclado lo haría a ciegas. La flecha ocupa un ancho fijo para que ▸ y ▾ no muevan el texto
   al abrir y cerrar. */
.pe-plie{ border:0; background:none; padding:0; margin:0; font:inherit; color:var(--gray);
  cursor:pointer; white-space:nowrap; text-align:left }
.pe-plie:hover{ color:var(--navy) }
.pe-plie:focus-visible{ outline:2px solid var(--navy); outline-offset:2px;
  border-radius:var(--r-sm) }
.pe-plie-f{ display:inline-block; width:1em }
/* Las operaciones cerradas, ya abiertas: llevan `pe-plegado` porque son el mismo bloque (el
   porqué está en `portal.js`), y aquí se les devuelve el aspecto de un renglón normal — son
   operaciones del cliente, no un pie de tabla. Va DESPUÉS de `.pe-plegado` a propósito: misma
   especificidad, gana el orden. */
.pe-tabla tr.pe-cerrada td{ background:var(--surface); color:var(--text) }
.pe-tabla tr.pe-total td{ border-top:2px solid var(--navy); background:var(--light);
  font-weight:700 }
/* El desglose de J-8 cuelga del renglón y se ve como lo que es: un mueble interno dentro de la
   fila del banco, no otra fila del libro. */
.pe-tabla tr.pe-det td{ background:var(--interno-bg); color:var(--interno-ink);
  font-size:var(--fs-xs); border-bottom:0 }
.pe-tabla tr.pe-det td.pe-p{ padding-left:var(--e5) }

/* ══ TANDAS 3 y 4 · EL DETALLE DE LA OPERACIÓN, AL LADO DE «MI CUENTA» (J, 11-sep) ════════════
   Calcado de la maqueta que J aprobó (`ROTULO-Y-CUADRO-v1.html`, md5 sin CR
   78f256b3e430452f21ec992d2a3b65d7, medida en Chrome real a 1280). Ni un color literal ni un
   `font-size` en px fuera de la escala: `hexEnJS` y `tamanosDeFuente` son trinquetes que sólo
   bajan, y todo lo de aquí sale de `tokens.css`.

   LA REJILLA. El maestro a la izquierda y el cuadro AL LADO (casilla e de J, 9-sep). El cuadro es
   un HERMANO de la tabla y no un `<tr>`: la tabla conserva sus cinco columnas —`render-invariantes`
   lee el saldo por POSICIÓN [4]— y el plegado móvil que ya existe no se toca.
   `minmax(0, 568px)` y no `568px` a secas: con el ancho fijo, una ventana angosta empujaría la
   página en horizontal, que es justo lo que `movil.test.js` existe para impedir. */
.pe-md{ display:grid; grid-template-columns:minmax(0,1fr); gap:var(--e4); align-items:start }
.pe-md-abierto{ grid-template-columns:minmax(0,568px) minmax(0,1fr) }
/* D-120 · ESCRITORIO MANDA, EL TELÉFONO PLIEGA. Un solo `@media`, y el cuadro cae DEBAJO del
   maestro en vez de encogerse hasta no leerse. Entra a 1100 y no a 600 porque lo que decide no es
   «teléfono» sino si los dos caben: el reparto de la maqueta pide 1 186 px de panel útil. */
@media (max-width: 1100px){ .pe-md-abierto{ grid-template-columns:minmax(0,1fr) } }
/* El renglón abierto se señala con la barra de la marca a la izquierda, ADEMÁS del fondo: en una
   impresión en gris el fondo solo no distingue nada. Va después de `.pe-cerrada` a propósito. */
.pe-tabla tr[data-accion="por:operacion"]{ cursor:pointer }
.pe-tabla tr.pe-abierta td{ background:var(--light) }
.pe-tabla tr.pe-abierta td:first-child{ box-shadow:inset 3px 0 0 var(--navy) }
/* EL CUADRO. Cabecera navy con el folio y su saldo —las palabras de la vista (J-1), nunca la
   palabra «cerrada» (decisión 4)— y los bloques debajo, separados por su propia línea. */
/* ⚠ SIN `overflow:hidden`, y hay que decir por qué o el siguiente lo vuelve a poner para redondear
   la cabecera: con él, una relación de gastos más ancha que el cuadro se CORTARÍA en silencio en
   vez de desplazarse dentro de su `tablewrap plain` — y un renglón truncado se ve completo
   (familia B). La esquina redonda la pone la propia cabecera, que es quien la necesita. */
.pe-cuadro{ background:var(--surface); border:1px solid var(--line);
  border-radius:var(--r-caja) }
.pe-ct{ background:var(--navy); color:var(--surface); padding:var(--e2) var(--e3);
  border-radius:var(--r-caja) var(--r-caja) 0 0;
  display:flex; justify-content:space-between; align-items:center; gap:var(--e3) }
.pe-ct b{ font-family:var(--font-mono); font-size:var(--fs-md) }
.pe-ct-est{ font-size:var(--fs-sm) }
/* La píldora del saldo dentro de la cabecera: la palabra va al lado y no debajo, o el renglón de
   la cabecera crecería el doble. `.pe-dir` es la misma de la tabla — no se estrena ninguna. */
.pe-ct-est .pe-dir{ display:inline; margin-left:var(--e2); color:var(--gray-soft) }
/* El rótulo de cada sección. Mismas reglas que `.seclabel` de la app interna —uppercase, el paso
   del micro-rótulo, peso 800— pero en el gris del portal y con el relleno del cuadro. */
.pe-cs{ font-size:var(--fs-rotulo); letter-spacing:.08em; text-transform:uppercase;
  color:var(--gray); font-weight:800; padding:var(--e3) var(--e3) var(--e1);
  border-top:1px solid var(--line) }
.pe-bloque:first-child .pe-cs:first-child{ border-top:0 }
/* «Datos del pedimento» · tres columnas, y sólo con lo capturado (decisión 5). */
.pe-ident{ display:grid; grid-template-columns:repeat(3, minmax(0,1fr));
  gap:0 var(--e3); padding:0 var(--e3) var(--e2) }
.pe-ident div{ font-size:var(--fs-sm); padding:3px 0; border-bottom:1px dotted var(--line);
  min-width:0; overflow-wrap:anywhere }
.pe-ident em{ display:block; font-style:normal; font-size:var(--fs-rotulo); letter-spacing:.05em;
  text-transform:uppercase; color:var(--gray-soft) }
/* «Estatus» · sólo los hitos que ya pasaron; su fecha a la derecha, en columna. */
.pe-hitos{ list-style:none; margin:0; padding:0 var(--e3) var(--e3) }
.pe-hitos li{ display:grid; grid-template-columns:20px minmax(0,1fr) auto; gap:var(--e2);
  align-items:baseline; padding:var(--e1) 0; border-bottom:1px dotted var(--line);
  font-size:var(--fs-sm) }
.pe-hito-p{ font-size:var(--fs-xs); line-height:16px; text-align:center; font-weight:700;
  border-radius:var(--r-pill); background:var(--ok-bg); color:var(--ok);
  border:1px solid var(--ok-border) }
.pe-hito-f{ text-align:right; font-variant-numeric:tabular-nums; white-space:nowrap;
  color:var(--gray) }
.pe-hito-folio{ display:inline-block; margin-left:var(--e2); font-size:var(--fs-rotulo);
  color:var(--gray); border:1px solid var(--line); border-radius:var(--r-sm); padding:0 4px }
/* La relación de gastos · Concepto · Pagado el · Importe, y su subtotal. */
.pe-rel{ width:100%; border-collapse:collapse; font-size:var(--fs-sm) }
.pe-rel th{ text-align:left; font-size:var(--fs-rotulo); letter-spacing:.05em;
  text-transform:uppercase; color:var(--gray); font-weight:700;
  border-bottom:1px solid var(--line); padding:var(--e1) var(--e3) }
.pe-rel td{ padding:var(--e1) var(--e3); border-bottom:1px dotted var(--line);
  vertical-align:top; min-width:0; overflow-wrap:anywhere }
.pe-rel td.num, .pe-rel th.num{ text-align:right; white-space:nowrap;
  font-variant-numeric:tabular-nums; overflow-wrap:normal }
.pe-rel td.pe-rel-f{ white-space:nowrap; overflow-wrap:normal }
.pe-rel td .muted{ display:block; font-size:var(--fs-xs) }
.pe-rel td.pe-rel-f .muted{ display:inline }
.pe-rel tr.pe-rel-sub td{ font-weight:700; background:var(--light);
  border-bottom:1px solid var(--line) }
/* «Servicios de la agencia» en UNA línea (decisión 7): rótulo a la izquierda, total a la derecha.
   No es una tabla de un renglón — es una línea, y se escribe como tal. */
.pe-linea{ display:flex; justify-content:space-between; gap:var(--e3);
  padding:var(--e1) var(--e3); font-size:var(--fs-sm);
  border-bottom:1px dotted var(--line) }
.pe-linea .num{ font-variant-numeric:tabular-nums; white-space:nowrap }
/* La nota al pie de un bloque: el pendiente de pago y la del IVA. */
.pe-nota{ font-size:var(--fs-xs); color:var(--gray); padding:var(--e2) var(--e3) var(--e3) }

/* ══ CLI-5 · EL EXPEDIENTE DE ALTA EN EL PORTAL · las ocho casillas de D-102 (J, 3-sep) ═══════
   Traducidas de la hoja `disenos/cli-5-expediente-portal-v-final.html` (md5 sin CR 52343e03…) a los
   tokens de la app — ni un color literal ni un `font-size` en px fuera de la escala (`hexEnJS` y
   `tamanosDeFuente` son trinquetes que solo bajan). La tabla es `pe-tabla pe-exp`, asi que hereda el
   plegado movil de las otras dos del portal (el `@media` de 600 px de mas abajo). */
.pe-ex-avance{ background:var(--surface); border:1px solid var(--line); border-radius:var(--r-caja);
  padding:var(--e3) var(--e4); margin-bottom:var(--e4) }
.pe-ex-avance-n{ font-size:var(--fs-lg); font-weight:700 }
.pe-ex-barra{ height:8px; background:var(--light); border-radius:var(--r-pill); margin-top:var(--e2);
  overflow:hidden }
/* El ancho lo pone `renderPortalEstado` con `.style.width` desde `data-w` — no un `style="width"`
   en el fuente, que el trinquete `estilosInline` contaria. Nace en 0 y crece al pintarse. */
.pe-ex-lleno{ height:100%; width:0; background:var(--ok); border-radius:var(--r-pill) }
.pe-ex-resto{ font-size:var(--fs-sm); color:var(--gray); margin-top:var(--e2) }
/* TRES COLUMNAS FIJAS · Ver · Historial · Accion (J, rebote del 7-sep: «pon el historial en una
   columna, en claro si no hay y prendido si si»). Lo que no aplica se ve EN CLARO en vez de
   desaparecer: todos los renglones tienen exactamente las mismas casillas y la fila se lee en
   columna — es la regla que mata el borde de serrucho que J vio. */
.pe-exp th.pe-ex-c-doc{ width:34% }
.pe-exp th.pe-ex-c-est{ width:22% }
.pe-exp th.pe-ex-c-ver{ width:10% }
.pe-exp th.pe-ex-c-hist{ width:14% }
.pe-exp th.pe-ex-c-acc{ width:20% }
.pe-exp td.pe-ex-ver, .pe-exp td.pe-ex-hist{ text-align:left; white-space:nowrap }
.pe-ex-ver .pe-ex-btn, .pe-ex-hist .pe-ex-btn, .pe-ex-hist .pe-plie{ margin-left:0 }
/* APAGADO, no ausente: mismo tamano y misma caja que el prendido, sin borde de marca y sin
   cursor de mano. No es un `<button disabled>` sino un `<span>` — un boton que no hace nada sigue
   recibiendo el foco del tabulador y prometiendo un gesto que no existe. */
.pe-ex-btn.off{ border-color:var(--line); color:var(--gray); background:var(--light);
  cursor:default }
.pe-ex-doc{ font-weight:600; color:var(--text) }
.pe-ex-archivo{ display:block; font-family:var(--font-mono); font-size:var(--fs-xs); color:var(--gray);
  margin-top:2px; word-break:break-all }
/* E5|2 P3 · EL MOTIVO de quien reviso, SIEMPRE visible bajo el nombre del archivo (J, 6-sep) — ni
   plegado ni en un tooltip. Es la UNICA clase nueva que el renglon del cliente estrena.
   Sin colores literales ni px fuera de la escala: `hexEnJS` y `tamanosDeFuente` son trinquetes que
   solo bajan. La caja neutra es la del mueble interno, la MISMA de las versiones anteriores.
   `.rech` la tine de rojo, y solo cuando el veredicto es un RECHAZO: pintar de rojo la nota de un
   documento aceptado seria que la pantalla afirme un problema que no existe. */
.pe-ex-motivo{ display:block; font-size:var(--fs-xs); color:var(--interno-ink);
  background:var(--interno-bg); border:1px solid var(--line); border-radius:var(--r-sm);
  padding:var(--e1) var(--e2); margin-top:var(--e2) }
.pe-ex-motivo.rech{ color:var(--danger); background:var(--danger-bg); border-color:var(--danger-border) }
/* C6 · «Subida el …» encima del nombre de cada version anterior (casilla 2 = B, J 4-sep). Es la
   fecha en que se CARGO el archivo, no la del documento — el rotulo lo dice y no cuesta un pixel
   (medido en los cuatro anchos de telefono y los cinco de compu). Va con la tinta del mueble
   interno, no con `--gray`: la fila vive sobre `--interno-bg`. Sin px literales — el trinquete
   `tamanosDeFuente` cuenta `font-size:<n>px` y solo puede bajar. */
.pe-ex-vfecha{ display:block; font-size:var(--fs-xs); color:var(--interno-ink) }
.pe-ex-est{ display:inline-block; font-size:var(--fs-xs); font-weight:700; padding:1px 8px;
  border-radius:var(--r-pill); border:1px solid transparent }
.pe-ex-est.ok{     background:var(--ok-bg);     color:var(--ok);     border-color:var(--ok-border) }
.pe-ex-est.pend{   background:var(--light);     color:var(--gray);   border-color:var(--line) }
/* UN COLOR POR CHIP (J, 7-sep). Hasta hoy Pendiente, Por confirmar y En revision compartian el
   gris, y Rechazado y Vencido compartian el rojo: seis palabras, cuatro colores. Ahora el color
   contesta DE QUIEN ES EL TURNO — del cliente (pend · rech · venc), de la casa (espera · llego),
   de nadie (ok). RECHAZADO se queda con el rojo, que es lo unico que el cliente tiene que
   atender; VENCIDO pasa al ambar, que es donde la maqueta lo puso: es un plazo que se cumplio,
   no un veredicto en contra. `.pronto` («Vence el …») comparte el ambar con `.venc` a proposito —
   son el mismo hecho a dos distancias, y la maqueta los dibuja iguales.
   ⚠ Lo que NO se probo: los dos azules con nadie daltonico. Se distinguen por tono, no por
     forma; queda dicho, no escondido. */
.pe-ex-est.rech{   background:var(--danger-bg); color:var(--danger); border-color:var(--danger-border) }
.pe-ex-est.venc{   background:var(--warn-bg);   color:var(--warn);   border-color:var(--warn-border) }
.pe-ex-est.espera{ background:var(--espera-bg); color:var(--espera); border-color:var(--espera-border) }
.pe-ex-est.llego{  background:var(--llego-bg);  color:var(--llego);  border-color:var(--llego-border) }
.pe-ex-est.pronto{ background:var(--warn-bg);   color:var(--warn);   border-color:var(--warn-border) }
.pe-ex-fecha{ display:block; font-size:var(--fs-xs); color:var(--gray); margin-top:3px }
.pe-ex-acc{ text-align:right; white-space:nowrap }
.pe-ex-btn{ display:inline-block; padding:var(--e1) var(--e3); font-size:var(--fs-sm);
  border-radius:var(--r-sm); border:1px solid var(--navy); background:var(--surface);
  color:var(--navy); cursor:pointer; margin-left:var(--e2); font-family:inherit }
.pe-ex-btn.g{ border-color:var(--line); color:var(--gray) }
.pe-ex-btn:hover{ border-color:var(--navy); color:var(--navy) }
.pe-ex-btn:focus-visible{ outline:2px solid var(--navy); outline-offset:2px }
/* Las versiones anteriores, ya abiertas: un mueble interno, como el desglose del libro. */
.pe-tabla tr.pe-ex-vrow td{ background:var(--interno-bg); color:var(--interno-ink);
  font-size:var(--fs-xs) }
.pe-ex-file{ display:none }   /* el <input file> oculto que recoge la subida (lo dispara «Subir») */
.pe-ex-pie{ font-size:var(--fs-sm); margin:var(--e4) 0 0 }
/* ── T-E|2 · «DOCUMENTOS DE LA OPERACIÓN» · el plegable al pie del cuadro (J, 13-sep) ─────────
   El control va en su propio renglón con el borde de arriba de una sección, pero SIN el
   `text-transform` de `.pe-cs`: ahí dentro vive un `<button>` con `font:inherit`, y en
   mayúsculas el rótulo dejaría de leerse como lo dibujó la maqueta. */
.pe-docs-ctrl{ padding:var(--e3); border-top:1px solid var(--line) }
.pe-docs-ctrl .pe-plie{ color:var(--navy); font-weight:700; white-space:normal;
  overflow-wrap:anywhere }
.pe-docs{ padding:0 var(--e3) var(--e3) }
.pe-docg{ margin-bottom:var(--e3) }
.pe-docg:last-child{ margin-bottom:0 }
/* La banda del grupo: el mismo navy de la cabecera del cuadro, y el conteo a la derecha. El
   número son RENGLONES, no archivos — las dos poblaciones no se mezclan (familia C). */
.pe-docg-h{ display:flex; justify-content:space-between; align-items:baseline;
  background:var(--navy); color:var(--surface); border-radius:var(--r-sm);
  padding:var(--e1) var(--e3); font-size:var(--fs-sm); font-weight:700; letter-spacing:.03em;
  margin-bottom:var(--e2) }
.pe-docg-h span{ font-weight:400; color:var(--gray-soft) }
/* El renglón. `align-items:center` y no `baseline`: los botones son cajas y con `baseline` se
   descuelgan cuando el nombre lleva su `<small>` debajo. El nombre PARTE (`overflow-wrap`) y los
   botones NO (`flex:0 0 auto`), que es lo que impide el scroll horizontal en el teléfono. */
.pe-doc{ display:flex; justify-content:space-between; align-items:center; gap:var(--e2);
  padding:var(--e1) 0; border-bottom:1px dotted var(--line) }
.pe-doc:last-child{ border-bottom:0 }
.pe-doc-nm{ font-size:var(--fs-sm); min-width:0; overflow-wrap:anywhere }
.pe-doc-nm small{ display:block; color:var(--gray-soft); font-size:var(--fs-xs);
  font-family:var(--font-mono) }
.pe-doc-bs{ flex:0 0 auto; display:flex; gap:var(--e1) }
/* Los botones son los MISMOS del expediente de alta (`.pe-ex-btn`): no se estrena ninguno. Aquí
   sólo se les quita el margen izquierdo, que ahí lo pone la columna de acciones. */
.pe-doc-bs .pe-ex-btn{ margin-left:0 }
/* CASILLA C5 · el encendido de condicionales en la ficha del lado agencia (`cli_exp`, app.js). */
.exp-cond{ display:flex; flex-direction:column; gap:var(--e2); margin-top:var(--e2) }
.exp-cond-row{ display:flex; align-items:center; gap:var(--e2); cursor:pointer }
/* 14.80 · J, 4-sep: «esto se ve horrible, no esta nada ordenado». No era gusto: la regla
   global `input,select,textarea{width:100%}` (arriba, ~:74) alcanza tambien a las CASILLAS,
   asi que cada palomita se dibujaba centrada en una caja de ~873 px y el rotulo se partia en
   dos renglones. Medido en Chrome sobre el render real a 1280: 872.9 / 906.9 / 837.1 / 887.2
   / 811.2 px de ancho, cinco centros distintos, bloque de 242 px.
   El primer renglon SACA la casilla de esa regla; los otros dos son las columnas que J
   escogio («En columnas, mas compacto»): 80 px de bloque a 1280, 1440 y 1920.
   ⚠ La regla global sigue alcanzando a TODA casilla de la app: esto la acota a `.exp-cond-row`
   y a nada mas. NO se midio si hay otras casillas rotas en otras pantallas. */
.exp-cond-row input[type="checkbox"]{ width:auto; padding:0; margin:0; flex:0 0 auto }
.exp-cond{ display:grid; grid-template-columns:repeat(auto-fill, minmax(280px, 1fr));
  gap:var(--e2) var(--e4) }
.exp-cond-row{ align-items:flex-start; padding:var(--e1) 0 }

/* ══ E2|2 · EL RENGLÓN EN DOS LÍNEAS · lo que J eligió viendo, el 3-sep ═══════════════════
   J vio tres opciones dibujadas a 390 px con los mismos ocho renglones reales y eligió la (B):
   en el teléfono, un renglón del libro son DOS líneas — arriba fecha · operación · concepto,
   abajo Cargo · Abono · Saldo alineados a la derecha, y el desglose indentado CON su concepto.
   (maqueta `disenos/E2-2-movil-tres-opciones.html`, md5 sin CR 7b6e624f… — entró al repo con
    esta misma tanda; su medición, en `disenos/E2-2-movil-tres-opciones-MEDICION.md`)

   SUSTITUYE A LA BANDA ANCLADA DE LA v3, que era el remedio de la opción (A) y ya no hace falta:
   si nada se desplaza, nada puede quedar debajo de un ancla ni cortado por el borde. Se va con
   ella todo su aparato — `position:sticky`, `--pe-num`, los cuatro `nth-last-child` y el
   `@media` de 374 —, y con él sus dos precios, que estaban DICHOS y medidos: el concepto se
   alcanzaba desplazando, y con él el del desglose.

   LO QUE CAMBIÓ DE VERDAD, medido sobre los 41 renglones reales de agosto de FAIST, en Chrome,
   con estos mismos tokens y en la misma corrida (`out/D3.out`):

                          esconde   importes      folios        conceptos          alto
     (A) la v3 @390          97 px   100 de 100    39 de 41      0 de 58           3 396 px
     (A) la v3 @375         111 px   100 de 100     0 de 41      0 de 58           3 396 px
     ESTO      @390           0 px   100 de 100    41 de 41     58 de 58           2 292 px
     ESTO      @320           0 px   100 de 100    41 de 41     58 de 58           2 410 px

   Los folios de (A) no eran un detalle: a 375 px se leían CORTADOS los 41 —«KG26A-0038» se leía
   «KG26A-003», que es otro folio plausible— y a 320 quedaban los 41 debajo del ancla. Y sus
   conceptos no se leían ENTEROS a ningún ancho.

   ── POR QUÉ SE PLIEGA LA <table> Y NO SE CAMBIA POR BLOQUES ─────────────────────────────────
   La maqueta dibujó (B) con `div`, y ésa era la pregunta abierta. Se midieron las dos formas con
   los mismos datos y el mismo Chrome, y la tabla plegada gana por dos razones medidas:
     · EL ÁRBOL DE ACCESIBILIDAD. Plegada, Chrome sigue publicando `table 1 · row 61 · cell 311 ·
       columnheader 6 · rowgroup 1`; con bloques no queda NADA (`generic` y texto suelto). Un
       estado de cuenta que un lector de pantalla no puede recorrer por columnas no es un estado
       de cuenta. La sonda sabe ver la diferencia — que es lo que la hace un dato y no un cero
       (regla Y): sobre los bloques sale vacía, sobre la tabla llena.
     · EL HTML NO CAMBIA NI UN BYTE. Una sola función de render, una sola fuente de datos, dos
       presentaciones; `portal.js`, `render.test.js`, `logic.test.js`, `render-real.js` y
       `movil-sitios.js` siguen viendo exactamente lo mismo. Con bloques habría dos markups y
       nadie mediría el segundo (regla 1).
   Y el alto sale igual o mejor: 2 292 px plegada contra 2 306 con bloques.

   ── LA REJILLA · POR QUÉ 24 COLUMNAS ────────────────────────────────────────────────────────
   Un `<tr>` es UN grid, así que sus dos líneas comparten plantilla: la única forma de que la
   línea de arriba reparta 4·8·12 y la de abajo 8·8·8 es que las dos partan de la misma rejilla
   uniforme. 24 y no 12 porque la partición que los datos piden no cabe en doceavos — medido con
   `width:max-content` sobre el libro completo: la fecha pide 40 px, la operación 83
   («ZAA-2026-0007», el folio más largo que existe) y cada importe 76.
   Lo que la rejilla entrega, medido a los cuatro anchos:
       @390  fecha  55 · operación 111 · concepto 166 · importes 111/111/111 · desglose 333
       @375         53 ·           106 ·          159 ·           106/106/106 ·          318
       @360         51 ·           101 ·          152 ·           101/101/101 ·          304
       @320         44 ·            89 ·          133 ·            89/ 89/ 89 ·          267
   Los tres importes son TERCIOS EXACTOS a propósito: es lo que los deja en columna de un renglón
   al siguiente, y una columna de importes que baila no es un libro. El día que un folio no quepa
   en su tramo, `movil.test.js` muerde nombrándolo — no se rompe en silencio.

   ── EL CONCEPTO ENVUELVE, NO SE CORTA ───────────────────────────────────────────────────────
   Con elipsis a una línea —lo que la maqueta dibujó— se perdían 6 conceptos a 360 px y 8 a 320
   («Honorarios de agencia aduanal»). Envolviendo, los 58 se leen enteros a los CUATRO anchos y
   el precio son 66 px de alto a 360 y 118 a 320, sobre 2 292. Medido: ningún concepto de
   movimiento pasa de DOS líneas a ningún ancho, así que el renglón sigue siendo el que J vio.

   ── EL CORTE SUBIÓ A 600 px · CASILLA 5 de E2|3 (J, 3-sep) · cierra la ficha CSS-4 ──────────
   Nació en 480 —el `@media(max-width:480px)` que ya existía en esta hoja— y eso dejaba una
   franja medida: la tabla de seis columnas cabe hasta 640 px de viewport (caja 572, pide 572) y
   deja de caber a 560 (caja 496, pide 525), así que ENTRE 481 y ~600 px seguía desplazándose y
   a 481 escondía 103 px. No es un teléfono: es media pantalla de computadora, y por eso la
   decisión era de J y no mía (ficha CSS-4, `PARKING-TECNICO.md:3435`). J eligió 600 el 3-sep
   viendo la franja dibujada a 520 px.
   El precio, DICHO: a 600 px caben seis columnas apretadas y el plegado gasta casi el doble de
   alto, así que alguien con la ventana a medio ancho ve formato de teléfono en una computadora
   donde la tabla casi cabía. Lo que lo revertiría: que J prefiera la tabla apretada en esa
   franja. Se descartó 640 —donde deja de caber del todo— porque a 560 ya no cabe: cortar donde
   el defecto empieza, no donde termina.
   Arriba del corte no se movió un píxel: careado a 1424, 1297, 768 y 640 px contra la hoja de la
   v3, idéntico en los cuatro. Los cuatro anchos de teléfono que mide `movil.test.js` (390 · 375
   · 360 · 320) están muy por debajo de los dos cortes, así que ninguna de sus catorce
   aserciones cambia de lado por esto — es la franja 481-600 la que se mueve.

   ⚠ «Mi cuenta» se pliega TAMBIÉN, y es una decisión: a 390 y 375 cabía sin plegar (320 px de
     337), pero a 360 pedía 329 en una caja de 308 y cortaba folios («KG26A-0048» se leía
     «KG26A-0»). Plegada cabe entera en los CUATRO anchos, sus cuatro importes quedan en cuartos
     alineados, y el precio son 67 px de alto (142 → 209). Se prefiere una sola regla —«en el
     teléfono un renglón son dos líneas»— a dos formas según el ancho y un tercer punto de corte.
     Lo que lo revertiría: que J prefiera ver «Mi cuenta» compacta donde cabe.

   Lo cobra `tests/movil.test.js` a 390 · 375 · 360 · 320, con roturas que muerden con rc=1:
   volver a una línea (folios truncados), quitar el indentado del desglose (se queda sin
   concepto) y fijarle un ancho a la tabla (vuelve el desplazamiento). */
@media (max-width: 600px){
  .pe-tabla{ font-size:var(--fs-xs); display:block }
  .pe-tabla thead, .pe-tabla tbody{ display:block }
  .pe-tabla th{ letter-spacing:0; text-transform:none }
  /* GENERAL y no la lista de las tres (regla O · lo que enumera nace con un ciego: el día que
     alguien agregue un cuarto `pe-c-*` volvería el defecto). Sin esto, un `th` con `width:9%`
     mide 5 px dentro de un tramo de 55 y su rótulo se sale por encima del vecino: «Abono» y
     «Saldo» se leían pegados. Lo destapó una CAPTURA mirada por una persona, no una aserción —
     y la guardia de columnas pasó a mirar también la cabecera. `[class]` empata la
     especificidad de `.pe-tabla th.pe-c-fecha` y gana por orden, que es lo que hace falta. */
  .pe-tabla th[class]{ width:auto }
  /* CASILLA 6 de E2|3 (J, 3-sep) · AIRE entre las dos líneas de la cabecera, y los tres rótulos
     de importe en gris más claro. Los seis rótulos se plegaban pegados —«Cargo» justo bajo
     «Operación» y «Abono» bajo «Concepto»— y las dos líneas se leían como una sola columna
     revuelta. Cada rótulo SÍ estaba sobre su importe (eso ya lo cobra la aserción H de
     `movil.test.js`); lo que faltaba era que se leyeran como lo que son: la segunda línea.
     El gris es `--gray-soft`, que YA existe en `tokens.css` y ya lo usa `.pe-rp-l em`: no se
     estrena ningún color, ni haría falta — `hexEnJS` no admitiría un literal.
     El precio, dicho: son unos píxeles multiplicados por nada, porque la cabecera aparece una
     sola vez en toda la tabla. Es exactamente el argumento en contra, y por eso el aire es un
     paso de la escala (`--e1`) y no un número suelto. */
  .pe-tabla thead tr{ row-gap:var(--e1) }
  .pe-tabla thead th.num{ color:var(--gray-soft) }
  /* TANDA 3 (J, 11-sep) · EL RÓTULO DE LA CABECERA SE PARTE EN VEZ DE ENCIMARSE.
     `.pe-tabla th` lleva `white-space:nowrap`, que en escritorio es correcto. Plegada, la cabecera
     reparte los cuatro importes en CUARTOS (~83 px a 390): «Honorarios» cabía y «Servicios (IVA
     incluido)» NO — medido en Chrome real, se salía 33 px por encima de su celda y se leía
     «GastosServicios (IVA in», dos rótulos pegados y uno cortado. Un rótulo truncado se ve
     completo (familia B), así que el remedio es que se parta, no que se recorte. Lo cobra la
     aserción V12 de la prueba visual de la tanda.
     ⚠ La segunda mitad del selector NO es redundante: `.pe-tabla th.num` —que es quien pone el
     `nowrap` a los cuatro rótulos de importe— pesa (0,2,1), y `.pe-tabla thead th` sólo (0,1,2).
     Sin `.num` escrito aquí, esta regla pierde y el rótulo sigue saliéndose (medido: 33 px). Y se
     acota a `thead`: los IMPORTES de las celdas conservan su `nowrap`, o se partirían a la mitad. */
  .pe-tabla thead th, .pe-tabla thead th.num{ white-space:normal }
  /* LA REJILLA. El relleno del renglón es la mitad del paso más chico, derivado del token y no
     un 2px suelto; el `column-gap` es CERO a propósito —con 24 columnas serían 23 huecos— y la
     separación la ponen las celdas con su relleno derecho. */
  .pe-tabla tr{ display:grid; grid-template-columns:repeat(24, 1fr); align-items:baseline;
    padding:var(--e1) calc(var(--e1) / 2); border-bottom:1px solid var(--line) }
  .pe-tabla th, .pe-tabla td{ display:block; padding:0 calc(var(--e1) / 2) 0 0; border-bottom:0;
    min-width:0 }
  .pe-tabla td.num, .pe-tabla th.num{ text-align:right; padding-right:0 }

  /* «Movimientos» · 6 celdas: 4·8·12 arriba, tercios abajo */
  .pe-movs tr > :nth-child(1){ grid-area:1 / 1 / 2 / 5 }
  .pe-movs tr > :nth-child(2){ grid-area:1 / 5 / 2 / 13 }
  .pe-movs tr > :nth-child(3){ grid-area:1 / 13 / 2 / 25; white-space:normal }
  .pe-movs tr > :nth-child(4){ grid-area:2 / 1 / 3 / 9 }
  .pe-movs tr > :nth-child(5){ grid-area:2 / 9 / 3 / 17 }
  .pe-movs tr > :nth-child(6){ grid-area:2 / 17 / 3 / 25 }
  /* el desglose: sus dos primeras celdas están VACÍAS y se llevarían media línea cada una, que es
     justo el ancho que hace que su concepto se lea (regla: el desglose va CON su concepto). */
  .pe-movs tr.pe-det > :nth-child(1), .pe-movs tr.pe-det > :nth-child(2){ display:none }
  .pe-movs tr.pe-det > .pe-p{ grid-area:1 / 1 / 2 / 25; padding-left:var(--e3) }
  /* la apertura es UNA línea: su etiqueta y su saldo, y el saldo cae en el tercio de los saldos */
  .pe-movs tr.pe-apertura > :nth-child(1){ grid-area:1 / 1 / 2 / 17 }
  .pe-movs tr.pe-apertura > :nth-child(2){ grid-area:1 / 17 / 2 / 25 }
  /* el cierre: la etiqueta entera arriba y los tres totales abajo, en los mismos tercios */
  .pe-movs tr.pe-total > :nth-child(1){ grid-area:1 / 1 / 2 / 25 }
  .pe-movs tr.pe-total > :nth-child(2){ grid-area:2 / 1 / 3 / 9 }
  .pe-movs tr.pe-total > :nth-child(3){ grid-area:2 / 9 / 3 / 17 }
  .pe-movs tr.pe-total > :nth-child(4){ grid-area:2 / 17 / 3 / 25 }
  .pe-movs tr > td[colspan="6"]{ grid-area:1 / 1 / 2 / 25 }

  /* «Mi cuenta» · 5 celdas: la operación arriba entera, los cuatro importes en CUARTOS */
  .pe-cuenta tr > :nth-child(1){ grid-area:1 / 1 / 2 / 25 }
  .pe-cuenta tr > :nth-child(2){ grid-area:2 / 1 / 3 / 7 }
  .pe-cuenta tr > :nth-child(3){ grid-area:2 / 7 / 3 / 13 }
  .pe-cuenta tr > :nth-child(4){ grid-area:2 / 13 / 3 / 19 }
  .pe-cuenta tr > :nth-child(5){ grid-area:2 / 19 / 3 / 25 }
  /* `:not(.pe-cerrada)` porque desde la casilla 8 hay DOS clases de renglón con `pe-plegado`: el
     resumen, que son dos celdas (el control y su $0.00), y las operaciones que aparecen al
     abrirlo, que son cinco y se reparten como cualquier otra de «Mi cuenta». Sin esto, abrir el
     bloque metía las cinco celdas en una rejilla de dos y el renglón se leía revuelto. */
  .pe-cuenta tr.pe-plegado:not(.pe-cerrada) > :nth-child(1){ grid-area:1 / 1 / 2 / 17 }
  .pe-cuenta tr.pe-plegado:not(.pe-cerrada) > :nth-child(2){ grid-area:1 / 17 / 2 / 25 }
  .pe-cuenta tr > td[colspan="5"]{ grid-area:1 / 1 / 2 / 25 }

  /* plegado, el renglón es UNA caja: el fondo y la línea pasan del <td> al <tr>, o cada celda
     pintaría su propio rectángulo y el renglón se leería como dos. */
  .pe-tabla tr.pe-libre, .pe-tabla tr.pe-apertura, .pe-tabla tr.pe-plegado,
  .pe-tabla tr.pe-total, .pe-tabla thead tr{ background:var(--light) }
  .pe-tabla tr.pe-libre td, .pe-tabla tr.pe-apertura td, .pe-tabla tr.pe-plegado td,
  .pe-tabla tr.pe-total td, .pe-tabla tr.pe-det td{ background:none }
  /* y las cerradas abiertas vuelven a fondo de renglón normal, igual que en escritorio */
  .pe-tabla tr.pe-cerrada{ background:var(--surface) }
  .pe-tabla tr.pe-total{ border-top:2px solid var(--navy) }
  .pe-tabla tr.pe-det{ background:var(--interno-bg); color:var(--interno-ink); border-bottom:0 }

  /* La barra de tres pestañas de CLI-5 C1 vivía aquí con su carril horizontal. MURIÓ en la
     14.95: en el teléfono la navegación es la barra de abajo (`.portal-nav` fija, tres entradas
     a un tercio cada una), y el problema que este bloque resolvía —los rótulos cortados a 360—
     ya no existe porque no hay tira que desborde. */

  /* ══ CLI-5 · EL EXPEDIENTE · en el teléfono cada renglón es una tarjeta de dos líneas ════════
     Documento arriba a lo ancho, y debajo el estado a la izquierda con las acciones a la derecha.
     Hereda `display:grid` de `.pe-tabla tr`; aquí sólo se reparten las tres celdas en la rejilla de
     24. La cabecera se oculta —un expediente de tres columnas se lee sin ella— igual que la hoja. */
  .pe-exp thead{ display:none }
  .pe-exp tbody tr > :nth-child(1){ grid-area:1 / 1 / 2 / 25 }
  .pe-exp tbody tr > :nth-child(2){ grid-area:2 / 1 / 3 / 25 }
  /* Las TRES columnas fijas caen en su propia línea, en tercios. En el teléfono no se pueden
     poner al lado del estado: «Confirmo que ya los envié» sola mide más que media pantalla a
     360 px, y lo que J pidió es que las casillas estén SIEMPRE en el mismo sitio — apretarlas
     hasta que se partan es perder justo eso. */
  .pe-exp tbody tr > :nth-child(3){ grid-area:3 / 1 / 4 / 9;   text-align:left; white-space:normal }
  .pe-exp tbody tr > :nth-child(4){ grid-area:3 / 9 / 4 / 17;  text-align:left; white-space:normal }
  .pe-exp tbody tr > :nth-child(5){ grid-area:3 / 17 / 4 / 25; text-align:right; white-space:normal }
  /* el rótulo del historial abierto y el aviso de vacío ocupan el renglón entero */
  .pe-exp tr > td[colspan="5"]{ grid-area:1 / 1 / 2 / 25 }
  /* una versión anterior: nombre a la izquierda, «Ver» a la derecha; sus celdas vacías no ocupan */
  .pe-exp tr.pe-ex-vrow > :nth-child(1){ grid-area:1 / 1 / 2 / 15 }
  .pe-exp tr.pe-ex-vrow > :nth-child(2){ display:none }
  .pe-exp tr.pe-ex-vrow > :nth-child(3){ grid-area:1 / 15 / 2 / 25; text-align:right; white-space:normal }
  .pe-exp tr.pe-ex-vrow > :nth-child(4){ display:none }
  .pe-exp tr.pe-ex-vrow > :nth-child(5){ display:none }
  /* el fondo de la tarjeta lo pinta el <tr>, no cada celda (o se leería como varias cajas) */
  .pe-exp tbody tr{ background:var(--surface) }
  .pe-exp tr.pe-ex-vrow{ background:var(--interno-bg) }
  .pe-exp tr.pe-ex-vrow td{ background:none }
}

/* ══ CLI-5 · LA FICHA DEL EXPEDIENTE, POR REQUISITO (v11, 9-sep) ═══════════════════════════
   Copiadas de la maqueta que J aprobo (FICHA-EXPEDIENTE-v11.html · 92b72b3de79471afb6890e062e52afe9).
   Prefijo `ex-` y NO `pe-ex-`: la del portal es una PASTILLA y esta es PUNTO + letra del color
   (decision de J: «solo aqui; el cliente conserva la pastilla»). Comparten los tokens, no las
   reglas. Viven bajo la clase `.ex-ficha` y NO bajo `#cli_exp`: desde la 15.02 la MISMA
   tabla se pinta en DOS pantallas (la ficha del cliente y «Expedientes»), asi que las reglas son
   del COMPONENTE y no de una pantalla. Un id habria dejado la segunda copia sin estilo. */
.ex-ficha .ex-tabla{ width:100%; border-collapse:collapse; font-size:var(--fs-sm); background:var(--surface) }
.ex-ficha .ex-tabla th{ text-align:left; font-size:var(--fs-rotulo); letter-spacing:.08em; text-transform:uppercase;
  color:var(--gray); background:var(--light); font-weight:700; border-bottom:1px solid var(--line);
  padding:var(--e2) var(--e3); white-space:nowrap }
.ex-ficha .ex-tabla td{ padding:var(--e3); border-bottom:1px solid var(--line); vertical-align:top }
/* J: «alinearia encabezados con contenido» · el rotulo y su contenido comparten sangria, o la
   columna no esta a plomo. */
.ex-ficha .ex-tabla td.ex-acc, .ex-ficha .ex-tabla th.ex-acc{ padding-left:6px; padding-right:6px; text-align:left; white-space:nowrap }
.ex-ficha .ex-c-doc{ width:32% } .ex-ficha .ex-c-est{ width:14% } .ex-ficha .ex-c-ven{ width:11% }
.ex-ficha .ex-c-h{ width:12% }   .ex-ficha .ex-c-s{ width:10% }   .ex-ficha .ex-c-a{ width:16% }
.ex-ficha .ex-c-m{ width:5% }
.ex-ficha .ex-doc{ font-weight:600; font-size:var(--fs-base) }
.ex-ficha .ex-doc.liga{ color:var(--navy); text-decoration:underline; text-underline-offset:3px; cursor:pointer }
.ex-ficha .ex-archivo{ display:block; font-family:var(--font-mono); font-size:var(--fs-xs); color:var(--gray); margin-top:3px }
.ex-ficha .ex-porque{ display:block; font-size:var(--fs-xs); color:var(--gray); margin-top:2px }
/* El estado: punto + letra del color. El punto no lleva texto, asi que el COLOR no es la unica
   senal — la palabra va al lado y dice lo mismo. */
.ex-ficha .ex-pt{ display:inline-block; width:10px; height:10px; border-radius:50%; margin-right:8px; vertical-align:0 }
.ex-ficha .ex-txt{ font-size:var(--fs-sm); font-weight:700 }
.ex-ficha .ex-pt.pend{  background:var(--pend) }   .ex-ficha .ex-txt.pend{  color:var(--pend) }
.ex-ficha .ex-pt.espera{background:var(--espera) } .ex-ficha .ex-txt.espera{color:var(--espera) }
.ex-ficha .ex-pt.llego{ background:var(--llego) }  .ex-ficha .ex-txt.llego{ color:var(--llego) }
.ex-ficha .ex-pt.ok{    background:var(--ok) }     .ex-ficha .ex-txt.ok{    color:var(--ok) }
.ex-ficha .ex-pt.venc{  background:var(--warn) }   .ex-ficha .ex-txt.venc{  color:var(--warn) }
.ex-ficha .ex-pt.rech{  background:var(--danger) } .ex-ficha .ex-txt.rech{  color:var(--danger) }
.ex-ficha .ex-vence{ font-size:var(--fs-sm); color:var(--pend) }
.ex-ficha .ex-vence.pronto{ color:var(--warn); font-weight:700 }
/* Lo secundario es un enlace tranquilo y lo apagado texto gris SIN caja: la uniformidad la da la
   COLUMNA, no dibujar una caja vacia (pasada de diseno de la v3, medida sobre 30 controles). */
.ex-ficha .ex-liga{ display:block; width:100%; text-align:left; font-size:var(--fs-sm); color:var(--navy);
  background:none; border:0; padding:0; text-decoration:underline; text-underline-offset:3px; cursor:pointer }
.ex-ficha .ex-liga.off{ color:var(--gray-soft); text-decoration:none; cursor:default }
.ex-ficha .ex-mas{ display:block; width:100%; text-align:center; border:0; background:none;
  color:var(--gray-soft); font-size:var(--fs-md); line-height:1; cursor:pointer }
.ex-ficha .ex-mas.off{ color:var(--line); cursor:default }
/* El renglon de detras del «⋯»: nace escondido y es donde vive lo que DESTRUYE. */
.ex-ficha .ex-menu td{ background:var(--light) }
/* El selector de archivo no se ve: lo dispara el «Subir» del renglon. Se esconde por CSS y no
   con `style=` en linea, para que no cuente como estilo suelto. */
/* 15.02 · va SIN el prefijo de la tabla: el <input> vive UNA vez en `#view-ficha` (index.html)
   y lo usan las DOS pantallas que pintan el expediente. */
/* 15.03 · HALLAZGO de la prueba visual (Chrome, 1280): la caja «de 1 px» medía 19. El selector
   era `.ex-file` —peso (0,1,0)— y `input[type=file]` (arriba, :217) pesa (0,1,1), así que le
   devolvía sus 8 px de relleno y su borde. Por eso el selector lleva la etiqueta: con el MISMO
   peso y más abajo en el archivo, gana. Es invisible (`opacity:0`) y está fuera del flujo, así
   que no se veía — pero el día que quede dentro de un contenedor con `overflow` aparece una
   barra que nadie explica. Se apaga AQUÍ, en la regla COMPARTIDA, y no en la pantalla nueva: el
   mismo defecto lo tenían la ficha del cliente y «Expedientes», que usan este molde (familia E). */
input.ex-file{ position:absolute; width:1px; height:1px; padding:0; margin:0; border:0;
               opacity:0; pointer-events:none }
