/* ==========================================================================
   Cuentas por Cobrar — hoja de estilos
   Estética: documento contable. Papel, tinta, reglas finas y números que
   alinean en columna. La única pieza llamativa es el total: ahí va el peso.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Variables
   -------------------------------------------------------------------------- */
:root {
  color-scheme: light dark;

  /* ---- Paleta ----
     Neutros de la rampa Slate, marca índigo, y color SOLO para el estado de una
     factura.

     **LA MARCA ES ÍNDIGO Y EL ROJO ES SOLO "VENCIDA".** Antes eran el MISMO
     `#D90429` a propósito y la diferencia la hacía la forma. Con la marca
     índigo esa regla desaparece y queda una más simple: **el rojo no significa
     nada más que "se te pasó la fecha"**.

     **LOS COLORES VIENEN DE UNA REFERENCIA Y ESTÁN OSCURECIDOS DONDE HACÍA
     FALTA.** La referencia usaba `#6366F1` de marca, `#10B981` de verde y
     `#EF4444` de rojo, y no pasaban accessibility:

       #6366F1 sobre el cuerpo  4.12  ✗    →  #4F46E5   5.80  ✓
       #10B981 sobre el cuerpo  2.34  ✗    →  #047857   5.06  ✓
       #EF4444 sobre el cuerpo  3.47  ✗    →  #B91C1C   5.97  ✓
       #94A3B8 sobre el cuerpo  2.37  ✗    →  #5B6676   5.37  ✓
       #64748B sobre el cuerpo  4.39  ✗    →  #475569   6.99  ✓

     El cuerpo gris `#F4F6F8` de la referencia es más oscuro que el nuestro y
     come contraste: por eso el texto apagado necesita `#5B6676` y no `#64748B`.
     La forma se copió entera; los colores se midieron. */

  --papel: #f4f6f8;           /* el gris de la referencia */
  --papel-alto: #ffffff;
  --tinta: #1a1c21;           /* 15.73 sobre el papel */
  --tinta-media: #475569;     /* 6.99  */
  /* El texto apagado. Con el papel gris de la referencia, `#64748B` da 4.39 y
     no pasa: hace falta oscurecerlo un paso. */
  --tinta-tenue: #5b6676;     /* 5.37 sobre papel, 5.82 sobre superficie */
  --acento: #94a3b8;          /* SUPERFICIE ONLY: pista vacía de la barra */
  --regla: #e2e8f0;
  --regla-fuerte: #cbd5e1;

  /* Marca índigo. `#6366F1` daba 4.12; este es el tono siguiente del mismo
     violeta y da 5.80. Sobre él va blanco: 6.29. */
  --marca: #4f46e5;
  --marca-fuerte: #4338ca;    /* hover: con blanco sigue en 7.90 */
  --marca-lavado: #eef2ff;
  --marca-sobre: #ffffff;

  --verde: #047857;           /* al día · 5.06 */
  --verde-lavado: #ecfdf5;

  --ambar: #b45309;           /* por vencer · 4.64 */
  --ambar-lavado: #fffbeb;

  --rojo: #b91c1c;            /* vencida · 5.97 */
  --rojo-lavado: #fef2f2;

  --foco: #4f46e5;

  --fila-hover: #eef0f3;

  /* Sombras: tres niveles, como la referencia. Antes había 2 `box-shadow` en
     todo el archivo y ninguna con nombre. */
  --sombra-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
  --sombra-md: 0 4px 6px -1px rgb(0 0 0 / 0.05), 0 2px 4px -1px rgb(0 0 0 / 0.03);
  --sombra-lg: 0 10px 15px -3px rgb(0 0 0 / 0.05), 0 4px 6px -2px rgb(0 0 0 / 0.025);
  --sombra-flotante: var(--sombra-lg);

  /* Radios: 4 y 8 para lo chico (píldoras de filtros, casillas, iconos) y 12/16
     para las superficies grandes. La referencia usa 12 y 16; los controles
     chicos se quedan en 4 porque a 12 un botón de 36px se ve una cápsula. */
  --r-chico: 4px;
  --r-medio: 8px;
  --r-md: 12px;
  --r-lg: 16px;
  --r-full: 9999px;

  /* Escala de texto */
  --t-xs: 0.75rem;
  --t-sm: 0.875rem;
  --t-base: 1rem;
  --t-md: 1.125rem;
  --t-lg: 1.5rem;
  --t-xl: 2rem;
  --t-cifra: clamp(2.5rem, 7vw, 3.75rem);

  --ancho-lateral: 260px;     /* el de la referencia, no 248px */

  /* Inter para el texto; IBM Plex Mono se queda PARA LAS CIFRAS: cuatro tablas
     alinean columnas con `tabular-nums` y la cifra protagonista es un número
     grande a propósito. */
  --sans: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --mono: 'IBM Plex Mono', ui-monospace, 'SF Mono', Menlo, monospace;
}



@media (prefers-color-scheme: dark) {
    :root {
      /* En oscuro la tinta se invierte y los colores se aclaran: un rojo oscuro
         sobre fondo oscuro no se ve. Todos los pares medidos también en oscuro;
         el más ajustado es `--marca` sobre el papel, 5.06. */

      --papel: #0f1117;
      --papel-alto: #161923;
      --tinta: #f1f3f7;         /* 16.9 sobre el papel */
      --tinta-media: #b7bfcd;   /* 9.6 */
      --tinta-tenue: #8e97a8;   /* 5.6 */
      --acento: #64748b;
      --regla: #232734;
      --regla-fuerte: #333949;

      --marca: #818cf8;         /* 5.06: el índigo claro */
      --marca-fuerte: #a5b4fc;
      --marca-lavado: #232449;
      --marca-sobre: #0f1117;

      --verde: #34d399;         /* 8.8 */
      --verde-lavado: #0d2f26;

      --ambar: #fbbf24;         /* 10.6 */
      --ambar-lavado: #33260a;

      --rojo: #f87171;          /* 6.4 */
      --rojo-lavado: #3d1418;

      --foco: #818cf8;
      --fila-hover: #1c2130;

      /* En oscuro las sombras no se ven: lo que separa es el filete. */
      --sombra-sm: 0 1px 2px 0 rgb(0 0 0 / 0.30);
      --sombra-md: 0 4px 6px -1px rgb(0 0 0 / 0.35), 0 2px 4px -1px rgb(0 0 0 / 0.25);
      --sombra-lg: 0 10px 15px -3px rgb(0 0 0 / 0.40), 0 4px 6px -2px rgb(0 0 0 / 0.30);
    }
  }

/* --------------------------------------------------------------------------
   2. Base

   ESTA SECCIÓN SE PERDIÓ Y SE RECONSTRUYÓ. El reemplazo del bloque `:root` y del
   de modo oscuro buscó el cierre con una cadena que depended de la sangría, no
   coincidió donde debía, y `str.index` devolvió la coincidencia SIGUIENTE: se
   comió desde ahí hasta tres secciones más adelante. Sin aviso: el navegador
  parsea el resto sin quejarse y la página sale sin `body`, sin la barra y sin
   el menú lateral.

   El aviso fue la captura: `node --check`, el cruce de IDs y el conteo de llaves
   daba todo verde con media hoja muerta. **Un CSS balanceado no es un CSS
   correcto**; lo que lo demuestra es mirarlo.
   -------------------------------------------------------------------------- */
/* El reset. `border-box` NO es un detalle de estilo: sin esto, `width: 100%`
   más el padding y el borde hace que CADA elemento se salga de su caja.

   Medido en el formulario, con el reset faltando: los tres campos de una fila
   medían 289px dentro de una caja de 267px — 22px de más, que son exactamente
   10px de padding a cada lado más 1px de borde a cada lado. Con los campos así,
   el formulario se ve desproporcionado y los textos de ayuda se cortan, sin que
   nada en el HTML esté mal.

   Ojo: esto no se nota en las tablas, porque `border-collapse` cambia el cálculo
   de ancho. Solo se nota en los formularios, que es donde se nota. */
*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  font-family: var(--sans);
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  font-family: var(--sans);
  font-size: var(--t-base);
  line-height: 1.5;
  color: var(--tinta);
  background: var(--papel);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1, h2, h3, p, ul, ol, figure { margin: 0; }
ul, ol { padding: 0; }

a { color: var(--marca); }

:focus-visible {
  outline: 2px solid var(--foco);
  outline-offset: 2px;
  border-radius: var(--r-chico);
}
:focus:not(:focus-visible) { outline: none; }

/* El `<h1>` recibe el foco por script al cambiar de vista —para que un lector de
   pantalla anuncie la vista nueva—, y se le pone `tabindex="-1"`. Eso hace que el
   navegador lo considere "foco visible" y le dibuje el anillo, que es lo correcto
   para un control pero no para un encabezado. El anillo no se le saca al foco de
   teclado: `tabindex="-1"` deja el elemento fuera de la tabulación. */
.vista-titulo:focus,
.vista-titulo:focus-visible {
  outline: none;
}

::selection { background: var(--marca-lavado); color: var(--marca); }

/* --------------------------------------------------------------------------
   3. Utilidades
   -------------------------------------------------------------------------- */

/* Solo para lectores de pantalla. `display: none` NO sirve: los lectores de
   pantalla tampoco lo leen, y el contenido desaparecería de verdad. */
.solo-lectores {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* --------------------------------------------------------------------------
   4. Disposición, barra superior y menú lateral
   -------------------------------------------------------------------------- */
.disposicion { display: block; }

/* --- El botón de menú ---

   **Es lo único que quedó de la barra superior**, y no se puede borrar: en el
   móvil es lo único que abre el lateral, que ahí es una pantalla completa. En
   escritorio se oculta, porque el lateral ya está siempre visible.

   Va `position: fixed` y con fondo de superficie, no `transparent`: al estar
   suelto sobre la página se leería como un ícono suelto. Con fondo, borde y
   sombra se lee como un botón, que es lo que es.

   **Cuando el panel está abierto se va a la esquina DERECHA.** El logo del panel
   ocupa la izquierda, así que a la izquierda lo taparía —y el mismo botón tiene
   que servir de "cerrar"—. A la derecha no hay nada en ninguno de los dos
   estados: cerrado, la cabecera de las vistas pone el título a la izquierda.
   Es el mismo elemento del DOM, solo cambia de esquina. */
.boton-menu {
  position: fixed;
  top: max(var(--r-md), env(safe-area-inset-top));
  left: max(var(--r-md), env(safe-area-inset-left));
  z-index: 110;
  display: grid;
  place-items: center;
  /* **44px, no 40.** Es el mínimo recomendado para un objetivo táctil. Con el
     botón escondido mientras se scrollea el área tapada es chica, pero el primer
     toque de la pantalla es el que decide si la app se siente bien. */
  width: 44px;
  height: 44px;
  padding: 0;
  background: var(--papel-alto);
  border: 1px solid var(--regla);
  border-radius: var(--r-md);
  box-shadow: var(--sombra-sm);
  color: var(--tinta);
  cursor: pointer;
  touch-action: manipulation;
}
.boton-menu:hover { background: var(--papel); }

/* **Se oculta al bajar y vuelve al subir.** Medido: con el botón fijo, 12 textos
   distintos del Dashboard pasaban por debajo al scrollear en el móvil —el número
   grande, los títulos de sección, las etiquetas de la leyenda—.

   Sin transición, por lo mismo que el panel lateral: seprobó y ninguna corre.
   `visibility: hidden` es lo que lo saca de verdad, y `pointer-events: none` lo
   pone por las dudas, para que un botón invisible nunca atrape un toque. */
.boton-menu--oculto {
  visibility: hidden;
  pointer-events: none;
}

/* **Atenuado: ya se scrolleó, así que baja contraste.** Menos borde, menos sombra
   y algo de opacidad: se sigue viendo y se sigue sabiendo dónde está, pero deja
   de flotar como un sticker sobre el contenido. */
.boton-menu--atenuado {
  opacity: 0.85;
  border-color: transparent;
  box-shadow: none;
}

/* Con el menú abierto es la X de cerrar: nunca se atenúa ni se esconde. La X es
   el elemento con más contraste del panel justamente porque es la salida. */
.boton-menu[aria-expanded='true'],
.boton-menu[aria-expanded='true'].boton-menu--atenuado {
  visibility: visible;
  opacity: 1;
  border-color: var(--regla-fuerte);
  box-shadow: var(--sombra-sm);
}

.boton-menu[aria-expanded='true'] {
  left: auto;
  right: max(var(--r-md), env(safe-area-inset-right));
  border-color: var(--regla-fuerte);
}
/* Las tres líneas. `::before` y `::after` heredan el ancho, el alto y el color de
   esta regla, así que no hay que repetirselos: por eso el selector va en grupo. */
.hamburguesa,
.hamburguesa::before,
.hamburguesa::after {
  display: block;
  width: 19px; height: 2px;
  background: var(--tinta);
  transition: transform 160ms ease, opacity 160ms ease;
}
.hamburguesa { position: relative; }
.hamburguesa::before, .hamburguesa::after {
  content: '';
  position: absolute;
  left: 0;
}
.hamburguesa::before { top: -6px; }
.hamburguesa::after { top: 6px; }

.boton-menu[aria-expanded='true'] .hamburguesa { background: transparent; }
.boton-menu[aria-expanded='true'] .hamburguesa::before { transform: translateY(6px) rotate(45deg); }
.boton-menu[aria-expanded='true'] .hamburguesa::after { transform: translateY(-6px) rotate(-45deg); }

/* --- Menú lateral ---
   En el móvil es una hoja que tapa el contenido: `fixed` y aparece con
   visibilidad y opacidad para poder animar la salida. */
.lateral {
  position: fixed;
  /* Pantalla completa en el móvil: no hay barra arriba que reservar. Es lo que
     deja libre al botón de cerrar, que va suelto y fijo, para quedar por
     encima (z-index 110 contra 90 de este). */
  inset: 0;
  z-index: 90;
  display: flex;
  flex-direction: column;
  padding: var(--r-lg) max(var(--r-medio), env(safe-area-inset-right)) var(--r-lg) max(var(--r-medio), env(safe-area-inset-left));
  background: var(--papel-alto);
  overflow-y: auto;
  overscroll-behavior: contain;
  /* **El panel se abre y se cierra de golpe: sin `transition`, sin `animation`.**

   Se intentó animar cuatro veces y ninguna corren. Medido, en cada caso: el panel
   quedaba "abierto" en el DOM —`data-abierto="true"`, `aria-expanded="true"`— y
   sin embargo invisible en pantalla.

   1. Transicionar `visibility`: el valor calculado nunca pasaba de `hidden`.
   2. Transicionar `opacity`: se quedaba en `0`.
   3. Transicionar `transform`: se quedaba en `translateX(-100%)`, con
      `matrix(1, 0, 0, 1, -485, 0)` leído 500ms después de poner el atributo.
   4. Una `animation` con `@keyframes`: tampoco corrió.

   En los cuatro, `pointer-events` —la única propiedad del estado abierto que NO
   estaba en la lista de `transition`— sí cambiaba. O sea: **ninguna transición
   arranca en este elemento cuando parte de oculto**, y `visibility` es la única
   que resuelve al instante.

   Así que el panel aparece y desaparece de golpe. Es un drawer de móvil: se abre
   donde uno lo está mirando, sin esperar 180ms a que llegue. Lo que lo hace
   navegable no es la animación sino el `inert` y el foco, y esos sí funcionan.

   El `inert` lo pone el JS, no el CSS: `visibility: hidden` ya lo saca del
   tabú, pero el panel tiene que seguir able de recibir foco al abrir. */
  visibility: hidden;
}

.lateral[data-abierto='true'] { visibility: visible; }

/* En escritorio pasa a ser columna del grid, y deja de flotar. */
@media (min-width: 62rem) {
  .disposicion { display: grid; grid-template-columns: var(--ancho-lateral) minmax(0, 1fr); }
  .boton-menu { display: none; }

  /* En escritorio el panel es una columna, no un drawer: siempre visible, sin
     `inert` — el JS no se lo pone — y sin nada que lo esconda. */
  .lateral {
    position: sticky;
    top: 0;
    inset: auto;
    height: 100vh;
    visibility: visible;
    border-right: 1px solid var(--regla);
  }
}

/* Los rótulos de sección dicen qué agrupa cada cosa. No van en mayúsculas: el
   tracking ya los distingue, y las mayúsculas son el tic más reconocible de una
   maqueta genérica. */
.lateral-titulo {
  margin: 0 0 var(--r-chico);
  padding: 0 var(--r-chico);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--tinta-tenue);
}
.lateral-titulo:first-child { margin-top: 0; }

/* --- La lista de secciones --- */

/* `flex: 0 0 auto` y no `flex: 1`: estirar la navegación empujaba el pie al
   fondo de la barra y dejaba un hueco de cientos de píxeles con cinco ítems
   arriba. El pie va pegado al último ítem, que es donde el ojo lo espera. */
.lateral-nav { flex: 0 0 auto; }

.lista {
  margin: 0 0 var(--r-md);
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.lista--inactiva { opacity: 0.5; }

/* Cada ítem del menú. `display: flex` va ACÁ y no en el bloque de "barra y menú"
   de más abajo: ese bloque solo ajusta medidas y radios, partiendo de que esta
   regla existe. Si el `display` viviera solo allí, un cambio de orden de las
   hojas lo dejaba como `inline-block` y el ícono se caía de línea. */
.item {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  width: 100%;
  padding: 0.5rem var(--r-md);
  border-radius: var(--r-md);
  font-size: var(--t-sm);
  font-weight: 500;
  color: var(--tinta-media);
  text-align: left;
  text-decoration: none;
}
button.item {
  font: inherit;
  background: none;
  border: 0;
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

.lateral-pie {
  margin-top: var(--r-lg);
  padding-top: var(--r-md);
  border-top: 1px solid var(--regla);
  font-size: var(--t-xs);
  color: var(--tinta-tenue);
}

/* (`.item--activo`, `.item-icono` y las medidas del ítem están más abajo, en el
   bloque "la barra y el menú", que las ajusta al diseño con tarjetas) */

/* --------------------------------------------------------------------------
   5. Contenido
   -------------------------------------------------------------------------- */
.contenido {
  min-width: 0;
  /* El `padding-top` de 56px es para el botón de menú flotante: sin él, el botón
     se pondría ENCIMA del `<h1>` de la vista, que está a la izquierda y arriba.
     12px de aire + 40px del botón + 4px de margen. Antes esto lo resolvía la
     barra superior, que ocupaba 72px; al quitarla, el sitio del botón hay que
     reservarlo igual. */
  padding: calc(var(--r-md) + 46px) max(var(--r-medio), env(safe-area-inset-right)) calc(var(--r-medio) * 3) max(var(--r-medio), env(safe-area-inset-left));
}
.contenido:focus { outline: none; }

@media (min-width: 48rem) {
  .contenido { padding-inline: var(--r-lg); }
}

/* A partir de 62rem el botón de menú se oculta (`.boton-menu { display: none }`,
   junto al lateral que pasa a columna) y el sitio que le estaba reservado arriba
   vuelve a ser aire perdido: medido, 56px de hueco entre el borde y el `<h1>` en
   una pantalla de 1440px donde no hay nada que reservar. */
@media (min-width: 62rem) {
  .contenido { padding-top: var(--r-lg); }
}

/* ══════════════════════════════════════════════════════════════════════════
   EL ANCHO DE CADA VISTA, Y POR QUÉ NO ES EL MISMO EN TODAS
   ──────────────────────────────────────────────────────────────────────────
   Antes todas eran 62rem, un número que venía de cuando el lateral se comía
   260px de la pantalla. Con la barra nueva ese tope ya no lo pide nadie: medido
   en 1920px, el Dashboard ocupaba 992px y dejaba **897px de aire muerto** — el
   48% de la pantalla.

   Pero **estirar todo a 1920px sería peor**. Acá hay una tensión real:

   - **Los datos ya se leen bien a 992px**, y estirarlos a 180 caracteres por línea
     los hace PEORES de leer. Una tabla de cobranza necesita poder comparar
     columnas, y comparar a lo largo de 180 caracteres es comparar a ciegas.
   - **Los gráficos sí ganan con ancho**, porque el ancho es información: más
     barra es más barra.

   Así que cada vista toma lo que le sirve:

   | Vista | Ancho | Por qué |
   |---|---|---|
   | Dashboard | 88rem | La cifra grande, los tres gráficos y la tabla de "exigen atención". Todo eso gana con ancho |
   | Listado | 76rem | 10 columnas. Ya necesitaba más que el resto; estirarlo más haría las cifras legibles de lo contrario |
   | Clientes | 76rem | 9 columnas, casi todas cifras: mismo motivo |
   | El resto | 62rem | Texto y formularios. Donde más importa es no alargar la línea |

   El aire que sobra no se reparte al azar: se queda como aire. Una pantalla
   ancha con contenido centrado y aire a los dos lados se lee mejor que una con
   el contenido pegado a la izquierda y 900px de nada a la derecha. */
.vista { max-width: 62rem; }
#dashboard { max-width: 88rem; }
/* El listado tiene 10 columnas: necesita más ancho que el resto.
   Con 62rem (952px útiles) la tabla pedía 1094px y nunca cabía. */
#listado { max-width: 76rem; }
/* Clientes tiene 9 columnas, casi todas cifras: mismo motivo. */
#clientes { max-width: 76rem; }
/* **`margin-inline: auto`: el contenido va al centro, no pegado a la izquierda.**
   Antes no había nada y el aire sobrante se acumulaba TODO a la derecha, que es
   como se lee un hueco: contenido arrimado a un borde y 900px de nada al otro.
   Centrado, el aire se reparte y la pantalla se ve compuesta. */
.vista { margin-inline: auto; }

/* Los anclas no deben quedar bajo la barra fija */
  .vista, #estados-titulo, #urgentes-titulo { scroll-margin-top: var(--r-medio); }

.vista-cabecera { margin-bottom: var(--r-lg); }
/* `text-wrap: balance` reparte el nombre largo en renglones parejos, que es justo
   lo que quiere una razón social de 57 caracteres. El `overflow-wrap` es el
   respaldo para el nombre que NO tiene espacios: sin él, un identificador largo
   empuja el título hacia la derecha y lo saca de la pantalla. */
.vista-titulo {
  font-size: var(--t-xl);
  letter-spacing: -0.025em;
  text-wrap: balance;
  overflow-wrap: break-word;
}

/* **En el Dashboard el título baja a 1.75rem.** Medido: la cabecera de la vista
   medía 69px y con el `h1` de 2rem el título "Cuentas por cobrar" queda más grande
   que cualquier otro dato de la pantalla —incluida la cifra del total—, que es
   justo lo contrario de lo que hace un encabezado de página. */
#dashboard .vista-titulo { font-size: 1.75rem; }
.vista-subtitulo { color: var(--tinta-media); font-size: var(--t-sm); text-wrap: pretty; }

/* El aviso de los datos de fábrica del Dashboard.
   Superficie con `--papel-alto` y filete de `--regla`, NO con `--marca`: el rojo
   es urgencia de cobro, y esto no es una factura vencida. El aviso se lee igual
   porque tiene su propio icono de información, que es la misma forma en que se
   diferencia del resto. */
.aviso-ejemplos {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--r-medio);
  flex-wrap: wrap;
  margin-bottom: var(--r-lg);
  padding: var(--r-medio);
  background: var(--papel-alto);
  border: 1px solid var(--regla);
  border-radius: var(--r-chico);
}
.aviso-ejemplos-cuerpo { display: flex; align-items: flex-start; gap: var(--r-chico); min-width: 0; }
.aviso-ejemplos-marca { flex: none; color: var(--tinta-media); line-height: 0; }
.aviso-ejemplos-marca svg { width: 18px; height: 18px; }
.aviso-ejemplos-titulo { margin: 0; font-size: var(--t-sm); font-weight: 600; }
.aviso-ejemplos-texto { margin: 2px 0 0; font-size: var(--t-xs); color: var(--tinta-media); text-wrap: pretty; }

/* --------------------------------------------------------------------------
   Los tres gráficos del Dashboard

   **Un color por gráfico, y nada más.** El rojo solo aparece en el de antigüedad
   porque todas sus facturas están vencidas —ese es el único significado del rojo
   en esta aplicación—. Los otros dos usan la marca, que es "esto es de la
   aplicación" y nada más. No hay degradados por severidad: en antigüedad, que es
   el gráfico donde más tentador sería, la longitud ya dice cuánto y un quinto
   tono de rojo sería una regla nueva que hay que aprender.

   El resto es estructura, no decoración: la pista gris detrás de cada barra es la
   que hace comparables dos barras de largos distintos sin tener que dibujar la
   cuadrícula y numerarla.

   `.grafico-pista` y `.grafico-col-pista` tienen ALTO FIJO a propósito: sin él el
   `width` / `height` en porcentaje no tiene contra qué medirse y la barra no
   sale. Es el mismo motivo por el que el `<colgroup>` del Listado existe. */
.analisis { margin-top: var(--r-lg); }
.analisis .bloque-cabecera { margin-bottom: var(--r-medio); }

.graficos {
  display: grid;
  gap: var(--r-md);
  grid-template-columns: 1fr;
}

/* **Dos en fila, y el de meses a todo el ancho. SIEMPRE — no hay `@media` que lo
   cambie arriba de 48rem.**

   Hubo una `@media` que ponía tres en fila desde 78rem, y fue una decisión
   medida: bajaba este bloque de 539px a 328px. **Estaba rota**, y no se veía en
   el CSS sino en el ancho de una columna.

   Medido en el navegador, con la cartera de fábrica:

   | pantalla | tarjeta | cada mes | "Bs 817.453,81" necesita |
   |----------|---------|----------|--------------------------|
   | 1440px   | 325px   | **46px** | 107px  → quedaba "Bs 81…" |
   | 1280px   | 325px   | **46px** | 107px  → quedaba "Bs 81…" |
   | 1024px   | 717px   | 112px    | 107px  → entraba entero  |

   El detalle que la hacía pasar desapercibida: **se veía bien a 1024 y mal a
   1440**, al revés de lo que se espera. Porque entre 48 y 78rem el gráfico de
   meses ya ocupaba la fila entera; con tres en fila la tarjeta cae a 325px,
   `grid-auto-columns: 1fr` reparte esos 325 entre seis meses, y quedan 46px para
   un número de 107.

   Es el mismo género de error que el `max-width: 1px` de las tablas: **el ancho
   de una columna lo decide el contenedor, no el hijo.** Y el
   `text-overflow: ellipsis` de `.grafico-col-valor` lo tapaba en vez de
   delatarlo — el recorteconvertería un bug de reparto en un recorte que parecía
   normal.

   **Lo que se cambia y lo que se paga:** el bloque pasa de 328px a 605px de
   alto. Los 328px se compraban con datos que no se leían, que es un precio que
   no hay que pagar. Y las dos tarjetas de arriba quedan en mitades iguales en
   vez de en tercios: sin el hueco vacío que dejaba la grilla de tres.

   La fila de una sola columna en móvil la pone el `@media` de 48rem hacia
   abajo, no uno nuevo. */
@media (min-width: 48rem) {
  .graficos { grid-template-columns: 1fr 1fr; }
}

/* El de vencimiento a su propia fila, a todo el ancho, en todos los tamaños. */
.grafico--ancho { grid-column: 1 / -1; }

/* El token es `--papel-alto`, NO `--superficie`: ese no existe. Con `var()` de
   una variable inexistente la declaración se invalida y el `background` queda en
   `transparent` — medido: `rgba(0, 0, 0, 0)`—, así que la tarjeta se veía solo por
   el filete. El chequeo de "clases CSS sin definir" NO lo detecta: mira
   selectores de clase, no nombres de variables.

   Para eso está `pruebas/tokens.php`-style: comparar los `var(--x)` del CSS, del
   HTML y del JS contra los `--x:` definidos. */
.grafico {
  /* `align-self: start`: sin esto las tarjetas se estiran a la altura de la más
     alta —328px medidos, la del vendedor con seis filas— y el de meses, que
     necesita 132px de contenido, ocupa 328 de caja con aire arriba y abajo. */
  align-self: start;
  /* **`min-width: 0` es lo que hace que la tarjeta NO crezca con el contenido.**

     Sin esto, el `grid-auto-columns: minmax(6.75rem, 1fr)` del eje de meses hizo
     que la tarjeta midiera 694px en una pantalla de 375 —el ancho MÍNIMO de las
     columnas se le sumaba a la tarjeta en vez de repartirse dentro de ella— y la
     página entera scrolleaba 327px de lado. Medido.

     Es el mismo caso de siempre: **en una grilla, el hijo tiene `min-width:
     auto` por defecto, así que nunca baja del tamaño de su contenido.** Hay que
     ponerlo en cero explícitamente para que `overflow-x: auto` del interior
     pueda recortar. */
  min-width: 0;
  padding: var(--r-md);
  background: var(--papel-alto);
  border: 1px solid var(--regla);
  border-radius: var(--r-md);
}

.grafico-titulo {
  font-size: var(--t-base);
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--tinta);
}
.grafico-nota {
  margin-top: 2px;
  font-size: var(--t-xs);
  color: var(--tinta-tenue);
}

/* --- Barras horizontales: antigüedad y vendedor --- */
.grafico-barras {
  display: grid;
  gap: 0.5rem;
  margin-top: var(--r-medio);
}

/* **La fila son DOS renglones: la cabecera y la barra.** Etiqueta y valor
   comparten la línea de arriba, y abajo la barra ocupa el ancho entero.

   Antes eran tres renglones con la etiqueta en una columna fija de 7.5rem, la
   barra en la de en medio y el valor a la derecha. En una tarjeta de 300px eso no
   funciona: la etiqueta se comía la mitad y a la barra le quedaban 90px.

   `gap` vertical de 6px entre renglones y 5px entre filas: medido, 24px por fila
   contra los 45px de antes, y con seis vendedores la tarjeta baja de 328 a 215. */
.grafico-fila {
  display: grid;
  gap: 4px 0;
  margin-bottom: 4px;
}
.grafico-fila:last-child { margin-bottom: 0; }

.grafico-cabecera {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
}

/* El nombre entero va en el `title` —lo pone `pintarBarras()`—, así que aquí el
   recorte con puntos suspensivos no pierde nada. Medido: "Maria Guadalupe
   Gonzalez Ramirez" son 185px y la tarjeta da ~300px, con el valor al lado
   entra entero. */
.grafico-etiqueta {
  min-width: 0;
  font-size: var(--t-xs);
  color: var(--tinta-media);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* La pista: el fondo contra el que se mide la barra. Sin ella, dos barras de
   largos distintos no se comparan. */
.grafico-pista {
  height: 7px;
  background: var(--regla);
  border-radius: 3px;
  overflow: hidden;
}

/* ══════════════════════════════════════════════════════════════════════════
   LAS BARRAS CRECEN, Y LO HACEN CON `transform`
   ──────────────────────────────────────────────────────────────────────────
   **Un solo momento, al pintar. No hay bucle ni efecto al hacer scroll.**

   Y se anima `transform`, no `width` ni `height`: el compositor mueve la barra
   sin recalcular el layout en cada cuadro. Animar el ancho sí se vería, pero
   recalcula el layout de la tarjeta sesenta veces por segundo, que es
   exactamente lo que `transform` evita.

   **`animation-fill-mode: both` no es un detalle.** Sin él, entre que se
   inyecta el HTML y que arranca la animación hay un cuadro en el que la barra
   está a tamaño final y después salta a cero: se ve un parpadeo. Con `both` la
   barra queda en `scaleX(0)` desde el primer cuadro.

   Y el motivo de que sea `scaleX` y no `width: 0`: con `width` el navegador
   tiene que ir recalculando el ancho real de cada barra cuadro a cuadro. Con
   `scaleX` la barra ya tiene su ancho FINAL en el layout y lo que se mueve es la
   escala, que es una operación de compositor.

   ── POR QUÉ `both`, Y POR QUÉ EL `transform: none` DEBAJO ──

   `animation-fill-mode: both` evita el parpadeo: sin él hay un cuadro en el que
   la barra está a tamaño final y después salta a cero.

   El `scaleX(0)` está DENTRO del `@keyframes`, no en la regla base. Esa es la
   diferencia entre una barra que puede quedar invisible y una que no: con el
   `scaleX(0)` en la regla base, apagar la animación dejaría todas las barras en
   cero. Acá la regla base **no declara `transform`**, así que sin animación la
   barra está a su tamaño natural.

   **Medido, no supuesto:** se repitió la prueba apagando la animación a mano
   (`animation: none`) y quitando el `transform: none` del `@media` — las 13
   horizontales y las 6 verticales siguió viéndose enteras. El `transform: none`
   de abajo está igual, pero es una **garantía explícita**, no una necesidad:
   si mañana alguien mueve el `scaleX(0)` a la regla base, esa línea es lo que
   impide que el gráfico aparezca sin barras. */
.grafico-barra {
  display: block;
  height: 100%;
  /* El mínimo existe para que una barra del 0,4% se vea. Una banda EN CERO no
     dibuja nada (lo hace el JS, no el CSS): un muñón de 3px en "Venció hoy"
     parece que hay un poco de dinero donde no hay nada. */
  min-width: 3px;
  background: var(--marca);
  border-radius: 3px;

  /* Crece desde la IZQUIERDA: es de donde se lee una barra horizontal. */
  transform-origin: left center;
  animation: barra-horizontal 520ms cubic-bezier(0.22, 1, 0.36, 1) both;
}

/* El escalonado: las barras aparecen de a una, y la que más importa —la más
   larga, la primera— se ve primero. 60ms entre cada una; con seis filas son
   300ms, que es lo que tarda en terminar la última. */
.grafico-fila:nth-child(2) .grafico-barra { animation-delay: 60ms; }
.grafico-fila:nth-child(3) .grafico-barra { animation-delay: 120ms; }
.grafico-fila:nth-child(4) .grafico-barra { animation-delay: 180ms; }
.grafico-fila:nth-child(5) .grafico-barra { animation-delay: 240ms; }
.grafico-fila:nth-child(6) .grafico-barra { animation-delay: 300ms; }
.grafico-fila:nth-child(7) .grafico-barra { animation-delay: 360ms; }

@keyframes barra-horizontal {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

.grafico-barra--rojo { background: var(--rojo); }

.grafico-valor {
  display: flex;
  flex: none;
  gap: 0.375rem;
  align-items: baseline;
  justify-content: flex-end;
  white-space: nowrap;
}
.grafico-monto {
  font-size: var(--t-xs);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--tinta);
}
.grafico-conteo {
  font-size: var(--t-xs);
  color: var(--tinta-tenue);
  font-variant-numeric: tabular-nums;
}

/* Una banda sin facturas NO se oculta: se muestra en cero. Saber que no hay
   nada en "31 a 60 días" es información; no dibujarla parece que el corte no
   existe. */
.grafico-fila--vacia .grafico-etiqueta,
.grafico-fila--vacia .grafico-monto { color: var(--tinta-tenue); font-weight: 400; }

/* "Otros 4": es una suma, no un vendedor, así que va atenuado y con filete. */
.grafico-fila--resumen {
  margin-top: 2px;
  padding-top: 0.5rem;
  border-top: 1px solid var(--regla);
}
.grafico-fila--resumen .grafico-barra { background: var(--acento); }

/* --- Columnas verticales: vencimiento mes a mes ---
 *
 * **Cada mes tiene un ancho MÍNIMO, y por eso el eje scrollea en vez de
 * recortar.** Medido: "Bs 817.453,81" necesita 107px y con `1fr` a 390px de
 * pantalla cada mes recibía 52px — el mismo bug del escritorio, en el otro
 * extremo.
 *
 * La diferencia con el resto de las tablas es que acá el scroll **no es un
 * mal necessary**: el eje de meses es una tira de tiempo y que siga hacia
 * adelante leyendo es normal. Lo que no es normal es que el número se corte.
 *
 * `contain: paint` es lo que evita que esa tira haga scrollear la PÁGINA entera
 * en horizontal —medido: 174px de desborde sin él—. */
.grafico-columnas {
  display: grid;
  grid-auto-flow: column;
  /* `1fr` con un piso: reparte lo que sobra y nunca reparte menos que el ancho
     que el número necesita. */
  grid-auto-columns: minmax(6.75rem, 1fr);
  gap: var(--r-chico);
  margin-top: var(--r-md);
  overflow-x: auto;
  /* El scroll con la y Pegada no se lee: sin esto el valor queda pegado a un
     borde mientras se mueve. */
  padding-bottom: var(--r-chico);
  contain: paint;
  /* En el móvil es un scroll de verdad, y sin esto el toque lo interpreta como
     un arrastre de página que no deja ver el eje. */
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
}

.grafico-col {
  display: grid;
  /* 5rem, no 7rem: con las dos tarjetas de arriba en fila, la altura de esta la
     marcaba la barra más alta y no hacía falta tanta. */
  grid-template-rows: auto 5rem auto;
  gap: 0.375rem;
  justify-items: center;
  text-align: center;
}

.grafico-col-valor {
  font-size: var(--t-xs);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--tinta);
  /* **SIN `text-overflow: ellipsis`, a propósito.** Estaba, y convertía un bug de
     reparto —una columna de 46px para un número de 107px— en un recorte que
     parecía una decisión de diseño: "Bs 81…". El dato no se recortaba, se
     escondía, y por eso nadie vio que el eje estaba mal.

     El ancho mínimo de `.grafico-columnas` es lo que garantiza que el número
     entre. Si algún día no entra, tiene que **desbordarse y verse**: eso es un
     aviso, y un aviso es mejor que una mentira. */
  white-space: nowrap;
  max-width: 100%;
}

/* Sin fondo gris: la pista llena convertía cada mes en un bloque del mismo
   ancho que el vecino, y cuatro bloques iguales no se leen como un gráfico. Lo
   que hace comparables las columnas es la línea de base, no el relleno.

   La barra es angosta y va al centro de la columna: con el ancho completo, un mes
   con Bs 23.000 y otro con Bs 55.650 se veían como dos rectángulos del mismo
   tamaño y lo único que los distinguía era cuánto se llenaba de arriba. */
.grafico-col-pista {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  width: 100%;
  height: 5rem;
  border-bottom: 1px solid var(--regla);
}

.grafico-col-barra {
  width: min(100%, 3.5rem);
  min-height: 3px;
  background: var(--marca);
  border-radius: 3px 3px 0 0;

  /* Las verticales crecen desde ABAJO —la línea de base—, y con
     `transform-origin: bottom`. Crecer desde abajo es de donde se lee: el mes
     es el eje, y la altura es lo que sube. Con `transform-origin: top` la barra
     bajaría desde arriba y se leería al revés. */
  transform-origin: bottom center;
  animation: barra-vertical 520ms cubic-bezier(0.22, 1, 0.36, 1) both;
}

.grafico-col:nth-child(2) .grafico-col-barra { animation-delay: 60ms; }
.grafico-col:nth-child(3) .grafico-col-barra { animation-delay: 120ms; }
.grafico-col:nth-child(4) .grafico-col-barra { animation-delay: 180ms; }
.grafico-col:nth-child(5) .grafico-col-barra { animation-delay: 240ms; }
.grafico-col:nth-child(6) .grafico-col-barra { animation-delay: 300ms; }
.grafico-col:nth-child(7) .grafico-col-barra { animation-delay: 360ms; }

@keyframes barra-vertical {
  from { transform: scaleY(0); }
  to   { transform: scaleY(1); }
}

.grafico-col-mes {
  font-size: var(--t-xs);
  font-weight: 600;
  color: var(--tinta-media);
}
.grafico-col-conteo {
  font-size: var(--t-xs);
  color: var(--tinta-tenue);
  font-variant-numeric: tabular-nums;
}

/* Con tres en fila, la nota de qué muestra cada gráfico es la que sobra: el
   título ya lo dice ("Antigüedad de la deuda vencida") y el dato está arriba del
   todo. Medido: 16px por tarjeta, 48px en total. */
@media (min-width: 78rem) {
  .grafico-nota { display: none; }
}

/* --- Estado vacío ---

   No es un error y no dice "sin datos": dice QUÉ FALTA, que es lo que la persona
   necesita saber para decidir si el gráfico le sirve. */
.grafico-vacio {
  margin-top: var(--r-md);
  padding: var(--r-md);
  font-size: var(--t-sm);
  color: var(--tinta-media);
  text-align: center;
  background: var(--papel);
  border: 1px dashed var(--regla-fuerte);
  border-radius: var(--r-md);
}

@media (prefers-reduced-motion: reduce) {
  /* `animation: none` apaga el movimiento, que es lo que se pidió.

     El `transform: none` que va junto **no es necesario hoy** —se comprobó
     apagando la animación a mano y las 19 barras siguió viéndose enteras—:
     queda como garantía de que si alguien mueve el `scaleX(0)` del `@keyframes`
     a la regla base, el gráfico no aparezca sin barras. El comentario de arriba
     lo explica con la medida. */
  .grafico-barra,
  .grafico-col-barra {
    animation: none;
    transform: none;
    transition: none;
  }

  /* El botón se sigue escondiendo: es información —"el menú no está acá, bajá"—
     y no decoración. Acá no hay nada que apagar porque no hay transición. */
}

.bloque-titulo { font-size: var(--t-md); }
.urgentes { margin-top: var(--r-lg); }
.protagonista-cuerpo { min-width: 0; }

.bloque-cabecera {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--r-medio);
  flex-wrap: wrap;
  margin-bottom: var(--r-chico);
}
.bloque-nota { font-size: var(--t-xs); color: var(--tinta-tenue); }

/* --------------------------------------------------------------------------
   6. El protagonista: el total
   -------------------------------------------------------------------------- */
.protagonista {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--r-medio);
  flex-wrap: wrap;
  padding: var(--r-lg) 0;
  border-top: 2px solid var(--tinta);
  border-bottom: 1px solid var(--regla);
  margin-bottom: var(--r-lg);
}
.protagonista-etiqueta {
  font-family: var(--sans);
  font-size: var(--t-sm);
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--tinta-media);
}

.cifra {
  margin: var(--r-chico) 0;
  overflow-wrap: anywhere;
  font-family: var(--mono);
  font-size: var(--t-cifra);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.03em;
  line-height: 1;
  color: var(--tinta);
}
.cifra span { color: var(--tinta-tenue); }

.protagonista-nota { font-size: var(--t-sm); color: var(--tinta-tenue); }

/* --------------------------------------------------------------------------
   7. Barra de cartera y leyenda
   -------------------------------------------------------------------------- */
.estados { margin-bottom: var(--r-lg); }

/* ══════════════════════════════════════════════════════════════════════════
   CÓMO SE REPARTE LA CARTERA: UNA FILA POR ESTADO
   ──────────────────────────────────────────────────────────────────────────
   Reemplaza a una barra apilada de 10px sobre 1408px. Medido a 1920px, lo que
   había fallaba en tres cosas concretas:

   1. **La tira era 141:1.** Diez píxeles de alto sobre 1408 de ancho es una
      línea con color, no un gráfico: no hay nada de qué compararla porque no
      tiene altura.
   2. **El nombre y su cifra quedaban a 238px** — tres veces el ancho del número.
      En una grilla de tres columnas con `justify-content: space-between`, el
      ojo tiene que atravesar medio bloque vacío para casarlos. Medido: 238px en
      "Vencidas", 261px en "Al día".
   3. **No había ni un porcentaje.** 58,4% salía de dividir dos cifras a mano.

   El arreglo no es un tipo de gráfico más complejo: es **una fila por estado**,
   con sus cifras agrupadas y **su propia barra justo debajo**. La asociación
   nombre→barra deja de deducirse porque la barra está en su fila, y las tres
   porciones se miden contra la misma escala de 0 a 100%.

   **Reutiliza `.grafico-pista` y `.grafico-barra`** a propósito: son las mismas
   piezas de los tres gráficos de abajo, con la animación y el contraste ya
   medidos. Un cuarto estilo de barra sería un cuarto lugar donde algo se rompe.

   ── POR QUÉ NO HAY EFECTO AL PASAR EL MOUSE ──

   La fila ya tiene su barra pegada debajo y sus cifras al lado: no hay nada que
   aclarar. Un hover que sólo agranda o repinta la fila es decoración, y
   `AGENTS.md` es explícito con eso. Lo que sí es información —y ya está— es la
   animación al pintar: al guardar una factura las tres barras crecen de nuevo y
   se ve en qué proporción cambió la cartera. */

/* El nombre tiene ancho FIJO para que las tres cifras queden alineadas en una
   columna. Sin esto cada fila empuja su número a un lado distinto y deja de
   poderse comparar de un vistazo, que es justo lo que se pidió. */
.reparto-nombre { flex: 0 0 11rem; }

.reparto {
  display: grid;
  gap: var(--r-md);
  margin: 0; padding: 0;
  list-style: none;
}

/* **`display: grid` en la fila no es decorativo: es lo que la sostiene.**
   `.reparto-cabeza` y `.reparto-pista` son dos `<span>`, que son INLINE. En los
   otros gráficos caen en `.grafico-fila`, que es grid, y por eso se vuelven
   bloque; acá, sin esto, la barra se dibuja encima de la fila siguiente y
   tapa el texto de "Por vencer". Es el mismo caso del `display: flex` que le
   faltaba a `.lista` cuando la barra de navegación se convirtió en fila. */
.reparto-fila {
  display: grid;
  gap: 5px;
}

.reparto-cabeza {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
}

.reparto-punto {
  align-self: center;
  flex: 0 0 8px;
  width: 8px; height: 8px;
  border-radius: 50%;
}
.reparto-punto--vencida { background: var(--rojo); }
.reparto-punto--por-vencer { background: var(--ambar); }
.reparto-punto--vigente { background: var(--verde); }

.reparto-nombre {
  font-size: var(--t-sm);
  color: var(--tinta-media);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* **`reparto-cifras` es lo que arregla los 238px.** El monto y la cantidad van
   en el MISMO grupo, con un hueco de 0.5rem entre ellos, y no separados por
   `space-between`. El `margin-left: auto` del porcentaje se come el aire que
   sobra, así que el aire queda donde no estorba: entre el último dato y el
   porcentaje, que es lo único que uno recorre sin leer. */
.reparto-cifras {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  min-width: 0;
}

.reparto-valor {
  font-family: var(--mono);
  font-size: var(--t-base);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.reparto-conteo {
  font-size: var(--t-xs);
  color: var(--tinta-tenue);
  white-space: nowrap;
}

/* El porcentaje en columna a la derecha: las tres se leen de un tirón y se
   comparan entre sí sin tener que restar nada. */
.reparto-pct {
  margin-left: auto;
  font-family: var(--mono);
  font-size: var(--t-base);
  font-variant-numeric: tabular-nums;
  color: var(--tinta-media);
  white-space: nowrap;
}

/* La pista es la de los otros gráficos pero con 10px en vez de 7: esta es la
   barra que resume todo lo de abajo, y a 7px se pierde entre las otras dos
   tarjetas. La diferencia es 3px y es a propósito. */
.reparto-pista { height: 10px; border-radius: var(--r-chico); }

.reparto-barra { transition: width 400ms cubic-bezier(0.22, 1, 0.36, 1); }
.reparto-fila[data-estado="vencida"] .reparto-barra { background: var(--rojo); }
.reparto-fila[data-estado="por-vencer"] .reparto-barra { background: var(--ambar); }
.reparto-fila[data-estado="vigente"] .reparto-barra { background: var(--verde); }

/* El escalonado lo traen los `nth-child` de `.grafico-barra`, que están atados a
   `.grafico-fila`. Acá se replica: tres filas, tres retardos. */
.reparto-fila:nth-child(2) .reparto-barra { animation-delay: 60ms; }
.reparto-fila:nth-child(3) .reparto-barra { animation-delay: 120ms; }

/* En el móvil el nombre no puede quedar en 11rem: se queda el ancho natural y las
   cifras bajan a su propia línea, que es lo que hace `flex-wrap`. */
@media (max-width: 34rem) {
  .reparto-nombre { flex: 0 0 auto; }
  .reparto-cifras { flex-wrap: wrap; }
}

/* --------------------------------------------------------------------------
   8. Tablas
   -------------------------------------------------------------------------- */
/* `contain: paint` NO es adorno: sin él, la tabla se salía del marco y la PÁGINA
   entera se scrolleaba en horizontal.

   Medido: con un solo cliente de razón social larga ("Distribuidora Nacional de
   Materiales de Construccion S.A."), la tabla de clientes llegaba a 1200px dentro
   de un marco de 971px —scrollea bien por dentro— pero `document.scrollWidth` era
   1439 en vez de 1280, y la página se iba 174px al costado. En el Listado, 167px.

   **`overflow-x: auto` recorta lo que se ve, pero su desborde igual cuenta para el
   área desplazable de la raíz.** Probado: `overflow: hidden` NO lo arregla (174px),
   ni `max-width: 100%` en la tabla, ni `overflow-x: clip` en el `<main>`. Lo que
   sí lo arregla es `contain: paint`, que convierte al marco en caja de
   contención: 0px de scroll de página, con la tabla scrolleando por dentro igual
   (verificado: `scrollLeft` llega al máximo).

   Con nombres cortos nunca se vio, porque la tabla no pasaba de su `min-width`.
   Aparece justo cuando alguien registra un nombre largo, que es lo que pasó. */
.marco-tabla {
  overflow-x: auto;
  contain: paint;
  border-top: 1px solid var(--regla);
  border-bottom: 1px solid var(--regla);
  background: var(--papel-alto);
}

.tabla {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--t-sm);
}
.tabla th, .tabla td {
  padding: 0.625rem var(--r-chico);
  text-align: left;
  white-space: nowrap;
}
.tabla th {
  font-size: var(--t-xs);
  font-weight: 600;
  color: var(--tinta-tenue);
  border-bottom: 1px solid var(--regla);
}
.tabla tbody tr { border-bottom: 1px solid var(--regla); }
.tabla tbody tr:last-child { border-bottom: 0; }
/* El hover de fila lo define el bloque "sistema de superficies" del final, en
   `--fila-hover`. Acá estaba el tinte azul del acento: la regla nueva gana por
   estar después, pero dejarla sería tener dos respuestas para lo mismo. */

/* Cifras: siempre monoespaciadas y tabulares para que la columna cierre */
.tabla .num, .tabla .cifra-celda {
  text-align: right;
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
}
.tabla .celda-cliente {
  max-width: 18rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* La tabla del Dashboard sí puede usar max-width en la celda: tiene pocas
   columnas y su tabla no compite por ancho con el listado. */

.fila-vacia td { padding: var(--r-lg); text-align: center; color: var(--tinta-tenue); }
/* Estado vacío con salida: no es un callejón sin salida, es una invitación. */
.vacio-titulo { font-size: var(--t-md); font-weight: 600; color: var(--tinta); margin: 0 0 var(--r-chico); }
.vacio-texto { margin-bottom: var(--r-medio); color: var(--tinta-media); }

/* Distintivo de estado: un punto y una palabra, nada de fondo de color */
.distintivo {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  font-size: var(--t-xs);
  font-weight: 600;
  white-space: nowrap;
}
.distintivo::before {
  content: '';
  width: 7px; height: 7px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}
.distintivo--vencida { color: var(--rojo); }
.distintivo--por-vencer { color: var(--ambar); }
.distintivo--vigente { color: var(--verde); }
/* Cobrada: no es una alerta, es un hecho consumado. */
.distintivo--cobrada { color: var(--verde); }

/* Una fila cobrada se apaga: sigue en el historial, ya no exige acción. */
.fila-cobrada td { opacity: 0.6; }
.celda-tasa { display: block; font-size: var(--t-xs); color: var(--tinta-tenue); }

/* --------------------------------------------------------------------------
   9. Botones
   -------------------------------------------------------------------------- */
.boton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: 40px;
  padding: 0.5rem var(--r-medio);
  font-family: var(--sans);
  font-size: var(--t-sm);
  font-weight: 600;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  border: 1px solid transparent;
  border-radius: var(--r-chico);
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
}
.boton:disabled { opacity: 0.45; cursor: not-allowed; }

/* Blanco sobre la marca azul: 5.17. */
.boton--primario { background: var(--marca); color: var(--marca-sobre); }
.boton--primario:hover:not(:disabled) { background: var(--marca-fuerte); }

.boton--secundario {
  background: transparent;
  border-color: var(--regla-fuerte);
  color: var(--tinta);
}
.boton--secundario:hover:not(:disabled) { background: var(--regla); }

/* Peligro usa el rojo de "vencida", que es lo que está en juego: borrar
   facturas. Su hover es más oscuro, #991B1B. */
.boton--peligro { background: var(--rojo); color: #fff; }
.boton--peligro:hover:not(:disabled) { background: #991b1b; }

.boton[aria-busy='true'] .boton-texto { visibility: hidden; }
.boton-cargando {
  width: 15px; height: 15px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: girar 700ms linear infinite;
}
@keyframes girar { to { transform: rotate(360deg); } }

/* --------------------------------------------------------------------------
   10. Formularios
   -------------------------------------------------------------------------- */
.formulario {
  max-width: 52rem;
  padding: var(--r-lg);
  background: var(--papel-alto);
  border: 1px solid var(--regla);
  border-radius: var(--r-medio);
}

.form-rejilla {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--r-lg);
}
@media (min-width: 44rem) { .form-rejilla { grid-template-columns: 1fr 1fr; } }

/* **Dos columnas, no tres.** Con tres, "Número de factura" y "Cliente" —los dos
   `.campo--ancho`— ocupaban las tres columnas y medían 798px completos, contra
   255px de los demás. Un número de factura de 798px es desproporcionado a
   propósito: el campo grande tiene que ser el que necesita el ancho, y ese es el
   del vencimiento. */
@media (min-width: 72rem) { .form-rejilla { grid-template-columns: 1fr 1fr; } }

.campo { display: flex; flex-direction: column; gap: 0.25rem; min-width: 0; }
/* `.campo--ancho` = ocupa la fila entera. En el móvil no hay grilla y ya ocupa
   todo; desde 44rem son dos columnas, así que `span 2`. */
.campo--ancho { grid-column: 1 / -1; }
@media (min-width: 44rem) { .campo--ancho { grid-column: span 2; } }

/* El vencimiento es el campo más ancho del formulario: fecha + plazo de 5
   opciones. Necesita 392px y la celda de 3 columnas da 281px, así que ocupaba
   2 columnas. Sin esto, el flex comprimía el grupo y se veía amontonado con
   el campo de monto de al lado. */
@media (min-width: 44rem) { .campo--ancho-2 { grid-column: span 2; } }

.campo-etiqueta { font-size: var(--t-sm); font-weight: 600; color: var(--tinta); }
.obligatorio { color: var(--rojo); }

.campo-input {
  width: 100%;
  min-height: 40px;
  padding: 0.5rem 0.625rem;
  font-family: var(--sans);
  font-size: var(--t-base);
  color: var(--tinta);
  background: var(--papel);
  border: 1px solid var(--regla-fuerte);
  border-radius: var(--r-chico);
}
.campo-input:hover { border-color: var(--tinta-tenue); }
.campo-input:focus { border-color: var(--marca); }
.campo-input[aria-invalid='true'] { border-color: var(--rojo); }

.campo-ayuda { font-size: var(--t-xs); color: var(--tinta-tenue); }
.campo-error { font-size: var(--t-xs); font-weight: 500; color: var(--rojo); }

/* flex-wrap: si algún día no entra, la fila se parte en dos en vez de
   comprimir los controles hasta volverlos ilegibles. */
.fila-vencimiento, .fila-monto {
  display: flex;
  flex-wrap: wrap;
  gap: var(--r-chico);
  align-items: center;
}
.fila-vencimiento .campo-input, .fila-monto .campo-input { flex: 1 1 8rem; min-width: 0; }

/* Selector segmentado: se ve presionado, no una lista desplegable */
/* Selector segmentado: se ve presionado, no una lista desplegable */
.grupo-radio, .grupo-moneda {
  display: inline-flex;
  flex: none;            /* nunca se comprime: comprimir lo hacía ilegible */
  padding: 2px;
  background: var(--regla);
  border-radius: var(--r-chico);
}
.grupo-radio-titulo {
  display: none;
  font-size: var(--t-xs);
  font-weight: 600;
  color: var(--tinta-media);
}
.opcion { position: relative; }
.opcion input {
  position: absolute;
  inset: 0;
  opacity: 0;
  cursor: pointer;
}
.opcion span {
  display: grid;
  place-items: center;
  min-width: 2.5rem;
  min-height: 34px;
  padding: 0 0.5rem;
  font-size: var(--t-xs);
  font-weight: 600;
  color: var(--tinta-media);
  border-radius: 2px;
  transition: background-color 120ms ease, color 120ms ease;
}
.opcion:hover span { color: var(--tinta); }
.opcion input:checked + span { background: var(--papel-alto); color: var(--marca); }
.opcion input:focus-visible + span { outline: 2px solid var(--foco); outline-offset: 1px; }

.form-acciones {
  display: flex;
  justify-content: flex-end;
  gap: var(--r-chico);
  margin-top: var(--r-lg);
  padding-top: var(--r-lg);
  border-top: 1px solid var(--regla);
}
.form-estado { display: block; min-height: 1.25rem; margin-top: var(--r-chico); font-size: var(--t-sm); }

/* --------------------------------------------------------------------------
   10b. Listado: filtros, tabla y paginación
   -------------------------------------------------------------------------- */
.filtros {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--r-medio);
  margin-bottom: var(--r-medio);
  align-items: end;          /* alinea las bases de los controles, no las puntas */
}

/* El buscador crece; los 3 selects comparten el resto. Nada de "auto": la
   columna auto medía 685px por la opción larga y dejaba el buscador en 11px. */
@media (min-width: 52rem) {
  .filtros { grid-template-columns: minmax(0, 1.6fr) minmax(0, 3fr); gap: var(--r-medio); }
}

.filtros-busqueda { min-width: 0; }
.campo-buscar { position: relative; }
.lupa {
  position: absolute;
  left: 0.625rem;
  top: 50%;
  transform: translateY(-50%);
  width: 17px; height: 17px;
  color: var(--tinta-tenue);
  pointer-events: none;
}
.campo-buscar .campo-input { padding-left: 2.25rem; }

/* Las etiquetas miden una línea: así los 4 controles quedan alineados. */
.filtros .campo-etiqueta { min-height: 1.35rem; }

.filtros-selects {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--r-chico);
  min-width: 0;
}
/* min-width:0 + width:100% para que un <select> largo no estire la columna */
.filtros-selects .campo-input { min-width: 0; width: 100%; }

.resultado-conteo {
  margin-bottom: var(--r-chico);
  font-size: var(--t-xs);
  color: var(--tinta-tenue);
}

/* Columna de texto larga: se recorta y el texto completo va en el title.

   **OJO: el `max-width` va en un bloque INTERIOR, y el ANCHO DE COLUMNA va en el
   `<colgroup>`.** Antes esta celda traía `max-width: 1px` con la idea de forzar
   un min-content de 1px para que la tabla no reservara espacio, y el span
   interior pusiera el ancho real.

   Eso no funciona, y está medido: la columna de Cliente quedó en **60px** —no 1px,
   ni 264px, 60px, la que el navegador repartió de lo que sobraba— y el nombre se
   veía como "D…" para cualquier cliente. El `max-width` del span no manda sobre
   una columna de tabla; con `white-space: nowrap` el navegador ni lo mira, y con
   `white-space: normal` lo parte por caracteres y salen filas de 219px de alto.

   Lo que sí manda es el ancho de la celda, y por eso ahora está en `.col-*`. */
.recorta {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* **15rem, no 13 ni 22.** Los nombres de empresa en Venezuela son largos: "Di-
   stribuidora Nacional de Materiales de Construccion S.A." son 57 caracteres. A
   13rem (208px) se leía "Distribuidora N…", que no dice nada; a 22rem la tabla
   entera pasaba de 1300px y había que scrollear media pantalla.

   A 15rem (240px) entra la razón social de casi todos los clientes y la tabla
   queda en ~1200px, que scrollea 240px a 1280 de ancho. Es el mismo intercambio
   que ya hace la tabla de clientes (`min-width: 64rem` dentro de un marco con
   scroll), y sale más barato que una columna de 60px.

   El nombre entero va en el `title` (lo pone `celdaRecortada()`), así que tampoco
   se pierde: se lee pasando el mouse, o entero en la ficha, a un click. */
.recorta--cliente { max-width: 13rem; }
/* 8rem. El vendedor es un nombre de persona o un apellido de empresa; a 12rem la
   columna se iba a 208px y no compensaba. */
.recorta--vendedor { max-width: 8rem; }

/* En la tabla del HISTORIAL el recorte toma el ancho de su columna y no el del
   listado. Medido: `.recorta--cliente` con `max-width: 13rem` daba un bloque de
   241px dentro de una celda de 128px, y se salía de la tabla.

   Acá el `max-width: 100%` SÍ funciona, al contrario que en la celda: con
   `table-layout: fixed` la celda ya tiene un ancho decidido, así que el
   porcentaje tiene contra qué medirse. */
.tabla--historial .celda-texto { max-width: none; }
.tabla--historial .recorta { max-width: 100%; }
/* ---------------------------------------------------------------------------
   LA TABLA DEL HISTORIAL: cómo se reparten los anchos
   ---------------------------------------------------------------------------
   El problema de fondo, medido con un mensaje real de la plantilla (159
   caracteres): la tabla llegaba a ~1550px en una pantalla de 900.

   LA CAUSA no es solo el `nowrap`. Es que **en una tabla el ancho de columna lo
   decide el algoritmo de tabla, no el `max-width` de la celda**. Medido con el
   navegador: `.recorta--cliente` declara `max-width: 208px`, su celda
   `max-width: 1px`, y el navegador le dio 50px — el mínimo — con el nombre
   partido en cinco renglones.

   LA SOLUCIÓN es `table-layout: fixed`: ahí los anchos SÍ se respetan, porque
   no se calculan del contenido. Es la herramienta correcta para "quiero que esta
   tabla se reparta así", y deja de depender de `max-width` en celdas, que es lo
   que no funciona.

   Los anchos van en `<colgroup>` del HTML, no en el CSS: con `fixed` cada
   `<col>` toma su parte y lo que sobra se reparte entre los `<th>` sin col.
   --------------------------------------------------------------------------- */
.tabla--historial {
  width: 100%;
}

/* **`table-layout: fixed` es CONDICIONAL, y el corte está en 78rem.**

   Los `<col>` de esta tabla suman 41rem (656px). Con `fixed`, si esa suma supera
   el ancho de la tabla el navegador recorta TODAS las columnas por igual y la que
   no tiene `width` —el mensaje— se queda sin nada. Medido a 700px: mensaje en
   **11px** y la fila en **3439px** de alto, con 137 renglones de tres palabras.

   El `max-width: 40rem` que había antes no cubría el caso: entre 640px y 1090px la
   tabla ya no llega a `min-width` pero tampoco tenía ancho para los `<col>`. Con la
   barra lateral abierta, el marco mide 656px justo alrededor de 700px de pantalla.

   A 78rem (1248px) el marco mide ~976px y al mensaje le quedan ~320px. Por debajo
   de eso la tabla va en `auto`: cada columna toma lo que necesita del contenido y
   la que sobra, que es el mensaje, se lleva el resto. Es el mismo comportamiento
   que ya tienen las otras tablas del proyecto. */
@media (min-width: 78rem) {
  .tabla--historial { table-layout: fixed; }
}

/* Los anchos de cada columna. Con `table-layout: fixed` estos valores SÍ se
   respetan; sin esa regla el ancho lo decide el contenido y no sirve de nada.

   LA SUMA IMPORTA: con `fixed`, si los `width` suman más que la tabla, el
   navegador los recorta TODOS por igual y la columna sin `width` —el mensaje—
   se queda sin espacio. Medido: con 54rem de suma en una tabla de 725px, el
   mensaje quedó en 21px con 137 renglones.

   Acá la suma da 36rem (576px), y en una tabla de 725px al mensaje le quedan
   ~150px: cuatro o cinco renglones, que es lo que un mensaje de cobranza
   necesita para leerse.
   Para ensanchar una columna, achicá otra. Sumar no funciona. */
/* En pantallas chicas NO puede haber `fixed`: seis columnas fijas suman más que
   el ancho disponible y el mensaje se queda en 0px. Medido a 380px: la celda
   del mensaje quedaba en 0px y la fila en 2801px de alto.

   Las tablas de este proyecto hacen scroll horizontal en el móvil —`.marco-tabla`
   tiene `overflow-x: auto` y `marcaScrollTabla()` avisa con una sombra—, así que
   la salida es dejar la tabla scrollear: sin `fixed` cada columna toma lo que
   necesita del contenido, como las demás tablas.

   El `min-width` evita que en el móvil las columnas se aplasten: si no cabe,
   scrollea. */
/* El `min-width` del móvil. Sin él, en una pantalla de 390px las seis columnas se
   aprietan hasta que el mensaje entra en 125px: cinco palabras por renglón.
   Medido. Con 54rem el mensaje queda en ~220px, que es lo mínimo para que un aviso
   de cobranza se lea, y la tabla scrollea —que es lo que ya hacen las demás
   tablas del proyecto en el móvil. */
@media (max-width: 77.99rem) {
  .tabla--historial { min-width: 54rem; }
}

/* **Cuándo: 5.5rem → 6rem.** Con la línea del año fuera, la celda quedó en dos
   renglones —"8 oct 2026" y "09:14 a. m."— y lo que manda es el ancho de la HORA:
   "09:14 a. m." mide 68px y con el padding de la celda necesita 88. A 6rem entran
   los dos renglones sin partirse. */
.col-cuando { width: 6rem; }

/* **Cliente: 7rem → 11rem.** Medido: a 112px, un nombre de 57 caracteres necesita
   391px — desbordaba 279px y se pintaba ENCIMA del teléfono y del mensaje. Eso no
   era un recorte: era texto de una columna encima de las otras.

   Con 176px y dejando que envuelva a dos líneas, la razón social más larga que
   probé entra entera en tres renglones y la fila crece lo justo. Es el mismo
   criterio que la barra apilada: mejor una fila más alta que un dato encima de
   otro. El nombre completo queda en el `title`, que pone `filaEnvio()`. */
.col-cliente { width: 11rem; }

/* **Destino: 6.5rem → 7.5rem.** Medido: "+584120000021" necesita 117px y la
   celda daba 104. Trece píxeles de recorte en el número al que se mandó. */
.col-destino { width: 7.5rem; }

/* **Estado: 6.5rem → 9.5rem.** El motivo del fallo ("No hay nada escuchando en
   http://localhost:8080/") es lo que dice por qué hay que reintentar, y con el
   `nowrap` de `.tabla td` se pintaba encima del botón Reintentar. */
.col-estado { width: 9.5rem; }

/* **Acciones: 5.5rem → 7rem.** El botón mide 87px y con el padding de la celda
   necesita 107. A 88px quedaba medio fuera de la tabla. */
.col-acciones { width: 7rem; }

/* **Las dos celdas que tienen que envolver, y las dos llevan DOS clases.**

   Igual que `.celda-mensaje`: `.tabla td` vale (1 clase, 1 elemento) = 11 y una
   sola clase daría (1,0) = 10, así que el `nowrap` de la tabla ganaría y el
   texto seguiría sin envolver. Con dos clases gana (2,0) = 20. No se simplifique.

   `.celda-cliente-hist` y NO `.celda-cliente`: esa otra es la de la tabla de
   Clientes y lleva `max-width: 22rem`, que con `table-layout: fixed` compite con
   el `<col>` y deja la columna en un ancho que no
   es el del `<col>`. */
.tabla .celda-cliente-hist {
  white-space: normal;
  line-height: 1.4;
  overflow-wrap: break-word;
}

/* El motivo del fallo envuelve, por lo mismo que el nombre del cliente.

   El selector NO lleva `.tabla--historial`: hay dos tablas que pintan filas de
   aviso —la vista general y la de la ficha del cliente— y la segunda es
   `class="tabla"` a secas, sin colgroup ni `table-layout`. Una regla atada a la
   primera dejaba el motivo del fallo sin envolver en la segunda.

   Con `.estado-celda--historial` alcanza para las dos, porque las dos salen de
   `filaEnvio()` y por lo tanto las dos llevan esa clase. El `white-space` va en el
   `<span>`, que es donde está el texto: hereda el `nowrap` de `.tabla td`, y
   cualquier valor declarado le gana a lo heredado. */
.estado-celda--historial .estado-detalle {
  white-space: normal;
  overflow-wrap: break-word;
}
/* `.tabla td`, NO `.celda-mensaje`: la especificidad importa acá.
   `.tabla td` vale (1 clase, 1 elemento) = 11 y `.celda-mensaje` (1,0) = 10,
   así que con una sola clase el `nowrap` de la tabla GANABA y el mensaje
   seguía sin envolver. Con dos clases, (2,0) = 20, gana esta.
   El selector lleva dos clases a propósito: no lo se simplifique. */
.tabla .celda-mensaje {
  white-space: normal;
  line-height: 1.45;
  /* Una palabra más larga que la celda no debe romper la tabla. */
  overflow-wrap: break-word;
  padding-right: var(--r-medio);
}

/* El texto del mensaje, sin recorte: es lo que se le dijo al cliente, y
   cortado desaparece justo la parte que importa.

   Sin `max-width` a propósito: con `table-layout: fixed` el ancho lo fija el
   `<col>` sin clase, y un `max-width` acá competía con él y lo dejaba en 21px. */
.mensaje-texto { display: block; }

/* La celda de "cuándo": cuatro renglones cortos y apilados — año, fecha, hora,
   reenvíos. Con `fixed` la celda no se parte sola, así que el apilado es
   explícito y además controla cuántas líneas ocupa.

   Medido antes: con "8 de octubre de 2026" en un renglón la celda quedaba con
   13 renglones de alto, más alta que el mensaje, que es al revés de lo que
   tiene que ser. Con "8 oct 2026" son cuatro renglones de una línea cada uno. */
/* `.tabla .celda-cuando`, NO `.celda-cuando`: la especificidad.
   `.tabla td` vale (1 clase, 1 elemento) y le gana a (1,0), así que el
   padding de 10px del que estabaYPE overriding el mío de 6px. Medido: la celda
   daba 163px de alto con hijos que sumaban 65px. No se simplifique el selector. */
.tabla .celda-cuando {
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
  line-height: 1.35;
}

/* Los renglones apilados. El `display: block` SÍ hace falta: sin él los spans
   son inline y se apilan en la misma línea por el `white-space: nowrap` de la
   tabla. */
.tabla .celda-cuando .fecha-hora {
  display: block;
  line-height: 1.35;
}
.celda-cuando .fecha-hora--fecha { font-variant-numeric: tabular-nums; }
.celda-cuando .fecha-hora--hora,
.celda-cuando .fecha-hora--reenvio {
  color: var(--tinta-tenue);
  font-size: var(--t-xs);
  font-variant-numeric: tabular-nums;
}

/* El teléfono es un número: cifras tabulares para que la columna no baile. */
.celda-cifra-tabular { font-variant-numeric: tabular-nums; }

/* La columna de acciones se dimensiona por su contenido, sin ensanchar. */
.celda-acciones { width: 1%; white-space: nowrap; }

/* Cuando la tabla no cabe, hay que avisar: la sombra del borde derecho
   desaparece al llegar al final, así que el usuario sabe que puede arrastrar. */
.marco-tabla--desplaza {
  background:
    linear-gradient(to right, var(--papel-alto) 30%, transparent) left / 24px 100% no-repeat,
    linear-gradient(to left,  var(--papel-alto) 30%, transparent) right / 24px 100% no-repeat,
    radial-gradient(farthest-side at 0 50%, rgb(0 0 0 / 0.14), transparent) left / 10px 100% no-repeat,
    radial-gradient(farthest-side at 100% 50%, rgb(0 0 0 / 0.14), transparent) right / 10px 100% no-repeat,
    var(--papel-alto);
}

.tabla--listado td { padding-block: 0.5rem; }

/* Los anchos del `<colgroup>` del Listado. Suman 1150px.

   **La tabla NO usa `table-layout: fixed`**, a diferencia del historial. Acá el
   ancho automático tiene una ventaja: si una celda necesita más de lo que dice su
   columna —una razón social que no entra en 264px, un código larguísimo— la
   columna CRECE y el contenido no se recorta ni se sale. Con `fixed` el navegador
   no cede: o recorta o desborda.

   Lo que no se negocia es el mínimo: sin estos anchos, el navegador le daba a
   Cliente 60px y a Factura 60px, porque la celda traía `max-width: 1px`. */
.col-factura { width: 112px; }
.col-cliente-factura { width: 208px; }
.col-codigo { width: 76px; }
.col-telefono { width: 120px; }
.col-vendedor { width: 112px; }
.col-fecha { width: 78px; }
.col-monto { width: 114px; }
.col-estado-factura { width: 128px; }
.col-acciones-factura { width: 104px; }

/* **Las tres columnas que no se ven por debajo de 88rem (1408px).**

   La tabla tiene 10 columnas y, con la barra lateral abierta, el marco mide 971px
   a 1280 de ancho. Medido: las diez juntas piden 1255px, así que el scroll
   horizontal dejaba fuera justo lo que uno escorea al cobrar — **Monto, Estado y
   Acciones**—, y eso no es un intercambio aceptable.

   Se van Código, Teléfono y Vendedor: son 352px, la tabla baja a ~903px y entra
   entera. Los tres datos están en la ficha del cliente y en el respaldo, así que
   no se pierde información, solo el atajo de verlos de un golpe.

   Por encima de 1408px entran las tres y la tabla scrollea un poco, como la de
   clientes. El orden de las columnas NO cambia: siguen siendo las diez del
   `<colgroup>`, en el mismo lugar. */
@media (max-width: 88rem) {
  .tabla--listado th:nth-child(3),
  .tabla--listado td:nth-child(3),
  .tabla--listado th:nth-child(4),
  .tabla--listado td:nth-child(4),
  .tabla--listado th:nth-child(5),
  .tabla--listado td:nth-child(5) { display: none; }
}

/* Distintivo + días restantes en la misma celda */
/* Estado en una sola línea: antes eran dos (distintivo + días) y la tabla
   quedaba al doble de alto para 15 filas. */
.estado-celda { display: inline-flex; align-items: baseline; gap: 0.375rem; white-space: nowrap; }
.estado-dias { font-size: var(--t-xs); color: var(--tinta-tenue); font-variant-numeric: tabular-nums; }

/* La celda de estado del historial es distinta a la de las facturas: el chip
   sigue en una línea, pero el MOTIVO del fallo puede ocupar media pantalla
   ("No hay nada escuchando en http://localhost:8080.") y con el nowrap de
   arriba empujaba toda la tabla.
   El chip se mantiene entero en su propia línea y el motivo envuelve debajo. */
.tabla .estado-celda--historial {
  display: block;
  white-space: normal;
  max-width: 15rem;
}
.estado-celda--historial .chip-estado {
  white-space: nowrap;
}
.estado-celda--historial .estado-detalle {
  display: block;
  margin-top: 0.25rem;
  line-height: 1.4;
}

.acciones-fila { display: flex; gap: 2px; justify-content: flex-end; }
/* El ícono sigue siendo de 18px —el TAMAÑO VISUAL no cambia—. Lo que crece es
   el blanco alrededor, que es lo que se toca.

   36px y no 44: el objetivo táctil de 44px lo pone el `@media` de 48rem, y en un
   escritorio con ratón 36px alcanza de sobra para acertar. **Subirlos a 44 en
   el escritorio sí haría daño**: la columna de acciones mide 3 botones y con 44
   px Passa a ocupar 132px de los 145px que tenía, y la tabla de diez columnas
   vuelve a pedir ancho.

   La lección es que "área táctil" y "tamaño visual" son dos cosas. Esta regla
   agranda la primera sin tocar la segunda, que es el punto. */
.boton-icono {
  width: 36px; height: 36px;
  display: grid;
  place-items: center;
  color: var(--tinta-tenue);
  background: none;
  border: 0;
  border-radius: var(--r-chico);
  cursor: pointer;
}
.boton-icono:hover { color: var(--tinta); background: var(--regla); }
.boton-icono:focus-visible { outline: 2px solid var(--foco); outline-offset: 2px; }

/* El ícono DENTRO de un botón con texto, como el de "Salir".
 *
 * **Sin `width`/`height` el SVG no tiene tamaño, y medido daba `0x0`.** Es la
 * misma familia que `var(--x)` inexistente: el navegador acepta el error en
 * silencio, parsea todo lo demás sin decir nada, y el botón se dibuja igual —
 * como un contorno de `10x36` con el ícono invisible adentro.
 *
 * Cada otro svg de la aplicación sí lo tenía (`.item-icono svg`,
 * `.tarjeta-icono svg`, `.acceso-logo svg`): este era el único sin regla, y
 * era el único que se veía roto. Por eso el nombre de la clase tiene que
 * existir **aunque se use una sola vez**: una clase que no está definida no
 * avisa, y acá no había nada que la nombrara.
 *
 * `flex: none` porque dentro de `.boton` es un ítem flex: sin esto puede
 * encogerse por debajo del tamaño que se le pide, que es el mismo problema de
 * la barra que ya está anotado unas líneas más abajo. */
.boton-icono-svg { width: 18px; height: 18px; flex: none; }
.boton-icono--peligro:hover { color: var(--rojo); background: var(--rojo-lavado); }
/* Cobrar es la acción principal de una fila: se ve más que editar o borrar. */
.boton-icono--cobrar { color: var(--verde); }
.boton-icono--cobrar:hover { background: var(--verde-lavado); color: var(--verde); }
.boton-icono--deshacer { color: var(--tinta-tenue); }
.boton-icono--deshacer:hover { background: var(--regla); color: var(--tinta); }

/* La cabecera del listado lleva la acción a la derecha en pantalla ancha. */
.vista-cabecera--con-accion {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--r-medio);
  flex-wrap: wrap;
}

/* La barra no aparece ni desaparece: si hay una sola página queda deshabilitada
   pero en su sitio, así filtrar no hace saltar el layout. */
.paginacion {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--r-chico);
  margin-top: var(--r-medio);
  min-height: 40px;
}
.paginacion .boton { min-width: 6rem; }
.paginacion-info {
  font-size: var(--t-xs);
  font-variant-numeric: tabular-nums;
  color: var(--tinta-media);
  min-width: 8.5rem;
  text-align: center;
}

/* El contador no debe reservar alto cuando no hay nada que decir. */
.resultado-conteo:empty { display: none; }

/* --------------------------------------------------------------------------
   6c. Clientes
   -------------------------------------------------------------------------- */
.buscador-clientes { max-width: 28rem; margin-bottom: var(--r-lg); }

/* La tabla de clientes tiene 9 columnas: como el listado, necesita más ancho
   que la vista por defecto, o el desplazamiento horizontal es constante. */
.tabla--clientes { min-width: 64rem; }

/* El nombre es un botón: es la acción de abrir la ficha, y tiene que poder
   enfocarse con el teclado, así que es <button> y no un onclick en la fila. */
.enlace-cliente {
  font: inherit;
  font-weight: 500;
  color: var(--tinta);
  text-align: left;
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
}
.enlace-cliente:hover { color: var(--marca); text-decoration: underline; }
.enlace-cliente:focus-visible { outline: 2px solid var(--marca); outline-offset: 2px; }

/* **El nombre se salía de su celda.** Medido con "Distribuidora Nacional de
   Materiales de Construccion S.A." (57 caracteres): el botón medía 388px dentro
   de una celda de 289px — 99px de más, invadiendo la columna del código.

   La causa es que `.enlace-cliente` no tenía ningún `max-width`: es un botón, y
   un botón inline se dimensiona al contenido, no a la celda. El ancho de columna
   lo decide el algoritmo de tabla, así que el `max-width` tiene que ir en un
   bloque INTERIOR, igual que hace `.celda-texto` + `.recorta` en el Listado.

   Por eso el nombre es `display: block` y el `title` lo pone el JS: se recorta con
   puntos suspensivos y el nombre entero se lee pasando el mouse. */
.celda-cliente { max-width: 22rem; }
.enlace-cliente {
  display: block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* "+1": otros nombres con los que aparece este mismo cliente.
   Va en su PROPIA línea, debajo del nombre. Antes iba en la misma, y con el
   nombre a 22rem el chip se caía fuera de la celda. Solo aparece cuando un mismo
   código de cliente llegó con el nombre escrito de varias formas, así que las
   filas con dos renglones son raras y la tabla sigue siendo escaneable. */
.cliente-alias {
  display: block;
  width: fit-content;
  margin-top: 2px;
  padding: 0 5px;
  background: var(--regla);
  border-radius: 8px;
  font-size: var(--t-xs);
  font-weight: 600;
  color: var(--tinta-media);
}

/* Una columna con "0" o "—" no debe competir con las que tienen plata. */
.cifra-cero { color: var(--tinta-tenue); }

/* El conteo va junto a la cifra, pero en un cuerpo más chico. */
.conteo-estado {
  margin-left: var(--r-chico);
  font-size: var(--t-xs);
  color: var(--tinta-tenue);
}

/* El color de la cifra dice en qué estado está el dinero de ese cliente. */
.cifra--vencida { color: var(--rojo); }
.cifra--por-vencer { color: var(--ambar); }
.cifra--vigente { color: var(--verde); }

.cifra-total { font-weight: 600; }
.cifra-total--cero { color: var(--tinta-tenue); font-weight: 500; }

.estado-vacio {
  padding: 48px var(--r-lg);
  text-align: center;
  border: 1px dashed var(--regla-fuerte);
  border-radius: var(--r-medio);
}

/* --- Ficha del cliente --- */
.boton--volver { margin-bottom: 18px; }

.ficha-cabecera {
  display: flex;
  flex-direction: column;
  gap: var(--r-lg);
  margin-bottom: var(--r-lg);
  padding-bottom: var(--r-lg);
  border-bottom: 1px solid var(--regla);
}
@media (min-width: 60rem) {
  .ficha-cabecera { flex-direction: row; align-items: flex-start; justify-content: space-between; }
}

.ficha-etiqueta { font-size: var(--t-xs); color: var(--tinta-tenue); margin-bottom: 2px; }
/* min-width:0 para que un nombre largo envuelva en vez de empujar las cifras. */
.ficha-cabecera-principal { min-width: 0; flex: 1; }
.ficha-codigo { font-family: var(--mono); font-size: var(--t-sm); color: var(--tinta-media); margin-top: var(--r-chico); }
.ficha-alias { font-size: var(--t-xs); color: var(--tinta-tenue); font-style: italic; margin-top: var(--r-chico); }

/* --- El rastro de cambios, en la ficha del cliente ---

   Es una lista de "esto cambió de A a B", no una tabla: son renglones cortos de
   ancho variable y no hay nada que comparar columna contra columna.

   Un cambio de identidad —el número de factura, el código, el nombre o el monto—
   se marca con el filete de la marca a la izquierda. Es el mismo azul de la casa
   y NO el rojo: el rojo es solo "vencida", y un cambio de identidad no es una
   factura vencida. La forma, no el color, dice que hay que mirarlo. */

.lista-cambios {
  display: grid;
  gap: var(--r-chico);
  margin: var(--r-medio) 0 0;
  padding: 0;
  list-style: none;
}

.cambio {
  padding: var(--r-chico) var(--r-medio);
  background: var(--papel-alto);
  border: 1px solid var(--regla);
  border-left: 3px solid var(--regla-fuerte);
  border-radius: var(--r-chico);
}

/* Solo los que mueven la identidad llevan el filete de color. */
.cambio--identidad {
  border-left-color: var(--marca);
}

.cambio-cabecera {
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--r-medio);
  align-items: baseline;
  justify-content: space-between;
}

.cambio-campo {
  font-size: var(--t-xs);
  font-weight: 600;
  color: var(--tinta-media);
}

.cambio-cuando {
  font-size: var(--t-xs);
  color: var(--tinta-tenue);
  font-variant-numeric: tabular-nums;
}

/* El valor nuevo se destaca y el viejo se apaga, porque la pregunta que se hace
   alguien leyendo esto es "¿qué dice ahora?", no "¿qué decía antes?". El `title`
   de los dos da el detalle exacto al pasar el mouse. */
.cambio-valores {
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
  align-items: baseline;
  min-width: 0;
}

.cambio-factura {
  font-family: var(--mono);
  font-size: var(--t-xs);
  color: var(--tinta-tenue);
}

.cambio-antes {
  overflow: hidden;
  color: var(--tinta-tenue);
  text-decoration: line-through;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 14rem;
}

.cambio-flecha {
  color: var(--acento);
}

.cambio-despues {
  font-weight: 500;
  overflow-wrap: anywhere;
}

.cambio-mas {
  padding: var(--r-chico);
  color: var(--tinta-tenue);
  font-size: var(--t-xs);
  text-align: center;
}

.ficha-cifras { display: flex; flex-wrap: wrap; gap: var(--r-lg); }
.ficha-cifra { display: flex; flex-direction: column; }
.ficha-cifra-valor {
  font-family: var(--mono);
  font-size: var(--t-lg);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.ficha-cifra-valor--vencida { color: var(--rojo); }
.ficha-cifra-valor--por_vencer { color: var(--ambar); }
.ficha-cifra-rotulo { font-size: var(--t-xs); color: var(--tinta-tenue); }

/* Las secciones de la ficha —Facturas, Avisos enviados, Cambios— se separan
   con un margen propio. Sin esto quedan pegadas al texto de ayuda de la anterior,
   y se leen como un solo bloque: el título de "Cambios" salía a 14px del párrafo
   de arriba. */
#ficha-cliente > section + section { margin-top: var(--r-lg); }

.ficha-acciones { display: flex; align-items: center; gap: var(--r-lg); margin-bottom: var(--r-medio); flex-wrap: wrap; }
/* 38rem, no 44rem. A 14px, 44rem son unos 97 caracteres por renglón y a partir de
   los 80 el ojo vuelve al principio de la línea y pierde el hilo — y más en
   pantalla chica, que es donde más se usa. 38rem quedan en ~84. */
.campo-ayuda--ancho { margin: 0 0 var(--r-lg); max-width: 38rem; }
.ficha-telefono { font-family: var(--mono); font-size: var(--t-sm); color: var(--tinta-media); margin: 0; }
.ficha-telefono--vacio { font-family: inherit; color: var(--tinta-tenue); font-style: italic; }

/* --- Mensajes de cobro --- */
.bloque-config--ancho { grid-column: 1 / -1; }
.plantilla-cuerpo { display: grid; gap: var(--r-lg); }
.plantilla-izq { min-width: 0; }
.plantilla-der { min-width: 0; }
@media (min-width: 64rem) { .plantilla-cuerpo { grid-template-columns: 1fr 20rem; } }

.campo-area { min-height: 7rem; resize: vertical; line-height: 1.5; }
.campo-ayuda--error { color: var(--rojo); }

.plantilla-chips-caja { margin-top: var(--r-lg); }
.chips-plantilla {
  display: flex; flex-wrap: wrap; gap: var(--r-chico);
  margin: var(--r-chico) 0 0; padding: 0; list-style: none;
}
.chip-plantilla {
  padding: 2px var(--r-chico);
  background: var(--regla);
  border: 1px solid transparent;
  border-radius: var(--r-chico);
  font-family: var(--mono);
  font-size: var(--t-xs);
  color: var(--tinta-media);
  cursor: pointer;
}
.chip-plantilla:hover { background: var(--marca-lavado); color: var(--marca); border-color: var(--marca); }
.chip-plantilla:focus-visible { outline: 2px solid var(--marca); outline-offset: 2px; }

/* El preview imita un chat para que se lea como un mensaje, no como un
   bloque de texto suelto. */
.preview-rotulo { margin: 0 0 var(--r-chico); font-size: var(--t-xs); color: var(--tinta-tenue); }
.preview-mensaje {
  padding: var(--r-medio);
  background: var(--regla);
  border-radius: 14px;
  border-top-left-radius: 4px;
}
.preview-persona { margin: 0 0 var(--r-chico); font-size: var(--t-xs); font-weight: 600; color: var(--tinta-media); }
.preview-texto { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
.preview-telefono { margin: var(--r-chico) 0 0; font-family: var(--mono); font-size: var(--t-xs); color: var(--tinta-tenue); }
.preview-nota { margin: var(--r-chico) 0 0; font-size: var(--t-xs); color: var(--tinta-tenue); }

.aviso-caja {
  margin-bottom: var(--r-lg);
  padding: var(--r-lg);
  background: var(--papel-alto);
  border: 1px solid var(--regla);
  border-left: 3px solid var(--marca);
  border-radius: var(--r-medio);
}
.aviso-texto-final {
  margin: 0 0 var(--r-lg);
  padding: var(--r-medio);
  background: var(--regla);
  border-radius: 14px;
  border-top-left-radius: 4px;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.aviso-botones { display: flex; flex-wrap: wrap; gap: var(--r-chico); }

/* --- Base de datos --- */
.bd-estado { display: flex; align-items: center; gap: var(--r-chico); margin-bottom: var(--r-lg); flex-wrap: wrap; }
.bd-estado-texto { font-size: var(--t-sm); color: var(--tinta-media); }

.bd-campos {
  display: grid; gap: var(--r-medio);
  grid-template-columns: 1fr 1fr;
}
@media (max-width: 40rem) { .bd-campos { grid-template-columns: 1fr; } }
@media (min-width: 64rem) { .bd-campos { grid-template-columns: 2fr 1fr 1.5fr 1fr 1.5fr; } }

.bd-botones { display: flex; gap: var(--r-chico); margin-top: var(--r-medio); flex-wrap: wrap; }

.bd-campos code {
  padding: 1px 4px;
  background: var(--regla);
  border-radius: 4px;
  font-family: var(--mono);
  font-size: 0.9em;
}

/* Mientras habla con el servidor, los campos se atenúan: un segundo clic
   mientras el primero está en vuelo probaría una cosa y guardaría otra. */
#form-bd[data-esperando='true'] .bd-campos { opacity: 0.6; }

/* --------------------------------------------------------------------------
   9b. Importar CSV
   -------------------------------------------------------------------------- */
.acciones-cabecera { display: flex; gap: var(--r-chico); flex-wrap: wrap; }

.paso-import {
  max-width: 58rem;
  margin-bottom: var(--r-lg);
  padding-bottom: var(--r-lg);
  border-bottom: 1px solid var(--regla);
}
.paso-import:last-of-type { border-bottom: 0; }

/* El botón de bajar el ejemplo, en el paso 1. Va debajo del texto de ayuda y no
   pegado a la zona de arrastrar: es la respuesta a "¿y cómo se hace?", no la
   acción principal, y tiene que pesar menos que subir el archivo. */
.boton-plantilla { margin-top: var(--r-chico); }

.paso-titulo {
  display: flex;
  align-items: center;
  gap: var(--r-chico);
  font-size: var(--t-md);
  margin-bottom: var(--r-medio);
}
.paso-num {
  display: grid;
  place-items: center;
  width: 26px; height: 26px;
  flex: none;
  border-radius: 50%;
  background: var(--marca);
  color: var(--marca-sobre);
  font-size: var(--t-sm);
  font-weight: 700;
}
.paso-nota { margin-bottom: var(--r-medio); color: var(--tinta-media); font-size: var(--t-sm); }

.zona-soltar {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--r-chico);
  width: 100%;
  padding: var(--r-lg);
  background: var(--papel-alto);
  border: 2px dashed var(--regla-fuerte);
  border-radius: var(--r-chico);
  color: var(--tinta);
  font: inherit;
  text-align: center;
  cursor: pointer;
  touch-action: manipulation;
  transition: background-color 120ms ease, border-color 120ms ease;
}
.zona-soltar:hover { border-color: var(--marca); background: var(--marca-lavado); }
.zona-soltar.zona--activa { border-color: var(--marca); background: var(--marca-lavado); }
/* El acento da 2.73 y los íconos con significado necesitan 3.0: va la tinta tenue. */
.zona-icono { width: 40px; height: 40px; color: var(--tinta-tenue); }
.zona-texto { font-size: var(--t-md); font-weight: 600; }
.zona-sub { font-size: var(--t-sm); color: var(--tinta-media); }
.enlace-zona { color: var(--marca); text-decoration: underline; text-underline-offset: 2px; }
.zona-archivo {
  margin-top: var(--r-chico);
  padding: var(--r-chico) var(--r-medio);
  background: var(--verde-lavado);
  color: var(--verde);
  border-radius: var(--r-chico);
  font-size: var(--t-sm);
  font-weight: 600;
}

.rejilla-mapeo {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--r-medio);
}
.campo-mapeo { min-width: 0; }
@media (min-width: 40rem) { .rejilla-mapeo { grid-template-columns: 1fr 1fr; } }
@media (min-width: 64rem) { .rejilla-mapeo { grid-template-columns: 1fr 1fr 1fr; } }

.ayuda-formato {
  margin-top: var(--r-medio);
  padding: var(--r-medio);
  background: var(--papel-alto);
  border: 1px solid var(--regla);
  border-radius: var(--r-chico);
}
.ayuda-formato summary { cursor: pointer; font-weight: 600; font-size: var(--t-sm); }
.ayuda-cuerpo { padding-top: var(--r-medio); display: flex; flex-direction: column; gap: var(--r-chico); align-items: flex-start; }
.ayuda-cuerpo code {
  padding: 1px 4px;
  background: var(--papel);
  border-radius: 3px;
  font-family: var(--mono);
  font-size: 0.9em;
}

.resumen-importacion {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--r-chico);
  margin-bottom: var(--r-medio);
}
.resumen-caja {
  padding: var(--r-medio);
  border: 1px solid var(--regla);
  border-radius: var(--r-chico);
  text-align: center;
}
.resumen-cifra {
  display: block;
  font-family: var(--mono);
  font-size: var(--t-xl);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.resumen-texto { font-size: var(--t-xs); color: var(--tinta-media); }
.resumen-caja--ok .resumen-cifra { color: var(--verde); }
.resumen-caja--duplicado .resumen-cifra { color: var(--tinta-media); }
.resumen-caja--error .resumen-cifra { color: var(--rojo); }

.lista-problemas {
  margin: 0 0 var(--r-medio);
  padding: 0;
  list-style: none;
  max-height: 16rem;
  overflow-y: auto;
  border: 1px solid var(--regla);
  border-radius: var(--r-chico);
}
.problema {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--r-chico);
  padding: var(--r-chico) var(--r-medio);
  border-bottom: 1px solid var(--regla);
  font-size: var(--t-xs);
}
.problema:last-child { border-bottom: 0; }
.problema-marca { color: var(--tinta-tenue); font-variant-numeric: tabular-nums; }
.problema-nombre { font-family: var(--mono); overflow: hidden; text-overflow: ellipsis; }
.problema-motivo { grid-column: 1 / -1; color: var(--tinta-media); }
.problema--error .problema-motivo { color: var(--rojo); }
.problema--duplicado .problema-motivo { color: var(--tinta-tenue); }
.problema--mas { color: var(--tinta-tenue); font-style: italic; }

.import-acciones {
  display: flex;
  justify-content: flex-end;
  gap: var(--r-chico);
}

/* --------------------------------------------------------------------------
   10c. Configuración
   -------------------------------------------------------------------------- */
.config-rejilla {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--r-lg);
  max-width: 56rem;
}

.bloque-config {
  padding-bottom: var(--r-lg);
  border-bottom: 1px solid var(--regla);
}
.bloque-config:last-of-type { border-bottom: 0; padding-bottom: 0; }
.bloque-config .bloque-cabecera { margin-bottom: var(--r-medio); }
.bloque-config--peligro { border-bottom: 0; }

/* --- Integraciones --- */
/* La lista de verificación: un renglón por cosa comprobada. */
/* Los filtros del historial, arriba de la tabla. Alineados con el botón para
   que se lean como una sola fila de control. */
.filtros-historial {
  display: flex;
  align-items: flex-end;
  gap: var(--r-medio);
  flex-wrap: wrap;
  margin-bottom: var(--r-medio);
}

.lista-estado {
  display: flex;
  flex-direction: column;
  gap: var(--r-chico);
  margin: 0 0 var(--r-lg); padding: 0;
  list-style: none;
}

.estado-fila {
  display: flex;
  align-items: baseline;
  gap: var(--r-chico);
  flex-wrap: wrap;
}

.estado-nombre {
  font-size: var(--t-sm);
  font-weight: 600;
}

.estado-detalle {
  font-size: var(--t-xs);
  color: var(--tinta-tenue);
}

/* "verificado hace 3 días". Va al lado del chip, no adentro: el chip dice si
   funciona, el tiempo dice si ese "sí" todavía sirve. */
.estado-cuando {
  font-size: var(--t-xs);
  color: var(--tinta-tenue);
  font-variant-numeric: tabular-nums;
}

/* Un verde viejo se ve más apagado que uno recién comprobado. Mismo color de
   texto, menos peso: la diferencia tiene que notarse sin leer la fecha. */
.estado-fila--viejo .chip-estado--listo {
  background: var(--regla);
  color: var(--tinta-media);
}

.verificar-acciones {
  display: flex;
  align-items: center;
  gap: var(--r-medio);
  flex-wrap: wrap;
}

.lista-integraciones {
  display: flex;
  flex-direction: column;
  gap: var(--r-chico);
  margin: 0; padding: 0;
  list-style: none;
}
.config-vacio { color: var(--tinta-tenue); font-size: var(--t-sm); }

.tarjeta-integracion {
  display: flex;
  align-items: center;
  gap: var(--r-medio);
  padding: var(--r-medio);
  background: var(--papel-alto);
  border: 1px solid var(--regla);
  border-radius: var(--r-chico);
  flex-wrap: wrap;
}
.tarjeta-icono {
  display: grid;
  place-items: center;
  width: 36px; height: 36px;
  flex: none;
  border-radius: var(--r-chico);
  background: var(--papel);
  /* `--tinta-tenue` y NO `--acento`: el acento da 1.9 de contraste y este es el
     ícono de la tarjeta de integración. Como icono ya debería llegar a 3.0, y
     mucho menos a 4.5 si llegara a ser texto. La regla del proyecto —"el acento
     es superficie y nada más"— estaba escrita y este la incumplía. */
  color: var(--tinta-tenue);
}
.tarjeta-icono svg { width: 20px; height: 20px; }
.tarjeta-cuerpo { flex: 1; min-width: 0; }
.tarjeta-fila { display: flex; align-items: center; gap: var(--r-chico); flex-wrap: wrap; }
.tarjeta-nombre { font-size: var(--t-base); }
.tarjeta-resumen { font-size: var(--t-xs); color: var(--tinta-tenue); }

/* Por qué un servicio no se usa. Acompaña al chip "En desuso": el chip dice
   que no va, y esto dice por qué, para que nadie lo vuelva a intentar. */
.tarjeta-desuso {
  font-size: var(--t-xs);
  color: var(--tinta-tenue);
  border-left: 2px solid var(--regla);
  padding-left: var(--r-chico);
  margin-top: var(--r-chico);
}

.chip-estado {
  display: inline-flex;
  align-items: center;
  padding: 1px var(--r-chico);
  border-radius: var(--r-chico);
  font-size: var(--t-xs);
  font-weight: 600;
  white-space: nowrap;
}
.chip-estado--listo { background: var(--verde-lavado); color: var(--verde); }
.chip-estado--incompleta { background: var(--ambar-lavado); color: var(--ambar); }
.chip-estado--inactivo { background: var(--regla); color: var(--tinta-tenue); }
/* "Clave en memoria" es efímero por diseño: se pierde al cerrar el navegador. */
.chip-estado--sesion { background: var(--marca-lavado); color: var(--marca); }

/* --- Formulario de una integración --- */
.form-integracion-caja {
  margin-top: var(--r-medio);
  padding: var(--r-medio);
  background: var(--papel);
  border: 1px solid var(--regla);
  border-radius: var(--r-chico);
}
.form-integracion-cabecera {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--r-chico);
  margin-bottom: var(--r-medio);
}
.boton-cerrar {
  width: 30px; height: 30px;
  display: grid;
  place-items: center;
  background: none;
  border: 0;
  border-radius: var(--r-chico);
  color: var(--tinta-tenue);
  cursor: pointer;
}
.boton-cerrar:hover { background: var(--regla); color: var(--tinta); }

.campos-integracion {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--r-medio);
}
@media (min-width: 40rem) { .campos-integracion { grid-template-columns: 1fr 1fr; } }

.aviso-secreto {
  margin-top: var(--r-medio);
  padding: var(--r-chico) var(--r-medio);
  background: var(--ambar-lavado);
  border-left: 3px solid var(--ambar);
  border-radius: var(--r-chico);
  font-size: var(--t-xs);
  color: var(--tinta-media);
}
.form-integracion-acciones {
  display: flex;
  justify-content: flex-end;
  gap: var(--r-chico);
  margin-top: var(--r-medio);
}
.boton--sm { min-height: 34px; padding: var(--r-chico) var(--r-medio); font-size: var(--t-xs); }

/* --- Áreas táctiles en pantalla chica ---

   **En el móvil los botones crecen.** En el escritorio los tamaños compactos están
   bien: `boton--sm` de 34px y `boton-icono` de 30px conviven con botones de 40px en
   la misma fila y se leen como una jerarquía, no como un error.

   En un teléfono no: 30px es la mitad del ancho de un dedo. Medido con
   `pruebas/movil.js` a 390px, esto es lo que quedaba chico:

   | Control | Antes |
   |---|---|
   | `.boton--sm` — "Quitar los ejemplos", "Importar CSV", "Actualizar" | 34-36px |
   | `.boton` — "Guardar cuenta", "Descartar" | 36px |
   | `.boton-icono` — editar y borrar de la fila | 30px |
   | `.enlace-cliente` — el nombre en la tabla de clientes | **21px** |
   | `.opcion` — los plazos 15/30/45/60/90 | 31px |

   El de 21px es el peor: es el nombre del cliente, o sea la acción principal de esa
   tabla, y era una fila de 39px con un botón de 21px adentro. Ahora el botón llena
   la fila.

   **44px** es el mínimo recomendado, y 40px donde el control ya vive dentro de una
   fila que aporta el alto. */
/* --- Datos --- */
.acciones-datos {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--r-medio);
}
@media (min-width: 40rem) { .acciones-datos { grid-template-columns: 1fr 1fr; } }

.accion-dato {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--r-chico);
  padding: var(--r-medio);
  border: 1px solid var(--regla);
  border-radius: var(--r-chico);
  background: var(--papel-alto);
}
.accion-titulo { font-weight: 600; }
.accion-nota { font-size: var(--t-xs); color: var(--tinta-tenue); flex: 1; }

.config-nota-legal {
  margin-top: var(--r-medio);
  font-size: var(--t-xs);
  color: var(--tinta-tenue);
  border-top: 1px solid var(--regla);
  padding-top: var(--r-medio);
}
/* Ajustes: plegables, para no competir con la tabla de vencimientos.
   Los 44rem acá NO son ancho de línea: es un plegable con una etiqueta y un
   control, no texto corrido, así que la regla de los 80 caracteres no aplica y
   este ancho se queda como estaba. */
.ajustes {
  max-width: 44rem;
  margin-top: var(--r-medio);
  border: 1px solid var(--regla);
  border-radius: var(--r-chico);
  background: var(--papel-alto);
}

.ajustes-resumen {
  padding: 0.625rem var(--r-medio);
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--tinta-media);
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.ajustes-resumen::-webkit-details-marker { display: none; }
.ajustes-resumen::before {
  content: '';
  width: 0; height: 0;
  border-left: 5px solid currentColor;
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  transition: transform 140ms ease;
  flex: none;
}
.ajustes-resumen:hover { color: var(--tinta); }
.ajustes[open] .ajustes-resumen::before { transform: rotate(90deg); }

.ajustes-cuerpo {
  padding: 0 var(--r-medio) var(--r-medio);
  border-top: 1px solid var(--regla);
}

.ajuste { padding-top: var(--r-medio); }
.ajuste + .ajuste { margin-top: var(--r-lg); }
.ajuste-titulo { font-size: var(--t-base); }
.ajuste-nota { margin-bottom: var(--r-chico); font-size: var(--t-xs); color: var(--tinta-tenue); }

.fila-feriado {
  display: flex;
  align-items: flex-end;
  gap: var(--r-chico);
}
.campo--crece { flex: 1; }
.campo--angosto { max-width: 12rem; }

.lista-feriados {
  display: flex;
  flex-direction: column;
  margin-top: var(--r-medio);
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--regla);
}
.lista-feriados:empty { display: none; }

.feriado {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--r-medio);
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--regla);
}
.feriado-fecha {
  font-family: var(--mono);
  font-size: var(--t-sm);
  font-variant-numeric: tabular-nums;
}
.feriado-quitar {
  flex: none;
  width: 28px; height: 28px;
  display: grid;
  place-items: center;
  color: var(--tinta-tenue);
  background: none;
  border: 0;
  border-radius: var(--r-chico);
  cursor: pointer;
}
.feriado-quitar:hover { color: var(--rojo); background: var(--rojo-lavado); }

/* --------------------------------------------------------------------------
   11. Diálogo de confirmación
   -------------------------------------------------------------------------- */
.dialogo {
  width: min(28rem, calc(100% - 2rem));
  padding: 0;
  color: var(--tinta);
  background: transparent;
  border: 0;
  border-radius: var(--r-medio);
}
.dialogo::backdrop { background: rgb(0 0 0 / 0.4); }

.dialogo-cuerpo {
  padding: var(--r-lg);
  background: var(--papel-alto);
  border-radius: var(--r-medio);
  overscroll-behavior: contain;
  max-height: 85dvh;
  overflow-y: auto;
}
.dialogo-titulo { font-size: var(--t-md); margin-bottom: var(--r-chico); }
.dialogo-mensaje { color: var(--tinta-media); font-size: var(--t-sm); }
.dialogo-pie {
  display: flex;
  justify-content: flex-end;
  gap: var(--r-chico);
  margin-top: var(--r-lg);
}

/* --------------------------------------------------------------------------
   12. Avisos
   -------------------------------------------------------------------------- */
.avisos {
  position: fixed;
  left: max(var(--r-medio), env(safe-area-inset-left));
  right: max(var(--r-medio), env(safe-area-inset-right));
  bottom: max(var(--r-medio), env(safe-area-inset-bottom));
  z-index: 150;
  display: flex;
  flex-direction: column;
  gap: var(--r-chico);
  pointer-events: none;
}

.aviso {
  display: flex;
  align-items: flex-start;
  gap: 0.625rem;
  padding: 0.75rem var(--r-medio);
  background: var(--papel-alto);
  border: 1px solid var(--regla-fuerte);
  border-left: 3px solid var(--tinta-media);
  border-radius: var(--r-chico);
  box-shadow: 0 8px 24px rgb(0 0 0 / 0.12);
  font-size: var(--t-sm);
  pointer-events: auto;
  animation: aviso-entra 180ms ease-out;
}
.aviso--saliendo { animation: aviso-sale 180ms ease-in forwards; }
.aviso--exito { border-left-color: var(--marca); }
.aviso--error { border-left-color: var(--rojo); }
.aviso--info { border-left-color: var(--marca); }

.aviso-marca { flex: none; width: 18px; height: 18px; color: var(--tinta-tenue); }
.aviso--exito .aviso-marca { color: var(--marca); }
.aviso--error .aviso-marca { color: var(--rojo); }

.aviso-cuerpo { flex: 1; min-width: 0; }
.aviso-titulo { font-weight: 600; }
.aviso-texto { color: var(--tinta-media); font-size: var(--t-xs); overflow-wrap: anywhere; }

.aviso-accion {
  flex: none;
  padding: 0.25rem 0.625rem;
  font-family: var(--sans);
  font-size: var(--t-xs);
  font-weight: 600;
  color: #fff;
  background: var(--tinta);      /* el gris anterior daba 3.5 con el texto blanco */
  border: 0;
  border-radius: var(--r-chico);
  cursor: pointer;
}
.aviso-accion:hover { background: var(--tinta); }

.aviso-cerrar {
  flex: none;
  display: grid;
  place-items: center;
  width: 24px; height: 24px;
  padding: 0;
  color: var(--tinta-tenue);
  background: none;
  border: 0;
  border-radius: var(--r-chico);
  cursor: pointer;
}
.aviso-cerrar:hover { color: var(--tinta); background: var(--regla); }

@keyframes aviso-entra {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}
@keyframes aviso-sale {
  to { opacity: 0; transform: translateY(8px); }
}

/* --------------------------------------------------------------------------
   13. Impresión: el estado de cuenta en papel
   -------------------------------------------------------------------------- */
@media print {
  .boton-menu, .lateral, .avisos, .dialogo, .boton { display: none !important; }
  body { background: #fff; }
  .disposicion { display: block; }
  .contenido { padding: 0; }
  .marco-tabla { border-color: #999; }
  .tabla thead { display: table-header-group; }
  .tabla tr { break-inside: avoid; }
}

/* --------------------------------------------------------------------------
   14. Movimiento reducido
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ==========================================================================
   SISTEMA DE SUPERFICIES

   Las reglas de "estilo Figma" que Spend un rato en un archivo aparte con un
   `<link>` después de este. Ahora viven acá, y son de toda la app.

   Lo que se conserva del intento de aislarlas: cada regla dice SI toca una sola
   vista o todas, porque el alcance es la parte que más se rompe en silencio.
   ========================================================================== */

/* ---------------------------------------------------------------------------
   Filete de 1px, nunca una regla gruesa.

   `border-top: 2px` es un recurso editorial —la revista, la crónica— que le da
   peso al bloque. Acá el peso se gana con espacio, no con tinta.
   --------------------------------------------------------------------------- */
.protagonista {
  border-top: 1px solid var(--regla);
}

/* ---------------------------------------------------------------------------
   El hover de la fila, en gris.

   Antes era `color-mix(in srgb, var(--acento) 18%, transparent)`: un tinte azul.
   Se lee como "esta fila es seleccionable" y compite con los tres estados de la
   barra de cartera. Un gris plano dice lo mismo sin gritar.
   --------------------------------------------------------------------------- */
.tabla tbody tr:hover {
  background: var(--fila-hover);
}

/* ---------------------------------------------------------------------------
   Densidad.

   Botón de 36px en vez de 40, y celda con 4px/8px en vez de 10px/4px. Entra más
   información por pantalla sin que se lea rápido en vez de bien.

   El alto de 36px es el mínimo que deja cómodo el área táctil: bajarlo sería
   accesibilidad, no estética.

   **En pantalla táctil sube a 44px**, con la regla de más abajo que va después de
   esta a propósito: dos reglas con la misma especificidad gana la última, y con
   esta escrita después del `@media` el móvil se quedaba en 36. Medido: los botones
   de las cabeceras de vista, "Quitar los ejemplos" y "Guardar cuenta", seguían
   midiendo 36px a 390px de pantalla.
   --------------------------------------------------------------------------- */
.boton {
  min-height: 36px;
}

@media (max-width: 48rem) {
  .boton,
  .boton--sm { min-height: 44px; }

  .boton-icono { width: 40px; height: 40px; }

  .enlace-cliente {
    display: inline-flex;
    align-items: center;
    min-height: 40px;
  }

  .opcion span,
  .opcion input { min-height: 44px; }

  /* Los chips de variable de la plantilla miden 21px: son botones que insertan
     "{cliente}" y friends en el mensaje, y a 21px no se aciertan con el dedo. */
  .chip-plantilla { min-height: 40px; }
}

.tabla th,
.tabla td {
  padding: var(--r-chico) var(--r-medio);
}

/* ---------------------------------------------------------------------------
   Los dos radios que se habían escapado del sistema.

   En 1790 líneas había exactamente dos superficies de 14px, contra unas 30 de
   4 y 8. Se ven mal: el ojo las lee como "algo está mal" porque son las únicas.
   No cambian ningún ancho, solo la forma de la esquina.
   --------------------------------------------------------------------------- */
.preview-mensaje,
.aviso-texto-final {
  border-radius: var(--r-medio);
}

/* ---------------------------------------------------------------------------
   Sombra con nombre, para lo único que flota.

   El diálogo de confirmar y los avisos abajo de la pantalla. Viven a nivel de
   `<body>`, fuera de cualquier `.vista`, así que esto no se puede acotar por
   pantalla: es de toda la app. Solo AGREGA una sombra donde no había ninguna,
   no mueve un píxel de layout.

   Baja y difusa a propósito: una sombra que se nota es una sombra de más.
   --------------------------------------------------------------------------- */
.dialogo,
.aviso {
  box-shadow: var(--sombra-flotante);
}

/* ==========================================================================
   LA BARRA Y EL MENÚ

   Lo que hace que esto se lea como una barra y no como un bloque más:

   - **Filete de 1px para separar, sombra para flotar.** La barra es fija y
     está sobre el contenido, así que lleva un filete abajo; el menú del móvil
     es una hoja que tapa, así que lleva sombra. Uno para separar, el otro para
     indicar que está encima. Nunca los dos.

   - **El ítem activo se marca con fondo lavado + filete interior de la marca.**
     Es el mismo criterio del botón primario —la forma distingue, no el color a
     secas— y con la marca azul el activo ya no se confunde con "vencida", que
     es rojo.

   - **Los ítems miden 36px de alto** como los botones: mismo ritmo vertical en
     toda la aplicación, que es de donde sale la sensación de "todo encaja".
   ========================================================================== */

/* El menú del móvil es una hoja: tapa contenido, así que flota. */
.lateral {
  border-top: 0;
  box-shadow: var(--sombra-flotante);
}

/* El ítem del menú: mismo alto y mismo radio que un botón. */
.item {
  min-height: 40px;
  padding: 0 var(--r-md);
  border-radius: var(--r-md);
  gap: var(--r-chico);
}

/* Cada ícono en su cuadro de 32px. El `currentColor` del SVG hace que el ícono
   tome el color del estado: apagado normally, de marca cuando el ítem está
   activo o con el cursor encima. */
.item-icono {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  flex: none;
  border-radius: var(--r-md);
  color: var(--tinta-media);
}
.item-icono svg { width: 18px; height: 18px; }
.item--activo .item-icono,
.item:hover:not(.item--activo) .item-icono {
  color: var(--marca);
  background: var(--marca-lavado);
}

/* El activo: fondo de la marca, entero, SIN filete interior.

   Antes llevaba `inset 2px 0 0` —la barra de color al izquierda—, que venía del
   diseño de la marca roja. Con la marca índigo y las superficies con sombra esa
   barra compite: pesa lo mismo que el texto y el activo se ve cargado. La
   referencia marca el activo solo con el fondo, y se lee mejor. */
.item--activo {
  color: var(--marca);
  font-weight: 600;
  background: var(--marca-lavado);
}

/* El hover comparte el fondo con el activo, como en la referencia: el menú se lee
   como una sola superficie y no como dos estados distintos. */
.item:hover:not(.item--activo):not(:disabled) {
  background: var(--marca-lavado);
  color: var(--marca);
}

/* Los rótulos de sección, un pelo más chico y con más aire arriba. */
.lateral-titulo {
  margin-top: var(--r-lg);
  letter-spacing: 0.06em;
}

.lateral-titulo:first-child {
  margin-top: 0;
}

/* ==========================================================================
   SUPERFICIES: TARJETAS CON SOMBRA

   La diferencia entre esto y lo que había es la que hace que una pantalla se
   lea como "diseño" y no como "tablas en una página": antes las superficies se
   separaban solo con filete de 1px, y ahora llevan SOMBRA además del filete.

   El filete solo no alcanza en pantalla clara: un #E2E8F0 sobre #F4F6F8 da
   1.14 de contraste, y a esa diferencia el ojo la adivina más que la ve. Con una
   sombra `--sombra-md` por debajo, la tarjeta se despega.

   Radios: 16px en las superficies grandes, 12px en las medianas, 8px y 4px en
   los controles chicos. A 16px un botón de 36px se vería como una cápsula.
   ========================================================================== */

/* El formulario es la superficie más grande de la app: radio y sombra de tarjeta. */
.formulario {
  border-radius: var(--r-lg);
  box-shadow: var(--sombra-md);
}

/* Los bloques de Configuración, que antes solo tenían filete. */
.bloque-config {
  padding: var(--r-lg);
  background: var(--papel-alto);
  border: 1px solid var(--regla);
  border-radius: var(--r-md);
  box-shadow: var(--sombra-sm);
}
.bloque-config:last-of-type { padding-bottom: var(--r-lg); }

/* El botón de volver a la plantilla de ejemplo, debajo del textarea y de su
   ayuda —no al lado, que ahí no entra en el móvil—. */
.plantilla-acciones {
  display: flex;
  flex-wrap: wrap;
  gap: var(--r-chico);
  margin: var(--r-chico) 0 0;
}

/* La tabla: el marco es la superficie. */
.marco-tabla {
  border-radius: var(--r-md);
  box-shadow: var(--sombra-sm);
  border: 1px solid var(--regla);
  border-top: 0;
  border-bottom: 0;
}

/* Las tres cajas de la revisión del importador. */
.resumen-caja {
  border-radius: var(--r-md);
  box-shadow: var(--sombra-sm);
}

/* Los paneles plegables de Configuración. */
.ajustes {
  border-radius: var(--r-md);
  box-shadow: var(--sombra-sm);
  background: var(--papel-alto);
}

/* El bloque protagonista del Dashboard: la cifra grande ya tiene filete arriba; con sombra se
   despega del bloque de la barra de cartera que va justo debajo. */
/* El padding vertical baja a `--r-md`. Medido: el bloque del total medía 144px y
   con `--r-lg` eran 32px de aire arriba y abajo que no dice nada —el filete y la
   sombra ya lo separan—. Con `--r-md` quedan 128. */
.protagonista {
  padding: var(--r-md);
  background: var(--papel-alto);
  border: 1px solid var(--regla);
  border-radius: var(--r-lg);
  box-shadow: var(--sombra-md);
}

/* El aviso de los datos de fábrica. */
.aviso-ejemplos {
  border-radius: var(--r-md);
  box-shadow: var(--sombra-sm);
}

/* La caja del mensaje armado, en la ficha del cliente. */
.aviso-texto-final,
.preview-mensaje {
  border-radius: var(--r-md);
}

/* Los estados vacíos: son tarjetas también. */
.vacio {
  border-radius: var(--r-lg);
}

/* ==========================================================================
   LA MARCA

   **ESTE BLOQUE NO EXISTÍA.** El logo era un `<svg viewBox="0 0 32 32">` sin
   `width` ni `height`, sin regla de CSS, acomodado por el flex del contenedor.
   Se veía bien por casualidad: el SVG es `inline` y su ancho por defecto es
   100%, pero dentro de un hijo directo de flex el ancho se resolvía al
   contenido.

   Al meter un `<span class="marca-icono">` intermedio —para ponerle el cuadro de
   32px como en la referencia— se rompió esa casualidad: el `<span>` es `inline`
   con `width: auto`, así que el 100% del SVG se resolvió contra la barra entera.
   Medido: **1249×1249**. La captura mostraba un cuadrado índigo ocupando toda la
   pantalla.

   **Un SVG sin dimensiones es un SVG sin tamaño.** Por eso el `width`/`height`
   está en la regla y NO como atributo: en el atributo no se puede heredar ni
   sobrescribir, y fue exactamente el atributo que faltó.
   ========================================================================== */
.marca {
  display: flex;
  align-items: center;
  gap: var(--r-chico);
  flex: none;
  color: var(--marca);
  text-decoration: none;
}

.marca-icono {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  flex: none;
  border-radius: var(--r-md);
  background: var(--marca-lavado);
}

.marca-logo {
  display: block;
  width: 32px;
  height: 32px;
}

.marca-nombre {
  font-size: var(--t-md);
  font-weight: 700;
  color: var(--tinta);
  letter-spacing: -0.01em;
}

/* La de la barra superior es solo el cuadro: con el sidebar al lado, el nombre se
   repetía dos veces en la misma pantalla. El `aria-label` lleva el nombre para
   que el lector de pantalla lo anuncie igual. */

/* El logo del sidebar, arriba del menú, con aire abajo. */
.lateral > .marca {
  margin-bottom: var(--r-lg);
  padding: 0 var(--r-chico);
}

/* --- Qué pasa sin JavaScript ---

   `#app` arranca con `hidden` en el HTML, así que sin JavaScript no se ve nada.
   Ese `<noscript>` es la contra cara: sin él la página sería un rectángulo
   blanco, que es la forma más difícil de depurar de todas.

   Va centrado y con el mismo aire que el resto, para que se lea como parte de
   la aplicación y no como un error del navegador. */
.sin-js {
  max-width: 34rem;
  margin: 18vh auto 0;
  padding: var(--r-lg);
  border: 1px solid var(--regla);
  border-radius: var(--r-md);
  /* `--papel-alto`, no `--superficie`: el token que se llama así no existe. Con
     una variable CSS no definida la declaración se invalida en tiempo de
     cálculo y el fondo cae en su valor inicial —medido antes: rgba(0,0,0,0)—.
     Lo dice su propio hermano `.grafico`, que pasó meses con la tarjeta sin
     fondo por lo mismo. */
  background: var(--papel-alto);
  text-align: center;
  color: var(--tinta);
}

.sin-js p {
  margin: 0 0 var(--r-medio);
  color: var(--tinta-tenue);
}

.sin-js strong {
  color: var(--tinta);
}


/* ══════════════════════════════════════════════════════════════════════════
   BARRA DE NAVEGACIÓN EN EL ESCRITORIO
   ──────────────────────────────────────────────────────────────────────────
   Va AL FINAL del archivo a propósito, y no por casualidad: dos reglas con la
   misma especificidad gana la última, y este bloque necesita ganarle a las del
   drawer que están más arriba.

   El corte está en 62rem, el mismo que ya separaba "columna" de "panel". Por
   debajo **no cambia nada**: sigue siendo el drawer a pantalla completa con su
   botón de menú, que es como ya funcionaba y como está probado.

   Y por eso el HTML no se tocó: `.lateral`, `.lateral-nav`, `.item` y
   `item--activo` ya existen y `app.js` los busca por ahí. Lo que cambia es en
   qué eje se reparten.

   ── POR QUÉ BARRA Y NO COLUMNA ──

   La columna fija de 260px se comía ancho de pantalla en un monitor de
   pantalla ancha para mostrar seis palabras. Una barra devuelve ese ancho a la
   tabla del listado, que es donde más falta hace: el listado tiene diez columnas
   y a 1440px medía 971px de marco.

   ── LO QUE NO SE PIDIÓ Y NO SE HIZO ──

   - Los rótulos de sección ("Resumen", "Cartera", "Sistema") **se ocultan**: en
     una barra horizontal no agrupan nada, y una fila de texto chico entre seis
     enlaces se lee como ruido. En el drawer siguen, porque ahí sí agrupan.
   - La nota del pie también: en una barra de una línea no hay dónde.
   - Los iconos **se quedan**, pero solo en la barra: los seis nombres juntos
     ocupan ~420px y entran de sobra. Los nombres son el rótulo; el ícono
     acompaña.
   ══════════════════════════════════════════════════════════════════════════ */

@media (min-width: 62rem) {
  /* Una sola columna: la barra arriba y el contenido abajo. El `minmax(0, 1fr)`
     que tenía la segunda columna del grid anterior venía de la misma trampa del
     `min-width: auto` — sin él, una tabla ancha estira la página entera. */
  .disposicion {
    display: block;
  }

  .lateral {
    /* Deja de flotar y deja de ser columna: pasa a ser la fila de arriba. */
    position: sticky;
    top: 0;
    inset: auto;
    height: auto;
    visibility: visible;

    flex-direction: row;
    align-items: center;
    gap: var(--r-md);

    padding: 0 max(var(--r-lg), env(safe-area-inset-right)) 0 max(var(--r-lg), env(safe-area-inset-left));
    background: var(--papel-alto);
    /* Abajo, no a la derecha: la barra separa de lo que hay debajo, que es lo
       que hay que separar. Con filete lateral, la barra seemed pegada al borde
       de la pantalla y no separaba nada. */
    border-right: none;
    border-bottom: 1px solid var(--regla);
    /* `overflow-y: auto` venía del drawer a pantalla completa. Acá sobra y hace
       mal: convierte la barra en un área de scroll con la barra de scroll del
       sistema en una línea de 60px. */
    overflow: visible;
  }

  /* El logo se queda a la izquierda y no se encoge: es el ancla de la barra. */
  .marca {
    flex: none;
    margin-right: var(--r-chico);
  }

  /* El nav toma lo que sobra y pone los seis en fila. */
  .lateral-nav {
    display: flex;
    align-items: center;
    flex: 1 1 auto;
    min-width: 0;
    gap: var(--r-chico);
  }

  /* Las tres listas en fila se vuelven UNA tira continua.
     `flex-wrap: nowrap` es explícito: los seis nombres entran en 62rem —medido,
     ocupan unos 430px de los 992 disponibles— y si faltara espacio es mejor
     que la barra scrollee en horizontal que no que el texto se corte. */
  .lateral-nav .lista {
    display: flex;
    /* **`flex-direction: row` es el arreglo, y faltaba.** `.lista` trae
       `flex-direction: column` del drawer (línea 414). Declarar solo `display:
       flex` y `align-items` no alcanza: la columna sigue mandando, y el nav se
       veía como una torre de seis enlaces apilados en vez de una fila.

       Y el `margin-bottom` de `.lista` también venía de la lista vertical: en
       una fila suma el alto de los tres `<ul>` y la barra midió 200px. */
    flex-direction: row;
    align-items: center;
    gap: var(--r-chico);
    flex: none;
    margin: 0;
  }

  /* Los rótulos de sección no van en una barra. En el drawer siguen, porque ahí
     sí agrupan: por debajo de 62rem este bloque no existe.

     **`.lateral-pie` sí vuelve, pero solo con el botón.** Estaba acá con
     `display: none` y `#btn-salir` vive adentro, así que **en escritorio no había
     forma de cerrar la sesión** — en la computadora compartida, que es
     justamente para lo que existe la clave. No daba ningún error: el botón
     sencillamente no estaba.

     Se recupera el bloque sin su texto —que en una fila de navegación no dice
     nada— y el botón queda solo con el ícono, como los de la barra. El texto va
     en un `<span class="boton-texto">` justamente para poder ocultarlo acá: a un
     nodo de texto pelado no lo saca el CSS.

     Y el ícono va con `aria-label` y `title` en el HTML, que valen en los dos
     modos, así que ocultar el texto no le deja nombre a nadie. */
  .lateral-nav .lateral-titulo {
    display: none;
  }

  .lateral-pie {
    display: flex;
    align-items: center;
    margin: 0;
    padding: 0;
    border-top: 0;
  }
  .lateral-pie-texto { display: none; }

  /* El botón de salir es un ÍTEM de la barra, no un botón de formulario.
   *
   * Medido antes: era un `boton--secundario` de `10x36` — el ícono daba `0x0` y
   * solo quedaba el borde. Encima le sobraba el ritmo: los seis ítems miden
   * 44px en la barra y el botón de salir 36, en una fila donde todo lo demás
   * tiene la misma altura.
   *
   * Sin borde y sin fondo, como los ítems: el hover toma el mismo lavado de la
   * marca. Y **nada de rojo**: en esta paleta el rojo significa "vencida" y
   * nada más, así que un ícono de salir en rojo se leería como una alerta. */
  .lateral-salir {
    min-height: 44px;
    padding-inline: var(--r-medio);
    background: transparent;
    border-color: transparent;
    border-radius: var(--r-chico);
    color: var(--tinta-media);
  }
  .lateral-salir:hover:not(:disabled) {
    background: var(--marca-lavado);
    color: var(--marca);
  }
  .lateral-salir .boton-texto { display: none; }

  /* El ícono sigue, pero más chico: en una fila es un acento, no el rótulo. */
  .item-icono {
    width: 28px;
    height: 28px;
  }
  .item-icono svg { width: 16px; height: 16px; }

  /* ── El ítem en la barra ──
     `justify-content: center` porque el drawer alineaba a la izquierda y acá
     cada uno tiene su ancho: centrado se lee como un menú, pegado a la
     izquierda se lee como texto corrido. */
  .item {
    justify-content: center;
    text-align: center;
    /* `width: 100%` venía del drawer, donde el ítem ocupa toda la columna. Acá
       haría que los seis se estiren a un tercio de la barra cada uno. */
    width: auto;
    min-height: 44px;
    padding: 0 var(--r-medio);
    border-radius: var(--r-chico);
    white-space: nowrap;
  }

  /* ── El activo: barra ABAJO, no fondo entero ──
     En el drawer un fondo entero funciona: hay ancho de sobra. En una barra, un
     rectángulo de fondo de 96px al lado de cinco textos sin fondo se lee como
     un botón grande y no como "estás acá".

     Abajo, el filete pega el ítem al borde inferior de la barra, que es de
     donde se lee: el menú está arriba y el contenido sigue abajo, y la marca
     apunta hacia abajo. */
  .item--activo {
    color: var(--marca);
    font-weight: 600;
    background: transparent;
    box-shadow: inset 0 -2px 0 0 var(--marca);
  }

  /* El hover sí lleva fondo lavado, como el drawer: es un estado momentáneo y
     un fondo lo hace legible sin competir con la barra del activo. */
  .item:hover:not(.item--activo):not(:disabled) {
    background: var(--marca-lavado);
    color: var(--marca);
  }

  /* El ícono del activo va en marca, como antes; el de los demás, apagado. El
     hover sigue moviéndolo a marca, igual que en el drawer. */
  .item--activo .item-icono {
    background: transparent;
  }

  /* ── El contenido ya no reserva el botón de menú ──
     El `padding-top` de 58px existía porque el botón de menú flotaba sobre el
     contenido. Acá el botón sigue oculto (`display: none`) y el contenido
     arranca debajo de la barra, que ocupa su propio alto. Con el padding
     quedaría un hueco de 58px entre la barra y el título. */
  .contenido {
    padding-top: var(--r-lg);
  }
}

/* ══════════════════════════════════════════════════════════════════════════
   LA BARRA NO CABÍA, Y EL ARREGLO NO ES SUBIR EL CORTE
   ──────────────────────────────────────────────────────────────────────────
   Medido a 1440px, con los seis nombres y sus iconos:

       el nav con los seis ítems      1166px
       el logo                         211px
       padding de los dos lados        32px
       ───────────────────────────────────────
       mínimo                        **1425px** (89.1rem)

   Y el corte está en 62rem = 992px. Con el nombre COMPLETO la barra desborda
   desde los ~1030px hasta los 1425: medido, 25px de desborde a 1000px y 33px a
   992px.

   **Subir el corte a 89rem sería el arreglo fácil y el equivocado**: deja a
   todos los portátiles de 1280 y 1366 —los más comunes— sin barra, con el
   drawer. Un menú que aparece y desaparece con el tamaño de la ventana no es un
   menú de escritorio.

   ── EL ARREGLO REAL: LOS ICONOS FUERA DE LA BARRA ──

   **Lo que hace falta es una cosa: sacar los iconos.** Miden 28px más 10px de
   hueco cada uno —228px los seis— y son redundantes: el ícono de "Clientes" no
   agrega nada que la palabra no diga. En el drawer siguen estando, porque ahí
   hay ancho de sobra y sí aportan.

   Se probó primero cambiar el nav de `flex: 0 0 auto` a `flex: 1 1 auto`, y
   **eso NO arregla nada por sí solo**: rompiendo esa regla a propósito, la
   barra siguió pasando los seis anchos. El nav sigue ocupando lo que necesita;
   que pueda encogerse no lo hace encogerse.

   La consecuencia es que el `flex: 1 1 auto` del nav más arriba **no está
   justificado por esta medida**: se llegó a él buscando el arreglo y no resultó
   ser el que funciona. Queda porque `min-width: 0` sí lo necesita para que el
   nav pueda reducirse, pero el desborde lo resuelve lo de abajo.

   Y hay una lección de método en el camino, que es la que más costó:

   **una prueba que pasa una vez no prueba nada hasta que se rompe a
   propósito.** La primera vez que se quitó la regla de los iconos, la prueba dio
   verde en los seis anchos — y estaba mal. No miraba el ancho que sobraba: el
   nav todavía no scrolleaba, así que el `scrollWidth` de la página daba 0 y todo
   parecía bien. Recién al repetir la rotura, con la comprobación del nav ya
   escrita, aparecieron los 17px y 49px reales.

   Por eso la prueba de escritorio mira `nav.scrollWidth > nav.clientWidth` y no
   solo el `scrollWidth` de la página: **el nav puede quedarse sin espacio sin
   que la página todavía desborde**, y ese es el instante en que hay que avisar.

   ── POR QUÉ NO SE TOCA `min-width` PARA QUE SE APLASTE ──

   `white-space: nowrap` está puesto a propósito. Si el nombre no cabe, tiene que
   **desbordarse y verse**, no cortarse: `.clientes` salía con el nombre del
   cliente a la mitad. Un nombre de menú a medio leer es peor que uno entero
   apretado. */

/* Los iconos fuera de la barra. El razonamiento está arriba: son 228px los seis
   y son redundantes con el nombre que ya está al lado. En el drawer siguen
   estando, porque ahí hay ancho de sobra y sí aportan. */
@media (min-width: 62rem) {
  .lateral-nav .item-icono {
    display: none;
  }
}
/* ══════════════════════════════════════════════════════════════════════════
   LA PANTALLA DE ACCESO
   ──────────────────────────────────────────────────────────────────────────
   Va al FINAL del archivo a propósito, y no por prolijía: dos reglas con la
   misma especificidad gana la última, y acá hay varias que compiten con lo que
   ya existe —`.campo-input`, `.boton--primario`, `.campo-error`—. Es el mismo
   motivo por el que el bloque de la barra de navegación está donde está.

   ── NINGÚN COLOR NUEVO ──

   Todo sale de tokens que la aplicación ya tenía. **El panel del formulario
   usa los suyos**: `--papel` de fondo, `--papel-alto` en la caja, `--regla` en
   el borde, `--marca` en el botón, `--rojo` en el error.

   El panel de la marca NO puede: necesita ser oscuro en los DOS temas, y los
   tokens de superficie se invierten con el tema —en oscuro `--tinta` pasa a ser
   `#f1f3f7`, un blanco, y la pantalla quedaría con un panel claro arriba y otro
   oscuro abajo, que es lo contrario de lo que se quiere—.

   Por eso hay TRES tokens congelados abajo. Sus valores **ya estaban en la
   paleta**, tomados del tema que correspondía:

   | token | valor | de dónde salió |
   |---|---|---|
   | `--acceso-fondo` | `#0f172a` | `--tinta` del tema claro |
   | `--acceso-texto` | `#ffffff` | `--papel-alto` del tema claro |
   | `--acceso-apagado` | `#b7bfcd` | `--tinta-media` del tema **oscuro** |

   El apagado se tomó del tema oscuro a propósito, y no por capricho: sobre
   `#0f172a` da 9.3 de contraste, mientras el `--acento` del tema claro
   (`#94a3b8`, que `AGENTS.md` marca como SUPERFICIE ONLY) daría menos, y el del
   tema oscuro (`#64748b`) daría 3.5 — por debajo de 4.5.

   ── POR QUÉ PARTIDA Y NO CENTRADA ──

   Una caja angosta centrada en el medio es la forma más rápida y más olvidable de
   una pantalla de acceso: es lo que hace cualquier plantilla. Partida da un
   lugar donde poner el nombre de la aplicación, que es **la información que
   importa**: en una computadora compartida, lo primero que hay que contestar es
   "¿estoy abriendo la aplicación correcta?".

   ── LO QUE NO ESTÁ ──

   **No hay animación de entrada.** Se probó y se sacó: es el gesto más
   reconocible de una pantalla generada, se repite en cada salida —o sea, cada
   vez que alguien cierra sesión en una PC compartida— y va en contra de lo que
   tiene que pasar acá. Lo que se ve al entrar es que *el formulario desaparece
   y la aplicación aparece*, que es un cambio que se entiende solo.

   **No hay párrafo de contexto.** El que había explicaba cómo está hecho el
   sistema —dónde viven las facturas, que no hay usuarios— y eso lo necesita
   quien lo mantiene, no quien está abriendo la puerta.

   El drama está en el contraste: media pantalla de casi negro contra media de
   blanco. Encima le pones una animación y lo diluí.
   ══════════════════════════════════════════════════════════════════════════ */

.acceso {
  display: grid;
  grid-template-columns: 1fr 1fr;
  min-height: 100dvh;
  background: var(--papel);

  /* Los tres del panel de la marca. Definidos acá y no en `:root` porque solo los
     usa esta pantalla, y para que quede escrito por qué no siguen al tema. */
  --acceso-fondo: #0f172a;
  --acceso-texto: #ffffff;
  --acceso-apagado: #b7bfcd;
}

/* ── Panel de la marca ── */
.acceso-marca {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--r-chico);
  /* La pantalla va de borde a borde, así que los notch del teléfono en
     horizontal la muerden. El resto de la app ya usa `env()`, así que esto no
     es una excepción: es la misma regla. */
  padding: var(--r-lg)
           max(var(--r-lg), env(safe-area-inset-right))
           var(--r-lg)
           max(var(--r-lg), env(safe-area-inset-left));
  background: var(--acceso-fondo);
  color: var(--acceso-texto);
}

.acceso-logo {
  display: grid;
  place-items: center;
  width: 44px; height: 44px;
  color: var(--marca-sobre);
  background: var(--marca);
  border-radius: var(--r-md);
}
.acceso-logo svg { width: 24px; height: 24px; }

/* El nombre de la aplicación: es lo más grande de la pantalla y lo primero que
   se lee. El `letter-spacing` negativo es el de los títulos de la app, para que la
   marca se vea la misma acá y adentro. `balance` reparte en renglones parejos,
   que es lo que ya hace `.vista-titulo`. */
.acceso-titulo {
  margin: var(--r-chico) 0 0;
  font-size: clamp(1.75rem, 3vw, 2.5rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.1;
  color: var(--acceso-texto);
  text-wrap: balance;
}

.acceso-bajada {
  margin: 0;
  font-size: var(--t-base);
  color: var(--acceso-apagado);
  text-wrap: pretty;
}

/* La regla de `--marca`: el único acento de color del lado oscuro. */
.acceso-marca::after {
  content: '';
  display: block;
  width: 48px;
  height: 3px;
  margin-top: var(--r-chico);
  background: var(--marca);
  border-radius: 2px;
}

/* ── Panel del formulario ── */
.acceso-panel {
  display: grid;
  place-items: center;
  padding: var(--r-lg)
           max(var(--r-lg), env(safe-area-inset-right))
           var(--r-lg)
           max(var(--r-lg), env(safe-area-inset-left));
}

.acceso-caja {
  width: 100%;
  max-width: 24rem;
  padding: var(--r-lg);
  background: var(--papel-alto);
  border: 1px solid var(--regla);
  border-radius: var(--r-lg);
  box-shadow: var(--sombra-flotante);
}

.acceso-subtitulo {
  margin: 0;
  font-size: var(--t-lg);
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--tinta);
  text-wrap: balance;
}

/* La ayuda SOLO existe en el primer uso. En "Entrar" el título ya dice todo, y
   dos renglones diciendo lo mismo es ruido.

   El `:empty` en vez de `hidden` es a propósito: `#ayuda-acceso` se vacía con
   `textContent = ''` y no con el atributo, y una clase con `display` gaining
   contra la `[hidden]` es un problema que este proyecto ya pagó tres veces. */
.acceso-ayuda {
  margin: var(--r-chico) 0 var(--r-md);
  font-size: var(--t-sm);
  color: var(--tinta-media);
}
.acceso-ayuda:empty { display: none; }

.acceso-caja .campo { margin-bottom: var(--r-medio); }

/* El botón ocupa todo el ancho: es una sola acción y no hay competencia por ella.
   El `min-height` de 44px es el área táctil, no un capricho de diseño. */
.acceso-boton {
  width: 100%;
  min-height: 44px;
  margin-top: var(--r-chico);
}

/* El error de la clave. `--rojo` es "vencida" en la paleta, y acá es literalmente
   "esa clave no sirve": no es un color nuevo, es el que ya significaba "algo está
   mal". */
.acceso-caja .campo-error {
  margin: 0 0 var(--r-chico);
}

/* El campo con la clave equivocada se marca con el borde, no solo el texto: el
   error tiene que verse sin leer. */
.acceso-caja .campo-input[aria-invalid="true"] { border-color: var(--rojo); }
.acceso-caja .campo-input:focus-visible { border-color: var(--marca); }
.acceso-caja .campo-input[aria-invalid="true"]:focus-visible { border-color: var(--rojo); }

/* ── El móvil: una sola columna ──

   El panel de la marca se achica a una franja con el nombre y la bajada, que es
   lo que identifica la pantalla; el formulario va en el espacio que sobra,
   CENTRADO.

   Antes iba pegado arriba y quedaba un hueco muerto abajo de casi media
   pantalla. Centrado no: `start` sigue siendo lo correcto en el único caso en
   que no hay espacio de sobra, que es el `@media` de más abajo. */
@media (max-width: 52rem) {
  .acceso { grid-template-columns: 1fr; grid-template-rows: auto 1fr; }

  .acceso-marca {
    gap: var(--r-chico);
    padding: var(--r-md)
             max(var(--r-lg), env(safe-area-inset-right))
             var(--r-md)
             max(var(--r-lg), env(safe-area-inset-left));
  }

  .acceso-logo { width: 36px; height: 36px; }
  .acceso-logo svg { width: 20px; height: 20px; }

  .acceso-titulo { font-size: 1.375rem; margin: var(--r-chico) 0 0; }
  .acceso-bajada { font-size: var(--t-sm); }
  .acceso-marca::after { width: 32px; height: 2px; margin-top: 0; }

  .acceso-panel {
    padding: var(--r-md)
             max(var(--r-lg), env(safe-area-inset-right))
             var(--r-lg)
             max(var(--r-lg), env(safe-area-inset-left));
  }
  .acceso-caja { max-width: 28rem; padding: var(--r-md); }
  .acceso-boton { min-height: 48px; }
}

/* ── Pantalla baja y angosta ──

   Con el teclado del móvil abierto queda poco alto, y una caja centrada empuja
   el botón hacia abajo y lo esconde. `start` lo deja pegado arriba, que es lo
   que hay que ver primero. Este es el único caso en el que `start` es lo
   correcto, y por eso vive acá y no en el bloque de arriba. */
@media (max-height: 30rem) {
  .acceso-marca { display: none; }
  .acceso-panel { align-items: start; padding-top: var(--r-md); }
}

/* ══════════════════════════════════════════════════════════════════════════
   EL GUARD DE `hidden`

   UNA sola regla para todo el archivo, y va AL FINAL a propósito.

   ── EL PROBLEMA QUE RESUELVE ──

   El atributo `hidden` trae `display: none` desde la hoja del navegador, pero
   **cualquier regla que declare `display` le gana**, porque `[hidden]` es un
   selector de atributo y tiene la misma especificidad que una clase: gana la
   última.

   En este archivo hay cinco elementos que se esconden con `hidden` y que
   declaran `display`: `.vista`, `.aviso-ejemplos`, `.paso-import`, `.campo` y
   `.acceso`. Antes cada uno necesitaba su propio `.algo[hidden] { display: none }`,
   y se acumularon a cinco porque **el que faltaba no se nota**: el elemento
   queda dibujado, el `hidden` sigue en el DOM, y ninguna prueba lo ve.

   Ocurrió dos veces acá y unaFoja rotura grave:

     1. `#contenedor-fecha-cobro` — "Fecha del cobro" se veía siempre.
     2. `#acceso-repetir-campo` — el formulario pedía repetir una clave que no
        se estaba poniendo.
     3. `.acceso` — **este es el que reportaste**: al entrar, el panel de
        `100dvh` seguía ocupando lugar y la aplicación se dibujaba DEBAJO.
        Había que hacer scroll para verla.

   ── POR QUÉ ESTÁ AL FINAL Y NO EN EL RESET ──

   Porque en el reset perdería: si `[hidden] { display: none }` estuviera arriba,
   `.campo { display: flex }` más abajo volvería a ganarle. Ganar por **orden** es
   exactamente el idioma que este archivo ya usa para el bloque de la barra y
   para las áreas táctiles.

   Sin `!important`, que acá no hace falta y sería un martillo.

   ── LO QUE ESTE GUARD NO PUEDE HACER ──

   No arregla un `display` que se ponga DESPUÉS de esta línea. Por eso el bloque
   va al final del archivo y no en un lugar "lógico": es lo único que lo vuelve
   cierto. `estructura.js` bloque 13 lo vigila.
   ══════════════════════════════════════════════════════════════════════════ */
[hidden] { display: none; }