/* Kiwi ERP · hoja de estilo propia.
   Sin framework y sin CDN: la CSP de src/app.js prohíbe estilos y scripts externos.
   Móvil primero: la tabla se convierte en tarjetas debajo de 760 px.

   ORDEN DE LAS SECCIONES (cada una arranca con una banda ===== para que se vean
   de un vistazo y para que un merge no tenga que leer el archivo entero):

     1  TIPOGRAFÍA          @font-face autoalojadas
     2  IDENTIDAD           variables de color, espaciado, radios y elevación
     3  BASE                reinicio, cuerpo, títulos, enlaces, foco
     4  ICONOS
     5  ARMAZÓN             barra superior, barra lateral, contenido
     6  MIGAS Y ENCABEZADOS
     7  BLOQUES Y TARJETAS
     8  TABLAS
     9  DISTINTIVOS         chips de estado
    10  FORMULARIOS
    11  BOTONES
    12  FILTROS
    13  AVISOS Y NOTIFICACIONES
    14  MODAL Y PANEL LATERAL
    15  ESQUELETOS DE CARGA
    16  EDITOR DE LÍNEAS    líneas de movimientos, cotizaciones y facturas
    17  PAGINACIÓN
    18  ACCESO Y ERROR
    19  RESPONSIVO
    20  MOVIMIENTO REDUCIDO
    21  AYUDA               manual de usuario: índice lateral y ancho de lectura
    22  PRIMEROS PASOS      guía de arranque del tablero
*/

/* ===========================================================================
   1 · TIPOGRAFÍA
   ---------------------------------------------------------------------------
   Las fuentes van AUTOALOJADAS en /fuentes, no traídas de Google.
   Dos razones, en este orden:
     · la CSP de src/app.js no deja cargar estilos ni fuentes de otro origen, y
       relajarla para una tipografía sería pagar un agujero por un adorno;
     · así el panel abre igual sin salida a internet y no se le avisa a un
       tercero cada vez que un contador de Cartago entra al sistema.
   `font-display: swap` + una pila de respaldo REAL: si el .woff2 no llega, el
   texto se lee igual desde el primer instante con la fuente del sistema.
   El rango latin-ext es obligatorio: el símbolo del colón (₡, U+20A1) vive ahí.
   ======================================================================== */
@font-face {
  font-family: "Open Sans Kiwi";
  src: url("/fuentes/open-sans-latin.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Open Sans Kiwi";
  src: url("/fuentes/open-sans-latin-ext.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Poppins Kiwi";
  src: url("/fuentes/poppins-600-latin.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Poppins Kiwi";
  src: url("/fuentes/poppins-600-latin-ext.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ===========================================================================
   2 · IDENTIDAD
   ---------------------------------------------------------------------------
   Todo el color, el espaciado y la elevación salen de acá. Un componente NUNCA
   lleva un hexadecimal suelto: si hace falta un tono nuevo, se agrega una
   variable con nombre de ROL (para qué sirve), no de color (de qué color es).

   Sobre los dos verdes: el acento de marca es #059669, pero sobre blanco da
   3,77:1 de contraste y eso NO alcanza para texto normal (WCAG pide 4,5:1). Por
   eso el acento se usa para bordes, barras de estado activo y anillos de foco
   —donde el mínimo es 3:1— y para texto y botones rellenos se usa --acento-fuerte
   (#047857), que da 5,55:1 sobre blanco. Es el mismo verde, un escalón más
   oscuro: la marca se mantiene y el texto se lee.
   ======================================================================== */
:root {
  /* -- color · estructura ------------------------------------------------ */
  --primario: #334155;          /* barra lateral, títulos                   */
  --primario-hundido: #2a3647;  /* fondo del bloque de contexto en el menú  */
  --secundario: #475569;        /* texto secundario (7,4:1 sobre blanco)    */
  --texto: #0f172a;
  --texto-suave: #475569;
  --texto-tenue: #64748b;       /* 4,8:1 sobre blanco: mínimo para texto    */
  --fondo: #f8fafc;
  --superficie: #ffffff;
  --apagado: #f2f3f4;
  --borde: #e6e8ea;
  --borde-fuerte: #cbd5e1;

  /* -- color · borde de los botones que SE PUEDEN APRETAR ------------------
     El botón secundario (blanco con borde) usaba `--borde-fuerte`, que da 1,5:1
     sobre blanco: el mismo gris apagado con el que el navegador pinta un control
     deshabilitado. La queja textual fue «los botones salen grises, no sabemos si
     se pueden clickear», y tenía razón: un control accionable tiene que
     distinguirse de su fondo con al menos 3:1 (WCAG 1.4.11, contraste de
     componentes de interfaz), y acá se va más allá porque es la única pista de
     que el botón existe. Es un token propio y no `--borde-fuerte` retocado
     porque ese sigue siendo el borde correcto para campos y tarjetas, que no se
     aprietan.                                                                 */
  --boton-borde: #64748b;       /* 4,8:1 sobre blanco                        */
  --boton-borde-hover: #334155; /* 9,7:1: el foco y el hover se ven venir    */

  /* -- color · acento y acciones ----------------------------------------- */
  --acento: #059669;            /* bordes, barras activas, anillo de foco   */
  --acento-fuerte: #047857;     /* texto y relleno de botón (5,55:1)        */
  --acento-hundido: #036049;    /* :hover / :active del botón principal     */
  --acento-suave: #ecfdf5;
  --acento-borde: #a7f3d0;

  /* -- color · semántica ------------------------------------------------- */
  --destructivo: #dc2626;       /* 4,84:1 sobre blanco                      */
  --destructivo-hundido: #b91c1c;
  --destructivo-suave: #fef2f2;
  --destructivo-borde: #fecaca;
  --alerta: #92400e;            /* ámbar oscuro: 7,1:1 sobre blanco         */
  --alerta-suave: #fffbeb;
  --alerta-borde: #fde68a;
  --info: #1d4ed8;
  --info-suave: #eff6ff;
  --info-borde: #bfdbfe;
  /* 1450 · LA FACTURA INTERNA (el documento que NO va a Hacienda). Un rojo
     propio y con rol: que nadie la confunda con la factura fiscal. Nunca solo
     color: va siempre con la insignia «No va a Hacienda».                     */
  --interna: #b91c1c;           /* 6,47:1 sobre blanco; blanco encima, igual */
  --interna-hundido: #991b1b;   /* :hover del botón (8,31:1)                 */
  --interna-suave: #fef2f2;     /* fondo de la banda; --interna encima 5,91:1 */
  --interna-borde: #fecaca;
  --interna-lateral: #fca5a5;   /* sobre la barra lateral (5,46:1)           */

  /* -- color · sobre la barra lateral oscura ----------------------------- */
  --lateral-texto: #e2e8f0;     /* 11,6:1 sobre --primario                  */
  --lateral-tenue: #cbd5e1;     /* 6,98:1: alcanza para los rótulos         */
  --lateral-hover: rgba(255, 255, 255, 0.08);
  --lateral-activo: rgba(255, 255, 255, 0.14);

  /* -- tipografía --------------------------------------------------------
     Poppins para títulos, Open Sans para el cuerpo. La pila de respaldo es de
     verdad: fuentes que existen en Windows, macOS, iOS, Android y Linux.      */
  --fuente-titulo: "Poppins Kiwi", "Segoe UI", system-ui, -apple-system, Roboto,
    "Helvetica Neue", Arial, sans-serif;
  --fuente-cuerpo: "Open Sans Kiwi", "Segoe UI", system-ui, -apple-system, Roboto,
    "Helvetica Neue", Arial, sans-serif;
  --fuente-mono: ui-monospace, "Cascadia Code", "Cascadia Mono", Consolas,
    "Liberation Mono", monospace;

  /* Escala de tamaños. El cuerpo es de 16 px; los tamaños chicos son para
     rótulos y metadatos, nunca para el texto que hay que leer seguido.        */
  --t-xs: 0.75rem;     /* 12 px · rótulos de grupo, data-etiqueta            */
  --t-sm: 0.8125rem;   /* 13 px · encabezados de tabla, chips               */
  --t-md: 0.875rem;    /* 14 px · tabla compacta, ayuda                     */
  --t-tabla: 0.9375rem;/* 15 px · celdas de tabla                           */
  --t-base: 1rem;      /* 16 px · cuerpo                                    */
  --t-lg: 1.125rem;    /* 18 px · h2                                        */
  --t-xl: 1.375rem;    /* 22 px · h1                                        */
  --t-2xl: 1.75rem;    /* 28 px · números de tarjeta                        */
  --t-3xl: 2.25rem;    /* 36 px · marca del acceso                          */

  /* -- espaciado · escala de 4 y 8, sin valores sueltos ------------------ */
  --e1: 4px;
  --e2: 8px;
  --e3: 12px;
  --e4: 16px;
  --e5: 24px;
  --e6: 32px;
  --e7: 48px;
  --e8: 64px;

  /* -- radios ------------------------------------------------------------ */
  --r-sm: 4px;
  --r-md: 6px;
  --r-lg: 10px;
  --r-pastilla: 999px;

  /* -- elevación · cuatro niveles, no más -------------------------------- */
  --elev-1: 0 1px 2px rgba(15, 23, 42, 0.05);
  --elev-2: 0 1px 2px rgba(15, 23, 42, 0.06), 0 2px 8px rgba(15, 23, 42, 0.05);
  --elev-3: 0 2px 4px rgba(15, 23, 42, 0.06), 0 8px 24px rgba(15, 23, 42, 0.1);
  --elev-4: 0 8px 16px rgba(15, 23, 42, 0.1), 0 24px 48px rgba(15, 23, 42, 0.18);

  /* -- movimiento · solo donde comunica algo ----------------------------- */
  --mov-rapido: 150ms;
  --mov-medio: 220ms;
  --mov-lento: 300ms;
  --curva: cubic-bezier(0.2, 0, 0.2, 1);

  /* -- medidas del armazón ----------------------------------------------- */
  /* 14-sep-2026 · MEDIDO, NO ESTIMADO. Con 248 px al rótulo de una entrada le
     quedaban 131 px libres —el resto se lo comen el relleno de la barra (16),
     el borde del activo (3), el relleno del enlace (24), el icono (18), su
     separación (12) y la estrella de favoritos (44)—, así que el menú mentía:
     «Órdenes de co…», «Facturas recibi…», «Conciliación co…». Con 280 quedan
     163 px y entran de una línea 68 de los 74 títulos de `util/navegacion.js`;
     los seis más largos (el mayor, «Correo a clientes y proveedores», mide 214
     px en Open Sans 14/600) pasan a DOS LÍNEAS, que dicen el nombre entero.
     Más ancho que esto se lo quitaría al trabajo sin resolver esos seis. */
  --lateral: 280px;
  /* Contraída a iconos: 64 px deja el objetivo táctil de 44 con aire a los
     lados. Menos que eso y el icono queda pegado al borde de la pantalla. */
  --lateral-contraida: 64px;
  /* Dos variables y no una. `--alto-barra-base` es el alto de DISEÑO: el mínimo
     que mide la barra aunque esté casi vacía. `--alto-barra` es el alto REAL,
     que `panel.js` remide cuando el nombre de la empresa se parte en dos líneas
     o cuando los controles se envuelven, y del que cuelgan los encabezados
     pegajosos de las tablas.
     Separarlas quita un trinquete: cuando el mínimo de la barra era el valor
     medido, la barra podía crecer pero nunca volver a bajar —se envolvía en el
     teléfono de pie y seguía igual de alta al girarlo. */
  --alto-barra-base: 56px;
  --alto-barra: var(--alto-barra-base);
  --ancho-contenido: 1440px;
  /* Las listas (`.vista-lista`, ver parciales/layout.ejs) no se leen de corrido
     como una ficha: cada columna que se aplasta es un dato que se parte en
     cinco renglones. Usan TODO el ancho de la pantalla. Hasta el 08-oct-2026
     llevaban un tope de 1,920 px «para que la fila se leyera de un vistazo»,
     pero en un monitor ancho (2,560 o 3,440) eso dejaba la tabla pegada a la
     izquierda, la barra superior corrida sola hasta el borde derecho y una
     franja vacía de 400 a 1,200 px: justo lo que Adán reportó como «el
     contenido angosto y la barra suelta». */
  --ancho-lista: none;
}

/* ===========================================================================
   3 · BASE
   ======================================================================== */
*,
*::before,
*::after { box-sizing: border-box; }

/* `hidden` tiene que ganarle a cualquier `display` nuestro.
   El `[hidden] { display: none }` del navegador es una regla de especificidad
   mínima: basta con que una clase diga `display: flex` para que un elemento
   marcado como oculto se siga viendo. Pasó con la cabecera vacía de
   «Favoritos», que es un flex y salía aunque no hubiera ninguno. */
[hidden] { display: none !important; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-padding-top: calc(var(--alto-barra) + var(--e4));
}

body {
  margin: 0;
  font-family: var(--fuente-cuerpo);
  font-size: var(--t-base);
  line-height: 1.5;
  color: var(--texto);
  background: var(--fondo);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
  font-family: var(--fuente-titulo);
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--texto);
}
h1 { font-size: var(--t-xl); line-height: 1.25; margin: 0 0 var(--e2); }
h2 { font-size: var(--t-lg); line-height: 1.3; margin: 0 0 var(--e3); }
h3 { font-size: var(--t-base); line-height: 1.35; margin: 0 0 var(--e2); }
p { margin: 0 0 var(--e3); }

a { color: var(--acento-fuerte); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--acento-hundido); }

/* Un código corto (SKU, sucursal, terminal) no se parte: «ART-044» cortado en
   dos renglones se lee como dos datos. El que SÍ se parte es el que lleva
   `.quiebre` a propósito, como el CABYS de 13 dígitos o una clave de 50. */
.tabla td.mono:not(.quiebre),
.tabla th.mono:not(.quiebre) { white-space: nowrap; }

code, .mono {
  font-family: var(--fuente-mono);
  font-size: 0.9em;
  font-variant-numeric: tabular-nums;
}

/* Los números NO bailan al ordenar: cifras de ancho fijo en todo lo que sea
   monto, cantidad o fecha. Es la diferencia entre una columna que se lee de un
   vistazo y una que hay que ir leyendo dígito por dígito. */
.der,
.numero,
.tabla td.der,
.tabla th.der,
input[type="date"],
input[type="number"] { font-variant-numeric: tabular-nums; }

/* Foco visible SIEMPRE. Nunca se quita un contorno sin poner otro mejor. */
:focus-visible {
  outline: 2px solid var(--acento);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

.saltar {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: var(--e2) var(--e4);
  background: var(--acento-fuerte);
  color: #fff;
  border-radius: 0 0 var(--r-md) 0;
  font-weight: 600;
  text-decoration: none;
}
.saltar:focus { left: 0; top: 0; color: #fff; }

/* Texto que existe para el lector de pantalla y no se ve. No se usa
   `display:none` ni `visibility:hidden` a propósito: esos lo esconden TAMBIÉN
   del lector, que es justo lo contrario de lo que se busca. */
.oculto-visual {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ===========================================================================
   4 · ICONOS
   ---------------------------------------------------------------------------
   Una sola familia, trazo 1.75, rejilla 24×24 (src/vistas/parciales/iconos.ejs).
   Heredan el color del texto y NUNCA comunican solos: siempre hay texto al lado
   o un aria-label.
   ======================================================================== */
.sprite-iconos { display: none; }

.icono {
  width: 18px;
  height: 18px;
  flex: none;
  vertical-align: -3px;
  color: inherit;
}
.icono.chica { width: 14px; height: 14px; vertical-align: -2px; }
.icono.grande { width: 22px; height: 22px; vertical-align: -5px; }

/* ===========================================================================
   5 · ARMAZÓN
   ======================================================================== */
.armazon {
  display: grid;
  grid-template-columns: var(--lateral) 1fr;
  /* El PIE es una fila más de la rejilla, no un hermano suelto debajo. Cuando
     vivía fuera del armazón, el armazón terminaba 53 px antes que la página: el
     `sticky` de la barra lateral se topa con el fondo de su bloque contenedor,
     así que al llegar al final de una página larga la barra se despegaba del
     borde inferior y esos 53 px del pie —fondo claro, de lado a lado— quedaban
     debajo de «Mi cuenta» como una franja gris. Metiendo el pie en la columna
     del contenido, el armazón mide lo mismo que la página y no hay nada debajo
     de la columna oscura. De paso el pie se centra bajo el contenido y no bajo
     la página entera, que era lo que se quería desde el principio. */
  grid-template-rows: auto 1fr auto;
  grid-template-areas: "lateral barra" "lateral contenido" "lateral pie";
  /* `dvh` es el alto que de verdad se ve: en el teléfono, con la barra de
     direcciones desplegada, `100vh` es MÁS alto que la ventana y la última fila
     del menú queda por debajo del borde. El `100vh` se queda como respaldo para
     el navegador que no entienda `dvh` (lo descarta y se queda con el de
     arriba). */
  min-height: 100vh;
  min-height: 100dvh;
  /* La barra lateral se queda pegada arriba y mide el alto de la ventana; sin
     esta franja, en una página larga quedaría flotando sobre el fondo claro. La
     franja pinta la columna de la izquierda hasta donde llegue el contenido. */
  background: linear-gradient(
    90deg,
    var(--primario) 0,
    var(--primario) var(--lateral),
    transparent var(--lateral)
  );
}

/* -- barra superior ------------------------------------------------------ */
.barra {
  grid-area: barra;
  display: flex;
  align-items: center;
  /* Interruptor de los RÓTULOS de los controles (el texto de «Buscar…», de
     «Cambiar empresa», de «Cambiar» y de «Salir»). Las condiciones que lo
     apagan están repartidas —el ancho de la propia barra, el menú desplegado,
     el teléfono— pero la consecuencia se escribe UNA vez, más abajo, en
     `display: var(--rotulos-barra)`. Antes cada corte repetía la lista de
     selectores y por eso el de 760 px se quedó corto: nadie se acordó de que
     entre 761 y 1100 la barra no empieza en el borde de la pantalla, sino
     donde termina el menú.

     El nombre accesible NUNCA depende de esto: cada control lleva su
     `aria-label` en la plantilla, así que apagar el rótulo no deja un botón
     mudo para el lector de pantalla. */
  --rotulos-barra: inline;
  /* La barra se declara contenedor para poder preguntar por SU ancho y no por
     el de la ventana: con el menú desplegado en una tableta de 768 la ventana
     mide 768 pero a la barra le quedan 504 px, y esa es la medida que decide si
     los rótulos caben. Donde no haya `@container` mandan las consultas de
     ventana de la sección 19 y, en última instancia, el `flex-wrap`. */
  container: barra / inline-size;
  /* Último seguro contra el desborde: si a pesar de la compactación algún
     rótulo largo (el nombre de una caja, «FE: Sin configurar») no cabe, los
     controles bajan a una segunda línea. La barra se hace más alta —`panel.js`
     mide el alto real y lo publica en `--alto-barra`, así que los encabezados
     fijos siguen pegados donde deben— y la página NUNCA se desplaza de lado. */
  flex-wrap: wrap;
  gap: var(--e3);
  min-height: var(--alto-barra-base);
  padding: var(--e2) var(--e5);
  background: var(--superficie);
  border-bottom: 1px solid var(--borde);
  position: sticky;
  top: 0;
  z-index: 20;
}

/* `min-width: 0` en los dos lados: el contenedor flex no encoge por debajo del
   contenido de sus hijos si no se le dice, y el nombre de la empresa (que sí
   puede recortarse con puntos suspensivos) empujaba la página entera de lado en
   375 y en 768. Lo que cede primero es el texto. */
.barra { min-width: 0; }
.barra-derecha {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--e2);
  /* Era `flex: none` —los controles no encogían nunca— y eso es lo que sacaba
     la barra de la pantalla: entre 761 y 1100 px, con el menú desplegado, a la
     derecha le quedan poco más de 500 px y el grupo medía 136 px más que eso.
     Ahora el grupo sí puede encoger y, si aun así no cabe, sus controles bajan
     de línea en vez de empujar la página. Los iconos no se deforman: cada
     control lleva su `min-width`. */
  flex: 0 1 auto;
  flex-wrap: wrap;
  justify-content: flex-end;
  min-width: 0;
}

/* Quién soy y con qué permisos. Antes era un nombre suelto en la esquina; ahora
   el rol se ve sin tener que adivinarlo desde el menú que aparece o no. */
.usuario-actual {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  line-height: 1.25;
  min-width: 0;
}
.usuario-actual .nombre {
  font-size: var(--t-md);
  font-weight: 600;
  color: var(--texto);
  max-width: 30ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 08-oct-2026 · el rótulo del rol se llamaba `.rol` a secas y heredaba la
   tarjeta de «Roles y permisos» (§ de B10): borde, relleno de 12 px y margen
   de 8 arriba y abajo. Eso partía «quién soy» en dos cajas y la barra medía
   92 px en vez de 56 en TODOS los anchos. Como `panel.js` publica ese alto en
   `--alto-barra`, el exceso se contagiaba a los encabezados pegajosos de las
   tablas, a la barra de selección masiva y a los avisos. */
.usuario-actual .usuario-rol { font-size: var(--t-xs); color: var(--texto-tenue); }
.en-linea { display: inline; }

/* -- controles de la barra superior ---------------------------------------
   Buscador global, distintivo de ambiente, selector de empresa y campana.
   Todos comparten alto (36 px en escritorio, 44 en táctil por la regla de
   `pointer: coarse`) para que la barra lea como una sola fila y no como cuatro
   trastes de distinto tamaño puestos uno al lado del otro.                   */
.barra-buscar {
  display: flex;
  align-items: center;
  gap: var(--e2);
  min-height: 36px;
  min-width: 220px;
  padding: var(--e1) var(--e2) var(--e1) var(--e3);
  font: inherit;
  font-size: var(--t-md);
  /* --texto-tenue sobre --fondo da 4,6:1: pasa por poco. Como acá el texto es
     chico y va sobre gris, se usa el tono de arriba, que da 7:1 de sobra. */
  color: var(--texto-suave);
  text-align: left;
  background: var(--fondo);
  border: 1px solid var(--boton-borde);
  border-radius: var(--r-md);
  cursor: pointer;
  transition: border-color var(--mov-rapido) var(--curva),
    background-color var(--mov-rapido) var(--curva);
}
.barra-buscar:hover { background: var(--superficie); border-color: var(--secundario); }
.barra-buscar .texto { flex: 1; }
/* El atajo se muestra escrito: un atajo que nadie ve no lo usa nadie. */
.barra-buscar .atajo {
  font-family: var(--fuente-mono);
  font-size: var(--t-xs);
  color: var(--texto-tenue);
  padding: 2px var(--e1);
  background: var(--superficie);
  border: 1px solid var(--borde-fuerte);
  border-bottom-width: 2px;
  border-radius: var(--r-sm);
}

/* Acá se cobra el interruptor declarado en `.barra`: los rótulos de los cuatro
   controles se muestran o se callan en un solo sitio, pase lo que pase en los
   cortes responsivos. Con el rótulo callado queda el icono, que es lo que sigue
   diciendo qué hace el botón. */
.barra-buscar .texto,
.barra-buscar .atajo,
.barra-boton .texto,
.barra .boton.chico .texto { display: var(--rotulos-barra); }

/* Cuando la barra se queda sin ancho, los rótulos sobran antes que los iconos.
   720 px es el punto en el que una empresa corriente —buscador, distintivo de
   FE, campana, usuario y «Salir»— todavía entra con el texto puesto; por debajo
   no entra, y lo que se pierde es el rótulo, no el control. */
@container barra (max-width: 720px) {
  /* El interruptor se apaga en `.barra-derecha` y no en `.barra`: una consulta
     de contenedor solo alcanza a los DESCENDIENTES del contenedor, nunca al
     contenedor mismo. Los cuatro rótulos viven ahí dentro, así que da igual, y
     lo demás lo hereda. */
  .barra-derecha { --rotulos-barra: none; }
  .barra-buscar {
    min-width: 44px;
    width: 44px;
    justify-content: center;
    padding: var(--e1);
  }
  /* El nombre de la caja se recorta antes que empujar la campana fuera. */
  .barra-punto select { max-width: 130px; }
}

.barra-boton {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--e2);
  min-height: 36px;
  min-width: 36px;
  padding: var(--e1) var(--e3);
  font: inherit;
  font-size: var(--t-md);
  font-weight: 600;
  color: var(--texto);
  text-decoration: none;
  background: none;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  cursor: pointer;
}
.barra-boton:hover { background: var(--apagado); border-color: var(--borde); color: var(--texto); }

/* Selector de punto de emisión de la barra (B29). Es un <form> de verdad para
   que funcione sin JavaScript; con JavaScript el <select> se envía solo. El
   ancho máximo evita que «Mostrador de repuestos · Cartago centro» empuje la
   campana fuera de la pantalla. */
.barra-punto { display: flex; align-items: center; gap: var(--e1); flex: none; }
.barra-punto select { max-width: 190px; min-height: 36px; }
@media (max-width: 900px) {
  /* En móvil la barra ya está llena: la caja se cambia desde Configuración. */
  .barra-punto { display: none; }
}
.barra-boton.solo-icono { padding: var(--e1); }

/* Contador de la campana. El número va escrito, no es un puntito de color: un
   punto solo dice «algo pasa» y obliga a abrir el panel para saber qué. */
.contador-avisos {
  position: absolute;
  top: -2px;
  right: -2px;
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--fuente-cuerpo);
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  color: #fff;
  background: var(--destructivo);
  border: 2px solid var(--superficie);
  border-radius: var(--r-pastilla);
}
.contador-avisos[hidden] { display: none; }

/* Distintivo de ambiente. En producción es un rótulo discreto; en una copia de
   pruebas se ve de lejos, porque el error caro es el contrario: creerse en la
   copia estando en el sistema de verdad. El color no va solo, el texto dice
   cuál es. */
.distintivo-ambiente {
  display: inline-flex;
  align-items: center;
  gap: var(--e1);
  padding: var(--e1) var(--e2);
  font-size: var(--t-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  white-space: nowrap;
  border-radius: var(--r-pastilla);
}
.distintivo-ambiente.produccion {
  color: var(--texto-suave);
  background: var(--apagado);
  border: 1px solid var(--borde);
}
.distintivo-ambiente.pruebas {
  color: var(--alerta);
  background: var(--alerta-suave);
  border: 1px solid var(--alerta-borde);
}

/* -- variación de una tarjeta de indicador -------------------------------
   Ver src/vistas/parciales/variacion.ejs: subir no se pinta de verde ni bajar
   de rojo. En un tablero de compras, que entren más facturas no es una buena
   noticia ni una mala; colorearlo sería el sistema opinando. */
.variacion {
  display: inline-flex;
  align-items: center;
  gap: var(--e1);
  margin-top: var(--e1);
  font-size: var(--t-md);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--texto-suave);
}
.variacion .icono { color: var(--texto-tenue); }
.variacion.sin-dato { font-weight: 400; font-size: var(--t-xs); color: var(--texto-tenue); }
.tarjeta-dato .enlace-mas { margin-top: var(--e1); }

/* -- resultados del buscador global -------------------------------------- */
.grupo-resultados { margin-bottom: var(--e4); }
.grupo-resultados:last-child { margin-bottom: 0; }
.grupo-resultados > .etiqueta {
  display: flex;
  align-items: center;
  gap: var(--e1);
  margin: 0 0 var(--e1);
  font-size: var(--t-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--texto-tenue);
}
/* El resultado y su botón de copiar. El botón NO va dentro del enlace (sería
   HTML inválido), así que la fila es quien los pone uno al lado del otro. */
.resultado-fila { display: flex; align-items: center; gap: var(--e1); }
.resultado-fila > .resultado { flex: 1 1 auto; min-width: 0; }
.resultado {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--e1) var(--e3);
  min-height: 44px;
  padding: var(--e2) var(--e3);
  color: var(--texto);
  text-decoration: none;
  border: 1px solid transparent;
  border-radius: var(--r-md);
}
.resultado:hover { background: var(--apagado); color: var(--texto); }
/* La fila marcada con el teclado lleva fondo Y anillo: el fondo solo no se ve
   en una pantalla mala ni en alto contraste. */
.resultado.activo {
  background: var(--acento-suave);
  border-color: var(--acento);
  color: var(--texto);
}
.resultado .titulo { font-weight: 600; }
/* L2 · lo que el buscador devuelve SIN tabla (pantallas y acciones de la
   página /buscar): un enlace de 44 px por renglón y su ruta en gris. */
.lista-resultados { margin: 0; padding: 0; list-style: none; }
.lista-resultados li { display: flex; flex-wrap: wrap; align-items: center; gap: 0 var(--e3); }
.lista-resultados a { display: inline-flex; align-items: center; min-height: 44px; font-weight: 600; }
.lista-resultados .sutil { font-size: var(--t-md); }
/* El dato de la derecha del resultado del modal: saldo o existencia. */
.resultado .extra { margin-left: auto; font-size: var(--t-md); color: var(--texto-suave); }
.resultado .detalle {
  font-family: var(--fuente-mono);
  font-size: var(--t-md);
  color: var(--texto-tenue);
}

/* -- panel de avisos ------------------------------------------------------ */
.aviso-lista {
  padding: var(--e3) var(--e4);
  margin-bottom: var(--e3);
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-left: 3px solid var(--texto-tenue);
  border-radius: var(--r-md);
}
.aviso-lista:last-child { margin-bottom: 0; }
.aviso-lista.alerta { border-left-color: var(--alerta); background: var(--alerta-suave); }
.aviso-lista.error { border-left-color: var(--destructivo); background: var(--destructivo-suave); }
.aviso-lista .titulo {
  display: flex;
  align-items: flex-start;
  gap: var(--e2);
  margin: 0 0 var(--e1);
  font-weight: 700;
  color: var(--texto);
}
.aviso-lista .detalle { margin: 0 0 var(--e2); font-size: var(--t-md); color: var(--texto-suave); }

/* -- selector de empresa -------------------------------------------------- */
.fila-empresa { display: block; margin-bottom: var(--e1); }
.opcion-empresa {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--e1) var(--e3);
  width: 100%;
  min-height: 44px;
  padding: var(--e2) var(--e3);
  font: inherit;
  font-size: var(--t-base);
  color: var(--texto);
  text-align: left;
  background: none;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  cursor: pointer;
}
.opcion-empresa:hover { background: var(--apagado); }
.opcion-empresa:disabled { cursor: default; background: var(--acento-suave); }
.opcion-empresa .nombre { flex: 1 1 auto; font-weight: 600; }
.opcion-empresa .detalle { font-family: var(--fuente-mono); font-size: var(--t-md); color: var(--texto-tenue); }

.boton-menu {
  display: none;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  color: var(--texto);
  background: none;
  border: 1px solid var(--boton-borde);
  border-radius: var(--r-md);
  cursor: pointer;
}
.boton-menu:hover { background: var(--apagado); }

/* Nombre de la empresa en la barra. En escritorio vive en el menú lateral; en
   móvil el menú está escondido, así que acá es el único lugar donde se ve. */
/* Base `0` y no `auto`: al envolverse, una barra flexible decide el corte de
   línea con el tamaño NATURAL de cada hijo, y «Distribuidora La Esperanza» mide
   314 px, así que en un teléfono se llevaba la primera línea entera y empujaba
   los controles a una segunda. Con base 0 el nombre ocupa lo que sobra y se
   recorta con puntos suspensivos —para eso está el `text-overflow` de abajo—,
   que es exactamente lo que se quería: el que cede es el texto.

   El `min-width` es el suelo de ese recorte. Con 0 el nombre llegaba a medir
   33 px en un teléfono, o sea «D…», y en móvil este es el ÚNICO sitio donde se
   ve en qué empresa se está trabajando: un nombre recortado hasta la primera
   letra no es un dato, es un adorno. Con el suelo puesto, cuando ya no cabe, la
   barra prefiere una segunda línea —el nombre completo arriba, los controles
   debajo— antes que dejarlo ilegible. */
.barra-empresa { display: none; flex: 1 1 0; min-width: 12ch; }
.barra-empresa strong {
  display: block;
  font-family: var(--fuente-titulo);
  font-size: var(--t-md);
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.barra-empresa span { font-size: var(--t-xs); color: var(--texto-tenue); }

/* -- barra lateral -------------------------------------------------------
   Oscura a propósito. Con todo blanco —menú, tarjetas, tablas y fondo— no había
   forma de saber dónde termina la navegación y dónde empieza el trabajo. El
   slate #334155 es el color primario de la paleta, así que la jerarquía se gana
   sin inventar un color nuevo.                                              */
.lateral {
  grid-area: lateral;
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 0 var(--e2) var(--e6);
  background: var(--primario);
  color: var(--lateral-texto);
  /* Se queda quieto mientras el contenido rueda: en una lista de 200 filas no
     hay que volver arriba para cambiar de sección. */
  position: sticky;
  top: 0;
  align-self: start;
  /* Mismo motivo que en `.armazon`: con `100vh`, en el teléfono la barra mide
     más que la ventana visible y «Mi cuenta» queda tapada por la barra de
     direcciones. `100vh` primero como respaldo. */
  height: 100vh;
  height: 100dvh;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.lateral :focus-visible { outline-color: #fff; }
.lateral:focus { outline: none; }

.lateral-marca {
  display: flex;
  align-items: center;
  gap: var(--e2);
  /* El alto de DISEÑO y no el medido. Con el medido, todo lo que hiciera
     crecer la barra (un rótulo largo, un control que baja de renglón, una
     regla ajena que se le cuela) agrandaba en la misma medida el bloque de la
     marca: arriba del menú quedaba un bloque oscuro vacío y los enlaces
     arrancaban a la altura de donde terminaba la barra (captura de Adán en un
     monitor ancho, 08-oct-2026). El menú arranca siempre arriba. */
  min-height: var(--alto-barra-base);
  margin: 0 calc(var(--e2) * -1) var(--e3);
  padding: 0 var(--e4);
  color: #fff;
  text-decoration: none;
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.lateral-marca .marca-texto {
  font-family: var(--fuente-titulo);
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}
.lateral-marca .marca-texto span { color: var(--acento-borde); }

/* Contexto: en qué empresa estoy y con qué rol. Es lo primero que se pregunta
   cualquiera que administra dos empresas, y antes había que deducirlo. */
.lateral-contexto {
  margin: 0 0 var(--e4);
  padding: var(--e3);
  background: var(--primario-hundido);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: var(--r-md);
}
.lateral-contexto .etiqueta {
  display: block;
  font-size: var(--t-xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--lateral-tenue);
}
.lateral-contexto .empresa {
  display: block;
  margin-top: var(--e1);
  font-family: var(--fuente-titulo);
  font-size: var(--t-base);
  font-weight: 600;
  line-height: 1.3;
  color: #fff;
}
.lateral-contexto .cedula {
  display: block;
  font-family: var(--fuente-mono);
  font-size: var(--t-xs);
  color: var(--lateral-tenue);
}
.chip-rol {
  display: inline-flex;
  align-items: center;
  gap: var(--e1);
  margin-top: var(--e2);
  padding: 2px var(--e2);
  font-size: var(--t-xs);
  font-weight: 600;
  border-radius: var(--r-pastilla);
  background: rgba(255, 255, 255, 0.12);
  border: 1px solid rgba(255, 255, 255, 0.18);
  color: #fff;
}
.chip-rol.kiwi { background: var(--acento-fuerte); border-color: var(--acento); }
.lateral-contexto .usuario {
  display: block;
  margin-top: var(--e2);
  padding-top: var(--e2);
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  font-size: var(--t-md);
  color: var(--lateral-tenue);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.lateral a {
  display: flex;
  align-items: center;
  gap: var(--e3);
  min-height: 40px;
  padding: var(--e2) var(--e3);
  border-left: 3px solid transparent;
  border-radius: 0 var(--r-md) var(--r-md) 0;
  color: var(--lateral-texto);
  text-decoration: none;
  font-size: var(--t-md);
  transition: background-color var(--mov-rapido) var(--curva);
}
.lateral a:hover { background: var(--lateral-hover); color: #fff; }
.lateral a.activo {
  background: var(--lateral-activo);
  border-left-color: var(--acento);
  color: #fff;
  font-weight: 600;
}
/* El estado activo no se apoya solo en el color: lleva además la barra de la
   izquierda, más peso de letra y aria-current para el lector de pantalla. */
.lateral a.activo .icono { color: var(--acento-borde); }

/* -- menú agrupado y plegable --------------------------------------------
   El rótulo de grupo es a la vez el <summary> que pliega: un botón aparte al
   lado del texto sería un objetivo de 20 px cuando la fila entera ya sirve. */
.lateral-grupo {
  display: flex;
  align-items: center;
  gap: var(--e2);
  min-height: 44px;
  margin: var(--e3) 0 var(--e1);
  padding: var(--e1) var(--e3) 0;
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  font-size: var(--t-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--lateral-tenue);
}
summary.lateral-grupo {
  cursor: pointer;
  border-radius: var(--r-md);
  /* El triángulo del navegador no se puede estilar igual en todos lados: se
     apaga y se pinta una flecha del sprite, que gira al abrir. */
  list-style: none;
  transition: background-color var(--mov-rapido) var(--curva);
}
summary.lateral-grupo::-webkit-details-marker { display: none; }
summary.lateral-grupo:hover { background: var(--lateral-hover); color: #fff; }
summary.lateral-grupo .texto { flex: 1 1 auto; }
summary.lateral-grupo .flecha {
  flex: 0 0 auto;
  transition: transform var(--mov-rapido) var(--curva);
}
details[open] > summary.lateral-grupo .flecha { transform: rotate(180deg); }

.lateral-lista { margin: 0; padding: 0; list-style: none; }
/* L2 · el rótulo de SECCIÓN dentro de un grupo abierto («Cobrar», «Fiscal»).
   Más chico y más tenue que el del grupo, sin borde ni flecha: no pliega
   nada, solo ordena la columna. No es objetivo táctil (no se toca), por eso
   no lleva los 44 px. */
.lateral-rotulo {
  margin: var(--e2) 0 0;
  padding: 0 var(--e3) 0 calc(var(--e3) + 3px);
  font-size: var(--t-xs);
  font-weight: 500;
  letter-spacing: 0.03em;
  color: var(--lateral-tenue);
}
.lateral-subseccion:first-of-type .lateral-rotulo { margin-top: 0; }
/* Configuración y Administración: un solo enlace, separado de los grupos de
   arriba igual que un rótulo de grupo. */
.lateral-compacta { margin-top: var(--e2); border-top: 1px solid rgba(255, 255, 255, 0.1); padding-top: var(--e1); }
.lateral-compacta + .lateral-compacta { margin-top: 0; border-top: 0; padding-top: 0; }
.lateral.contraida .lateral-rotulo { display: none; }
.lateral-fila { display: flex; align-items: stretch; }
.lateral-fila .lateral-enlace { flex: 1 1 auto; min-width: 0; min-height: 44px; }
/* 14-sep-2026 · EL RÓTULO SE PARTE EN DOS, NO SE CORTA. Con puntos suspensivos
   el menú decía «Evaluación de p…» y «Conciliación co…»: había que pasar el
   mouse por encima para saber a dónde llevaba un enlace, que es exactamente lo
   que un menú no puede pedir. Con la barra de 280 px entran de una línea 68 de
   los 74 títulos y los seis largos bajan a una segunda.
   El tope de dos líneas no es para esos seis —caben— sino para los RECIENTES,
   cuyo rótulo es el título de un documento y puede medir lo que sea; esos sí
   llevan `title`, así que recortar el tercer renglón no esconde nada. */
.lateral-fila .lateral-enlace .texto {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  /* Una «palabra» larguísima (un consecutivo, una clave) no puede ensanchar la
     columna de la rejilla y desplazar la página entera de lado. */
  overflow-wrap: anywhere;
}

/* La estrella de favoritos. 44×44 de objetivo táctil, igual que el enlace, y
   marcada se RELLENA: la diferencia no se apoya solo en el color. */
.lateral-estrella {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  border: 0;
  background: none;
  color: var(--lateral-tenue);
  cursor: pointer;
  opacity: 0;
  transition: opacity var(--mov-rapido) var(--curva), color var(--mov-rapido) var(--curva);
}
/* Se revela al pasar por la fila o al llegar con el teclado; la ya marcada se
   ve siempre, que para eso está marcada. */
.lateral-fila:hover .lateral-estrella,
.lateral-fila:focus-within .lateral-estrella,
.lateral-estrella.marcada { opacity: 1; }
.lateral-estrella:hover { color: #fff; }
.lateral-estrella.marcada { color: var(--acento-borde); }

.lateral-herramientas {
  display: flex;
  align-items: center;
  gap: var(--e2);
  margin-bottom: var(--e3);
}
.lateral-herramientas .en-linea { flex: 0 0 auto; }
.lateral-filtro {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 40px;
  padding: var(--e2) var(--e3);
  font-size: var(--t-md);
  color: #fff;
  background: rgba(0, 0, 0, 0.2);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: var(--r-md);
}
.lateral-filtro::placeholder { color: var(--lateral-tenue); }
.lateral-contraer {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  color: var(--lateral-tenue);
  background: none;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  cursor: pointer;
}
.lateral-contraer:hover { color: #fff; background: var(--lateral-hover); }
.lateral-sin-resultados {
  margin: 0 0 var(--e3);
  padding: 0 var(--e3);
  font-size: var(--t-md);
  color: var(--lateral-tenue);
}

/* -- barra contraída a iconos --------------------------------------------
   Solo cambian el ancho y qué se ve: el rótulo sigue en el marcado, así que el
   lector de pantalla lo lee igual y el `title` del navegador lo enseña. */
.armazon.menu-contraido {
  grid-template-columns: var(--lateral-contraida) 1fr;
  background: linear-gradient(
    90deg,
    var(--primario) 0,
    var(--primario) var(--lateral-contraida),
    var(--fondo) var(--lateral-contraida)
  );
}
.lateral.contraida { padding-left: var(--e1); padding-right: var(--e1); }
.lateral.contraida .marca-texto,
.lateral.contraida .lateral-contexto,
.lateral.contraida .lateral-filtro,
.lateral.contraida .lateral-estrella,
.lateral.contraida .lateral-grupo .texto,
.lateral.contraida .lateral-grupo .flecha,
/* Recientes del menú: DOS renglones con papeles distintos en vez de un título
   partido donde caiga. Arriba la contraparte (lo que se recuerda), abajo el
   número corto y hace cuánto, más chico y tenue. Cada uno en una línea con
   puntos suspensivos: el título entero va en `title`. */
.lateral-reciente .lateral-enlace { align-items: center; }
.lateral-fila.lateral-reciente .lateral-enlace .texto {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}
.lateral-reciente .reciente-nombre,
.lateral-reciente .reciente-detalle {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.lateral-reciente .reciente-detalle {
  font-size: var(--t-xs);
  font-weight: 400;
  color: var(--lateral-tenue);
  font-variant-numeric: tabular-nums;
}
.lateral.contraida .lateral-enlace .texto { display: none; }
.lateral.contraida .lateral-reciente .lateral-enlace .texto { display: none; }
.lateral.contraida .lateral-enlace { justify-content: center; padding-left: 0; padding-right: 0; }
.lateral.contraida .lateral-grupo { justify-content: center; padding: 0; }
.lateral.contraida .lateral-herramientas { justify-content: center; }
.lateral.contraida .lateral-marca { justify-content: center; padding: 0; }

/* -- contenido y pie ----------------------------------------------------- */
.contenido {
  grid-area: contenido;
  width: 100%;
  max-width: var(--ancho-contenido);
  padding: var(--e5) var(--e5) var(--e8);
  /* Sin esto una tabla ancha ensancha la columna de la rejilla y la PÁGINA
     entera se desplaza de lado. Con min-width:0 el desborde se queda adentro
     de .tabla-envoltorio, que es donde tiene que estar. */
  min-width: 0;
}

/* En el panel es la última fila de la rejilla (ver `.armazon`); en las pantallas
   sueltas —entrar, error, comprobante público— no hay rejilla y `grid-area` no
   hace nada, así que la misma regla sirve para las dos. */
.pie {
  grid-area: pie;
  padding: var(--e4) var(--e5);
  color: var(--texto-tenue);
  font-size: var(--t-md);
  text-align: center;
}
/* En una pantalla de más de 1688 px el contenido se corta en `--ancho-contenido`
   y se queda pegado a la izquierda: sin este mismo tope, el pie se centraría
   respecto de la columna entera y quedaría corrido a la derecha del contenido. */
.armazon > .pie {
  width: 100%;
  max-width: var(--ancho-contenido);
}
.armazon.vista-lista > .contenido,
.armazon.vista-lista > .pie { max-width: var(--ancho-lista); }

.velo { display: none; }

.banda-impersonacion {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--e2);
  flex-wrap: wrap;
  padding: var(--e2) var(--e4);
  background: var(--alerta-suave);
  border-bottom: 1px solid var(--alerta-borde);
  color: var(--alerta);
  font-size: var(--t-md);
  text-align: center;
}

/* -- franja del CARRIL DE PRUEBAS (migración 830) -------------------------
   Misma forma que la banda de impersonación y por la misma razón: es un aviso
   de CONTEXTO, no de una acción. Quien está viendo el carril de pruebas tiene
   que saberlo en TODAS las pantallas y no solo en la que abrió el conmutador,
   porque el resto del sistema se ve exactamente igual: las mismas listas, los
   mismos totales, los mismos botones. Un rótulo que solo estuviera en la barra
   se pierde en cuanto alguien baja la página.

   Se pinta más fuerte que la de impersonación —fondo lleno y no suave— porque
   el error caro acá es el contrario del de aquella: creerse en producción
   estando en pruebas hace que alguien dé por hecho un trabajo que no existe. */
.banda-carril-pruebas {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--e2);
  flex-wrap: wrap;
  padding: var(--e2) var(--e4);
  background: var(--alerta);
  border-bottom: 1px solid var(--alerta-borde);
  color: var(--blanco, #fff);
  font-size: var(--t-md);
  font-weight: 600;
  text-align: center;
}
.banda-carril-pruebas .boton-texto {
  color: inherit;
  text-decoration: underline;
}

/* El conmutador «Ver: Producción | Pruebas» de la barra. Dos botones de un
   formulario POST y no un <select>: sin JavaScript un desplegable no cambia
   nada hasta que alguien lo envíe, y acá el clic TIENE que cambiar lo que se
   ve. El que está activo se marca con `aria-current` y no solo con color. */
.conmutador-carril {
  display: inline-flex;
  align-items: center;
  gap: 0;
  border: 1px solid var(--borde);
  border-radius: var(--r-pastilla);
  overflow: hidden;
}
.conmutador-carril .rotulo {
  padding: var(--e1) var(--e2);
  font-size: var(--t-xs);
  color: var(--texto-tenue);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  border-right: 1px solid var(--borde);
}
.conmutador-carril button {
  padding: var(--e1) var(--e2);
  font-size: var(--t-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  white-space: nowrap;
  border: 0;
  background: transparent;
  color: var(--texto-suave);
  cursor: pointer;
}
.conmutador-carril button:hover { background: var(--apagado); }
.conmutador-carril button[aria-current='true'] {
  background: var(--apagado);
  color: var(--texto);
}
.conmutador-carril button[aria-current='true'].pruebas {
  background: var(--alerta);
  color: var(--blanco, #fff);
}

/* ===========================================================================
   6 · MIGAS Y ENCABEZADOS DE PÁGINA
   ======================================================================== */
.migas {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--e1);
  margin: 0 0 var(--e2);
  padding: 0;
  list-style: none;
  font-size: var(--t-md);
  color: var(--texto-tenue);
}
.migas li { display: inline-flex; align-items: center; gap: var(--e1); }
.migas a { color: var(--texto-suave); text-decoration: none; }
.migas a:hover { color: var(--acento-fuerte); text-decoration: underline; }
.migas .separador { color: var(--borde-fuerte); }
.migas [aria-current="page"] { color: var(--texto); font-weight: 600; }

.encabezado-pagina { margin-bottom: var(--e5); }
.encabezado-pagina .descripcion {
  max-width: 80ch;
  margin: 0;
  color: var(--texto-suave);
  font-size: var(--t-md);
}

.encabezado-accion {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--e4);
  flex-wrap: wrap;
  margin-bottom: var(--e3);
}
.encabezado-accion .acciones { display: flex; gap: var(--e2); flex-wrap: wrap; }
/* 08-oct-2026 · «Excel formato Factura Profesional» con su casilla del cuadre,
   en el mismo renglón que el otro Excel; 44 px de toque (§5). */
.excel-fp { display: inline-flex; align-items: center; gap: var(--e2); flex-wrap: wrap; margin: 0; }
.excel-fp label.casilla { margin: 0; min-height: 44px; }
/* 1740 · Tarjetas: las fechas de las remisiones (2026-08-03) no se parten. */
.sin-partir { white-space: nowrap; }
/* Título con descripción Y acciones al lado: el encabezado deja de poner su
   propio margen abajo, porque el que manda es el de `.encabezado-accion`. */
.encabezado-accion .encabezado-pagina { flex: 1 1 32ch; min-width: 0; margin-bottom: 0; }

/* ===========================================================================
   7 · BLOQUES Y TARJETAS
   ======================================================================== */
.bloque {
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--r-lg);
  padding: var(--e4);
  margin-bottom: var(--e5);
  box-shadow: var(--elev-1);
}
.bloque > h2:first-child,
.bloque > .encabezado-accion:first-child { margin-top: 0; }
/* Un formulario largo se lee por secciones. Sin aire arriba del título, el
   <h2> de la sección siguiente queda pegado a la ayuda del último campo de la
   anterior y las dos parecen la misma cosa. */
.bloque h2,
.bloque form > h2 { margin-top: var(--e6); }
.bloque h3 { margin-top: var(--e5); }
.bloque > h2:first-child,
.bloque form > h2:first-child,
.bloque h2.sin-margen-arriba,
.bloque h3.sin-margen-arriba { margin-top: 0; }
.bloque.estrecho { max-width: 560px; }

.panel-doble {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--e5);
  align-items: start;
}
/* Para los tableros: la lista de la izquierda tiene columnas y la de la derecha
   son tres cifras. Partir la pantalla por la mitad dejaba media pantalla vacía. */
.panel-doble.principal-izquierda { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); }

.tarjetas {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--e3);
  margin-bottom: var(--e5);
}

/* -- Configuración en tarjetas (L2) --------------------------------------
   Una tarjeta por área y, adentro, cada pantalla en su renglón con la frase
   que dice para qué sirve. `minmax(280px, 1fr)` da tres columnas en 1440 px y
   una sola en el teléfono sin media consulta aparte. */
.tarjetas-config {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr));
  gap: var(--e4);
  align-items: start;
}
.tarjeta-config {
  padding: var(--e4);
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--r-md);
  min-width: 0;
}
.tarjeta-config h2 {
  display: flex;
  align-items: center;
  gap: var(--e2);
  margin: 0 0 var(--e3);
  font-size: var(--t-lg);
}
.tarjeta-config ul { margin: 0; padding: 0; list-style: none; }
.tarjeta-config li { padding: var(--e2) 0; border-top: 1px solid var(--borde); }
.tarjeta-config li:first-child { border-top: 0; padding-top: 0; }
.tarjeta-config li a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-weight: 600;
}
.tarjeta-config li .sutil { display: block; font-size: var(--t-md); overflow-wrap: anywhere; }
.tarjeta-config.kiwi { border-style: dashed; }

/* Primer título dentro de un bloque, cuando le sigue algo que ya trae su propio
   aire arriba. (`.acciones-encabezado` que venía de main se unificó con
   `.encabezado-accion .acciones`: dos nombres para lo mismo era una trampa.) */
.sin-margen-arriba { margin-top: 0; }

.tarjeta-dato {
  position: relative;
  padding: var(--e3) var(--e4);
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--r-lg);
  box-shadow: var(--elev-1);
  overflow: hidden;
}
/* Barra de acento arriba: le da peso a la fila de indicadores sin agregar un
   solo píxel de altura ni robarle espacio al dato. */
.tarjeta-dato::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  background: var(--borde-fuerte);
}
.tarjeta-dato .etiqueta {
  display: block;
  color: var(--texto-tenue);
  font-size: var(--t-md);
}
.tarjeta-dato .numero {
  display: block;
  margin-top: var(--e1);
  font-family: var(--fuente-titulo);
  font-size: var(--t-2xl);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.02em;
}
.tarjeta-dato .numero .sutil { font-size: var(--t-base); font-weight: 400; }
.tarjeta-dato.alerta { border-color: var(--destructivo-borde); background: var(--destructivo-suave); }
.tarjeta-dato.alerta::before { background: var(--destructivo); }
/* Sobre el rosado de la tarjeta en alerta, --texto-tenue cae a 4,35:1 y se queda
   corto: el rótulo sube un tono. */
.tarjeta-dato.alerta .etiqueta { color: var(--texto-suave); }
.tarjeta-dato.alerta .numero { color: var(--destructivo-hundido); }
/* 1.39.0 · una tarjeta que filtra (la de «Vencido» del estado de cuenta): se
   ve como tarjeta, se señala como enlace. */
a.tarjeta-dato.tarjeta-enlace { color: inherit; text-decoration: none; }
a.tarjeta-dato.tarjeta-enlace:hover { border-color: var(--acento); }
a.tarjeta-dato.tarjeta-enlace .etiqueta:last-child { text-decoration: underline; }
/* El rojo no va solo: la tarjeta en alerta también lleva su icono. */
.tarjeta-dato.alerta .etiqueta::before {
  content: "";
  display: inline-block;
  width: 8px;
  height: 8px;
  margin-right: var(--e1);
  border-radius: 50%;
  background: var(--destructivo);
}

.definiciones {
  display: grid;
  grid-template-columns: minmax(140px, auto) 1fr;
  gap: var(--e2) var(--e4);
  margin: 0;
  align-items: baseline;
}
.definiciones dt { color: var(--texto-tenue); font-size: var(--t-md); }
/* `min-width: 0` no es adorno: sin él la columna `1fr` se ensancha hasta el
   valor más largo (un correo, la clave de 50 dígitos) y arrastra la página
   entera fuera de la pantalla en 375 px. */
.definiciones dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }

/* ===========================================================================
   8 · TABLAS
   ---------------------------------------------------------------------------
   Encabezado fijo: el envoltorio es su PROPIO contenedor de scroll y el `th` se
   pega a `top: 0` dentro de él.
   Se intentó primero pegarlo a la ventana (`top: var(--alto-barra)`) y no se
   puede: una tabla ancha necesita `overflow-x: auto`, y en cuanto un eje es
   `auto` el otro deja de poder ser `visible`. `overflow-y: clip` tampoco sirve
   —el navegador lo computa como `hidden` cuando el otro eje es `auto`—, y con
   `hidden` el envoltorio ya es un contenedor de scroll: el encabezado se pegaba
   56 px por debajo del borde de la tarjeta, encima de sus propias filas.
   Con `max-height` la tabla corta solo cuando es larga de verdad; la de diez
   filas no muestra ninguna barra y se comporta como siempre.
   ======================================================================== */
/* Una tabla suelta en la página también es una superficie: sin fondo propio la
   lista quedaba flotando sobre el papel y no se leía como un bloque. */
.tabla-envoltorio {
  overflow: auto;
  /* `overflow: auto` recorta lo que se PINTA, pero el desbordamiento de layout
     de una tabla ancha se le sigue sumando al alto/ancho desplazable de la
     página: en 1024 px el editor de líneas dejaba la página entera corrida 31 px
     de lado, con la barra superior asomando. `contain: paint` cierra esa fuga
     sin cambiar nada de lo que se ve; lo único absoluto que vive adentro es
     `.solo-lectores`, que ya es invisible de todas formas. */
  contain: paint;
  max-height: calc(100vh - 180px);
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--r-lg);
  box-shadow: var(--elev-1);
}
.tabla {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--t-tabla);
}
.tabla th,
.tabla td {
  padding: var(--e2) var(--e3);
  text-align: left;
  border-bottom: 1px solid var(--borde);
  vertical-align: top;
}
/* 1.39.0 · `.der` en una celda alinea a la derecha de verdad: la regla de
   arriba es más específica que `.der` a secas y dejaba los montos a la
   izquierda, donde las cifras no se pueden comparar de un vistazo. */
.tabla th.der,
.tabla td.der { text-align: right; }
.tabla thead th {
  position: sticky;
  top: 0;
  z-index: 5;
  background: var(--apagado);
  border-bottom: 1px solid var(--borde-fuerte);
  font-family: var(--fuente-titulo);
  font-size: var(--t-sm);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--texto-suave);
  white-space: nowrap;
}
.tabla tbody tr { transition: background-color var(--mov-rapido) var(--curva); }
.tabla tbody tr:hover { background: var(--acento-suave); }
.tabla tbody tr:last-child td { border-bottom: none; }
.tabla.compacta { font-size: var(--t-md); }
.tabla.compacta th,
.tabla.compacta td { padding: var(--e1) var(--e2); }

/* Encabezado de FILA (la primera celda de un cuadro de totales, por ejemplo).
   No es el encabezado de la tabla y no se pega a nada. */
.tabla tbody th {
  font-size: var(--t-md);
  font-weight: 600;
  color: var(--texto-suave);
  white-space: nowrap;
}
/* El total es el dato que se viene a buscar: se separa del resto con una línea
   más marcada y un fondo propio, no solo con negrita. */
.tabla.totales .total-final th,
.tabla.totales .total-final td {
  border-top: 2px solid var(--borde-fuerte);
  background: var(--apagado);
}
.tabla.totales tbody tr:hover { background: transparent; }
.tabla.totales .total-final:hover th,
.tabla.totales .total-final:hover td { background: var(--apagado); }

/* Dentro de una tarjeta la tabla es más baja y no lleva sombra propia: la
   tarjeta ya acota y ya está elevada. */
.bloque .tabla-envoltorio {
  max-height: calc(100vh - 260px);
  border-radius: var(--r-md);
  box-shadow: none;
}

/* Acciones de una fila: en línea y sin envolverse a mitad de un botón. */
.acciones-fila { display: flex; flex-wrap: wrap; gap: var(--e1); align-items: flex-start; }

/* Barra de acciones en lote de las facturas recibidas (B5). Va pegada a la
   tabla de la selección y por eso comparte su borde superior: la barra y la
   tabla son una sola cosa, no dos bloques que se parecen. */
.barra-lote {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e2);
  align-items: center;
  padding: var(--e2);
  border: 1px solid var(--borde);
  border-bottom: 0;
  border-radius: var(--r-md) var(--r-md) 0 0;
  background: var(--apagado);
}
.barra-lote > p { margin: 0; }
.barra-lote > .ayuda { flex: 1 1 100%; }
/* Una casilla con su texto al lado, sin que el texto se despegue al envolver. */
.casilla-en-linea {
  display: inline-flex;
  align-items: center;
  gap: var(--e1);
  font-weight: 500;
  margin: 0;
}
/* La columna de las casillas: lo más angosta que se pueda sin apretar el clic. */
.tabla th.angosta { width: 2.5rem; }

.der { text-align: right; }
.fuerte { font-weight: 700; }
.quiebre { word-break: break-all; }
.d-bloque { display: block; }

/* ===========================================================================
   9 · DISTINTIVOS DE ESTADO
   ---------------------------------------------------------------------------
   El color nunca es el único indicador: cada chip lleva su texto, y los de
   alerta y error llevan además un punto al frente para quien no distingue el
   rojo del ámbar.
   ======================================================================== */
.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--e1);
  padding: 2px var(--e2);
  border-radius: var(--r-pastilla);
  font-size: var(--t-sm);
  font-weight: 600;
  line-height: 1.5;
  white-space: nowrap;
  background: var(--apagado);
  border: 1px solid var(--borde);
  color: var(--texto-suave);
}
.chip::before {
  content: "";
  width: 6px;
  height: 6px;
  flex: none;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.7;
}
.chip.verde,
.chip.estado-aceptado {
  background: var(--acento-suave);
  border-color: var(--acento-borde);
  color: var(--acento-fuerte);
}
.chip.rojo,
.chip.estado-rechazado {
  background: var(--destructivo-suave);
  border-color: var(--destructivo-borde);
  color: var(--destructivo-hundido);
}
.chip.ambar,
.chip.estado-aceptado_parcial {
  background: var(--alerta-suave);
  border-color: var(--alerta-borde);
  color: var(--alerta);
}
.chip.azul { background: var(--info-suave); border-color: var(--info-borde); color: var(--info); }
/* Migración 450. «No aplica» (mensaje receptor de una factura registrada a
   mano) y la etiqueta «a mano» del listado: son informativos, no un estado que
   alguien deba atender, así que se quedan en el gris del chip base y sin el
   punto de color que arrastra la mirada. */
.chip.sutil,
.chip.estado-no_aplica { font-weight: 500; }
.chip.sutil::before,
.chip.estado-no_aplica::before { display: none; }
/* `display` de la clase le gana al `[hidden]` del navegador, así que hay que
   decirlo (mismo caso que .combo-lista y .boton-quitar). */
.chip[hidden] { display: none; }
.texto-rojo { color: var(--destructivo-hundido); font-weight: 600; }
/* 1.39.0 · Cuentas por cobrar: el selector de vista y la celda compacta del cliente. */
.vista-cartera { display: flex; align-items: center; gap: var(--e2); flex-wrap: wrap; margin: var(--e3) 0; }
.celda-cliente { white-space: nowrap; }
.cartera-por-cliente td:first-child { min-width: 180px; }
.texto-chico { font-size: var(--t-sm, 0.85em); }
.cartera-por-cliente td.der, .cartera-por-cliente th.der { white-space: nowrap; }
.celda-cliente .icono-enlace { display: inline-flex; align-items: center; justify-content: center; min-width: 32px; min-height: 32px; vertical-align: middle; color: var(--texto-tenue); }
.celda-cliente .icono-enlace:hover { color: var(--acento-fuerte); }

/* ===========================================================================
   10 · FORMULARIOS
   ======================================================================== */
label {
  display: block;
  margin: var(--e3) 0 var(--e1);
  font-size: var(--t-md);
  font-weight: 600;
  color: var(--texto-suave);
}
label.casilla {
  display: inline-flex;
  align-items: center;
  gap: var(--e2);
  min-height: 32px;
  margin: var(--e3) var(--e2) var(--e1) 0;
  font-weight: 400;
}
label.casilla input { width: 18px; height: 18px; margin: 0; accent-color: var(--acento-fuerte); }

input[type="text"], input[type="email"], input[type="password"], input[type="search"],
input[type="date"], input[type="number"], input[type="tel"], select, textarea {
  width: 100%;
  padding: var(--e2) var(--e3);
  font: inherit;
  font-size: var(--t-base);
  color: var(--texto);
  background: var(--superficie);
  border: 1px solid var(--borde-fuerte);
  border-radius: var(--r-md);
  transition: border-color var(--mov-rapido) var(--curva),
    box-shadow var(--mov-rapido) var(--curva);
}
input:hover:not(:disabled):not([readonly]),
select:hover:not(:disabled),
textarea:hover:not(:disabled) { border-color: var(--secundario); }
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--acento);
  box-shadow: 0 0 0 3px var(--acento-borde);
}
input:focus-visible, select:focus-visible, textarea:focus-visible { outline: none; }

input:disabled, select:disabled, textarea:disabled {
  background: var(--apagado);
  border-color: var(--borde);
  color: var(--texto-tenue);
  cursor: not-allowed;
}
input[readonly] { background: var(--apagado); color: var(--texto-suave); }
textarea { resize: vertical; min-height: 88px; }

/* ---------------------------------------------------------------- campo de fecha
   `src/vistas/parciales/campo-fecha.ejs`. Es un campo de texto normal con sitio
   a la derecha para el botón del calendario; el `<input type="date">` que hay
   detrás NO se ve nunca: se usa solo para abrir el selector nativo del
   navegador, que es el único calendario que funciona igual en teléfono y en
   escritorio sin traer una librería. */
.campo-fecha { position: relative; display: block; }
.campo-fecha input[type="text"] { padding-right: 40px; }

.campo-fecha-boton {
  position: absolute;
  top: 1px;
  right: 1px;
  bottom: 1px;
  width: 36px;
  /* Sin JavaScript el botón no hace nada: no se pinta. El campo de texto sigue
     funcionando solo, y eso es lo que tiene que quedar a la vista. */
  display: none;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 0 var(--r-md) var(--r-md) 0;
  background: transparent;
  color: var(--texto-suave);
  cursor: pointer;
}
[data-campo-fecha-js] .campo-fecha-boton { display: flex; }
.campo-fecha-boton:hover { color: var(--acento-fuerte); background: var(--apagado); }
.campo-fecha input[type="text"]:disabled ~ .campo-fecha-boton,
.campo-fecha input[type="text"][readonly] ~ .campo-fecha-boton {
  color: var(--texto-tenue);
  cursor: not-allowed;
}

/* ---------------------------------------------------------- ver la contraseña
   `public/js/ver-contrasena.js` envuelve cada `input[type="password"]` y le pone
   el botón del ojo a la derecha, dentro del campo, como el calendario de
   arriba. El envoltorio solo existe con JavaScript: sin él el campo queda como
   siempre. El botón mide lo mismo que el campo (44 px en el teléfono) para que
   se acierte con el pulgar. */
.campo-contrasena { position: relative; display: block; }
.campo-contrasena > input { padding-right: 44px; }
.campo-contrasena-boton {
  position: absolute;
  top: 1px;
  right: 1px;
  bottom: 1px;
  width: 42px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 0 var(--r-md) var(--r-md) 0;
  background: transparent;
  color: var(--texto-suave);
  cursor: pointer;
}
.campo-contrasena-boton:hover { color: var(--acento-fuerte); background: var(--apagado); }
.campo-contrasena-boton[aria-pressed="true"] { color: var(--acento-fuerte); }
.campo-contrasena-boton:focus-visible { outline-offset: -3px; }

/* Invisible pero RENDERIZADO: `showPicker()` no abre el calendario de un
   control con `display: none`. */
.campo-fecha-selector {
  position: absolute;
  right: 4px;
  bottom: 0;
  width: 1px;
  height: 1px;
  min-height: 0;
  padding: 0;
  border: 0;
  opacity: 0;
  pointer-events: none;
}

/* Estado de error del campo. Además del borde rojo va el texto de .error-campo:
   el color por sí solo no le dice nada a quien no lo ve. */
input[aria-invalid="true"], select[aria-invalid="true"], textarea[aria-invalid="true"] {
  border-color: var(--destructivo);
}
input[aria-invalid="true"]:focus, select[aria-invalid="true"]:focus,
textarea[aria-invalid="true"]:focus { box-shadow: 0 0 0 3px var(--destructivo-borde); }
.error-campo {
  display: flex;
  align-items: center;
  gap: var(--e1);
  margin: var(--e1) 0 0;
  color: var(--destructivo-hundido);
  font-size: var(--t-md);
  font-weight: 600;
}

.rejilla {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 0 var(--e4);
}
.rejilla .ancho-total { grid-column: 1 / -1; }
/* 09-oct-2026 · «Identificación» de la ficha del artículo: el SKU angosto y el
   nombre con el resto de la fila (con `auto-fit` los dos quedaban de ~200 px y
   un nombre largo se leía cortado). En el teléfono, cada uno en su renglón. */
.rejilla.rejilla-identificacion { grid-template-columns: minmax(10rem, 14rem) minmax(0, 1fr); }
@media (max-width: 760px) {
  .rejilla.rejilla-identificacion { grid-template-columns: minmax(0, 1fr); }
}
/* B46 · las casillas que dependen de otra casilla se sangran, para que se lea
   que son SUYAS y no tres decisiones sueltas del mismo bloque. La línea de la
   izquierda hace el mismo trabajo que la sangría y sobrevive al modo oscuro. */
.rejilla .sangria {
  padding-left: var(--e3);
  border-left: 2px solid var(--borde);
}
/* Campo corto (confirmaciones, códigos). En móvil ocupa todo el ancho igual. */
.campo-corto { max-width: 240px; }
/* Campos cuyo contenido es un CÓDIGO en mayúsculas: la moneda (USD, CRC) y el
   país en ISO de dos letras. Existe como clase y no como `style=` en la vista
   porque la CSP de la aplicación no permite estilos en línea: los descartaría
   sin avisar y el campo se vería en minúsculas mientras el servidor guarda
   mayúsculas, que es la clase de discrepancia que nadie reporta.
   Es solo PRESENTACIÓN: el valor se normaliza igual en el servidor. */
.campo-mayusculas { text-transform: uppercase; }

.ayuda { margin: var(--e1) 0 0; font-size: var(--t-md); color: var(--texto-tenue); }
/* Ayuda que dejó de ser informativa: el tipo de cambio que se rellenó solo es
   de otro día, o no se pudo obtener (B20.1). En ámbar y con más peso porque un
   tipo de cambio equivocado no se nota hasta que ya está en el XML. */
.ayuda.alerta { color: var(--alerta); font-weight: 600; }
.sutil { color: var(--texto-tenue); font-size: var(--t-md); }
.vacio { color: var(--texto-tenue); padding: var(--e4) 0; }

/* Estado vacío con salida. Se conserva el texto que ya explicaba qué hacer:
   lo que cambia es que ahora se ve como un estado y no como un párrafo perdido. */
.estado-vacio {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--e2);
  padding: var(--e6) var(--e4);
  text-align: center;
  color: var(--texto-suave);
  background: var(--superficie);
  border: 1px dashed var(--borde-fuerte);
  border-radius: var(--r-lg);
}
.estado-vacio .icono { width: 32px; height: 32px; color: var(--borde-fuerte); }
.estado-vacio p { max-width: 52ch; margin: 0; }
/* Varias salidas en el mismo estado vacío (p. ej. las entradas que hacen nacer
   un lote): en fila y centradas, y en el teléfono bajan solas. */
.estado-vacio-acciones { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--e2); }
/* Los pasos de «¿Cómo nacen los lotes?» dentro del plegable. */
.lotes-como-nacen { margin: 0 0 var(--e2); padding-left: 1.4em; }
.lotes-como-nacen li { margin-bottom: var(--e1); }

/* ===========================================================================
   11 · BOTONES
   ---------------------------------------------------------------------------
   Tres pesos y nada más: principal (uno por pantalla), secundario y
   destructivo. Si hacen falta tres botones del mismo peso, el problema es la
   pantalla, no el botón.
   ======================================================================== */
.boton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--e2);
  min-height: 38px;
  padding: var(--e2) var(--e3);
  font-family: var(--fuente-cuerpo);
  font-size: var(--t-md);
  font-weight: 600;
  line-height: 1.3;
  color: var(--texto);
  background: var(--superficie);
  /* Ver `--boton-borde`: con `--borde-fuerte` el botón secundario se leía como
     deshabilitado y la gente no sabía que se podía apretar. */
  border: 1px solid var(--boton-borde);
  border-radius: var(--r-md);
  cursor: pointer;
  text-decoration: none;
  text-align: center;
  white-space: nowrap;
  transition: background-color var(--mov-rapido) var(--curva),
    border-color var(--mov-rapido) var(--curva), color var(--mov-rapido) var(--curva);
}
.boton:hover {
  background: var(--apagado);
  color: var(--texto);
  border-color: var(--boton-borde-hover);
}
.boton:active { background: var(--borde); }

.boton.principal {
  background: var(--acento-fuerte);
  border-color: var(--acento-fuerte);
  color: #fff;
}
.boton.principal:hover { background: var(--acento-hundido); border-color: var(--acento-hundido); color: #fff; }

/* El destructivo tenía el mismo problema que el secundario y peor: su borde
   (`--destructivo-borde`, un rosa de 1,2:1) era todavía menos visible, así que
   «Quitar el buzón» parecía un botón apagado en vez de uno peligroso. El borde
   pasa a `--destructivo` (4,84:1), que es rojo de verdad y se ve. */
.boton.peligro { color: var(--destructivo-hundido); border-color: var(--destructivo); }
.boton.peligro:hover { background: var(--destructivo-suave); border-color: var(--destructivo); color: var(--destructivo-hundido); }
.boton.peligro.principal {
  background: var(--destructivo);
  border-color: var(--destructivo);
  color: #fff;
}
.boton.peligro.principal:hover { background: var(--destructivo-hundido); border-color: var(--destructivo-hundido); color: #fff; }

.boton.ancho { display: flex; width: 100%; }
.boton[hidden] { display: none; }
.boton.chico { min-height: 32px; padding: var(--e1) var(--e2); font-size: var(--t-sm); }

.boton:disabled,
.boton[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
  pointer-events: none;
}

/* Estado de carga. El botón se bloquea mientras el POST viaja para que nadie
   cree dos movimientos con doble clic; el texto sigue ahí y se le agrega un
   girador, así que el ancho no salta. */
.boton[aria-busy="true"] { pointer-events: none; opacity: 0.8; }
.boton[aria-busy="true"]::before {
  content: "";
  width: 14px;
  height: 14px;
  flex: none;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: girar 600ms linear infinite;
}
@keyframes girar { to { transform: rotate(360deg); } }

.boton-texto {
  display: inline-flex;
  align-items: center;
  gap: var(--e1);
  padding: var(--e1) var(--e2);
  font: inherit;
  font-size: var(--t-md);
  font-weight: 600;
  color: var(--acento-fuerte);
  background: none;
  border: none;
  border-radius: var(--r-sm);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.boton-texto:hover { color: var(--acento-hundido); background: var(--acento-suave); }
.banda-impersonacion .boton-texto,
.barra .boton-texto { color: inherit; }
.banda-impersonacion .boton-texto:hover { background: rgba(146, 64, 14, 0.1); }

.enlace-mas {
  display: inline-flex;
  align-items: center;
  gap: var(--e1);
  font-size: var(--t-md);
  font-weight: 600;
  text-decoration: none;
}
.enlace-mas:hover { text-decoration: underline; }

/* Barra de acciones al pie de un formulario. El principal va PRIMERO en el
   orden visual y en el del DOM: es el que se activa con Enter y el que busca la
   vista. «Cancelar» es un enlace, no un botón de peso, porque no hace nada.
   En móvil se apilan a ancho completo, que es donde cae el pulgar. */
.acciones-formulario {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e2);
  margin-top: var(--e5);
  padding-top: var(--e4);
  border-top: 1px solid var(--borde);
}
.acciones-formulario .separa { margin-left: auto; }

/* -- botón «copiar» -------------------------------------------------------
   El parcial src/vistas/parciales/copiar.ejs y public/js/copiar.js.

   Es de SOLO ICONO, y es la única familia de botones que lo es. La razón es de
   sitio: va pegado al valor —dentro de una celda de tabla, al lado de una clave
   de 50 dígitos— y un botón con la palabra «Copiar» al lado de cada cédula de
   un listado de 200 clientes tapa el listado. Por eso lleva `aria-label` y
   `title` obligatorios (los pone el parcial): quien no ve el icono oye qué
   copia, y quien pasa el mouse lo lee.

   El globito de «Copiado» se pinta con ::after y `content: attr(data-mensaje)`
   en vez de meter un nodo al DOM: así el aviso aparece y desaparece sin tocar
   la estructura de la tabla, que es lo que provoca saltos de layout. El aviso
   para lector de pantalla es OTRO —una región viva que crea el script—, porque
   lo que se pinta con `content` no todos los lectores lo anuncian. */
.copiable {
  display: inline-flex;
  align-items: center;
  gap: var(--e2);
  max-width: 100%;
  min-width: 0;
}
.copiable-valor { min-width: 0; overflow-wrap: anywhere; }
/* Para valores largos (clave, api key, enlace público): el valor toma el
   renglón entero y el botón se alinea arriba, no en el centro de tres líneas. */
.copiable.bloque { display: flex; align-items: flex-start; gap: var(--e3); }
.copiable.bloque > .copiable-valor { flex: 1 1 auto; }
.copiable.solo-boton { gap: 0; }

.boton-copiar {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 28px;
  height: 28px;
  padding: 0;
  color: var(--texto-tenue);
  background: none;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  cursor: pointer;
}
.boton-copiar:hover {
  color: var(--acento-fuerte);
  background: var(--acento-suave);
  border-color: var(--acento-borde);
}
.boton-copiar[data-copiado] { color: var(--acento-fuerte); border-color: var(--acento-borde); }
.boton-copiar[data-copiado="error"] { color: var(--destructivo-hundido); border-color: var(--destructivo-borde); }

/* El globito. `pointer-events: none` para que no se coma el clic siguiente, y
   z-index alto porque a veces vive dentro de un contenedor con `overflow`. */
.boton-copiar[data-copiado]::after {
  content: attr(data-mensaje);
  position: absolute;
  bottom: calc(100% + 4px);
  left: 50%;
  z-index: 30;
  transform: translateX(-50%);
  padding: var(--e1) var(--e2);
  font-family: var(--fuente-cuerpo);
  font-size: var(--t-xs);
  font-weight: 600;
  white-space: nowrap;
  color: #fff;
  background: var(--texto);
  border-radius: var(--r-sm);
  pointer-events: none;
  animation: aparecer-copiado 120ms ease-out;
}
.boton-copiar[data-copiado="error"]::after { background: var(--destructivo-hundido); }
@keyframes aparecer-copiado {
  from { opacity: 0; transform: translate(-50%, 4px); }
  to { opacity: 1; transform: translate(-50%, 0); }
}

/* El textarea del respaldo `execCommand('copy')`: tiene que estar en el
   documento y poder recibir la selección, así que no vale `display: none`. */
.copiar-oculto {
  position: fixed;
  top: 0;
  left: -9999px;
  width: 1px;
  height: 1px;
  opacity: 0;
}

/* ===========================================================================
   12 · FILTROS
   ======================================================================== */
.filtros {
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--e3);
  align-items: flex-end;
  padding: var(--e2) var(--e4) var(--e4);
  margin-bottom: var(--e4);
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--r-lg);
  box-shadow: var(--elev-1);
}
.filtros > div { min-width: 150px; }
.filtros > div.crece { flex: 1 1 240px; }
.filtros-acciones { display: flex; align-items: flex-end; gap: var(--e2); padding-bottom: 1px; }
.filtros-acciones .boton { margin-top: var(--e3); }
/* B47 · filtros CORTOS de las recibidas: buscar + fechas arriba, los chips
   «Qué falta» en su renglón y el resto plegado en «Más filtros». El cuerpo
   plegado reusa `.filtros` para las columnas, sin repetir la tarjeta. */
.filtros-cortos > .ancho-total { flex: 1 1 100%; }
.filtros-cortos .chips-falta { margin: var(--e3) 0 0; }
.chips-falta a { min-height: 44px; }
.chips-falta .chip-cuenta { font-weight: 400; margin-left: var(--e1); }
.mas-filtros { margin-top: var(--e2); }
.mas-filtros > summary {
  display: flex;
  align-items: center;
  gap: var(--e1);
  min-height: 44px;
  cursor: pointer;
  font-weight: 600;
  color: var(--texto-suave);
}
.mas-filtros-cuerpo { padding: 0; margin: 0; border: 0; box-shadow: none; background: transparent; }
.campo-con-boton { display: flex; flex-wrap: wrap; gap: var(--e2); align-items: stretch; }
.campo-con-boton .boton { margin: 0; white-space: nowrap; }
/* El campo es lo que crece y el botón se queda con su ancho natural. La
   base de 12rem es el punto en que el botón —icono y un texto largo como
   «Consultar en Hacienda»— ya no cabe al lado: ahí baja a la línea de
   abajo en vez de aplastar el campo o desbordar la pantalla en el móvil. */
.campo-con-boton > input { flex: 1 1 12rem; min-width: 0; }

/* ===========================================================================
   13 · AVISOS Y NOTIFICACIONES
   ---------------------------------------------------------------------------
   Dos cosas distintas y no se mezclan:
     · .aviso   — en el flujo de la página, se queda. Es el que explica un error
                  de formulario o una condición del sistema, y desaparecer sería
                  perder información.
     · .notificacion — flotante, se anuncia y se va sola. Es el "quedó guardado".
   Ninguna de las dos roba el foco: se anuncian por región viva (role=status
   para lo bueno, role=alert para lo malo), que avisa sin sacar al usuario del
   campo donde estaba escribiendo.
   ======================================================================== */
/* OJO: .aviso se queda como BLOQUE, no como contenedor flex.
   Se probó con `display: flex` y rompe cualquier aviso cuyo texto lleve <strong>
   o <code> sueltos: cada uno se vuelve un ítem de flex, la línea deja de
   partirse y el párrafo se sale de la pantalla. Le pasó justo a la advertencia
   de no subir la firma digital de persona física, que es el aviso que más
   importa de todo el sistema. Con display de bloque el icono va en línea con la
   primera línea de texto y el contenido se comporta como texto normal. */
.aviso {
  padding: var(--e3) var(--e4);
  margin-bottom: var(--e4);
  border-radius: var(--r-md);
  border: 1px solid var(--alerta-borde);
  border-left-width: 3px;
  background: var(--alerta-suave);
  color: var(--alerta);
  font-size: var(--t-md);
}
.aviso .icono { margin-right: var(--e1); }
.aviso.exito { background: var(--acento-suave); border-color: var(--acento-borde); color: var(--acento-fuerte); }
.aviso.error { background: var(--destructivo-suave); border-color: var(--destructivo-borde); color: var(--destructivo-hundido); }
.aviso.info { background: var(--info-suave); border-color: var(--info-borde); color: var(--info); }

/* «Está creando un documento de PRUEBAS» (890). Es un `.aviso` normal —el ámbar
   ya es el color del carril de pruebas en todo el sistema— con el borde entero
   marcado en vez de solo el izquierdo: este no informa de algo que pasó, avisa
   de lo que va a pasar al apretar el botón de abajo, y tiene que pesar más que
   el resto de los avisos de la pantalla. El formulario que lleva adentro se
   alinea en la misma línea del texto para que no parezca un botón de guardar. */
.aviso.carril-nuevo {
  border-width: 2px;
  border-left-width: 4px;
}
.aviso.carril-nuevo .en-linea { display: inline; }
.aviso.carril-nuevo .boton-texto { color: inherit; text-decoration: underline; }

/* El botón de guardar de un documento que va a nacer en PRUEBAS. Va en ámbar y
   no en el verde de `.boton.principal`: el color es lo que hace que quien ya
   apretó ese botón cien veces esta semana note que esta vez es distinto. Lleva
   además la palabra «PRUEBAS» dentro, porque el color solo no basta. */
.boton.principal.de-pruebas {
  background: var(--alerta);
  border-color: var(--alerta);
  color: #fff;
}
.boton.principal.de-pruebas:hover {
  background: #7c3608;
  border-color: #7c3608;
  color: #fff;
}

.nota {
  padding: var(--e3) var(--e4);
  background: var(--apagado);
  border-left: 3px solid var(--acento);
  border-radius: 0 var(--r-md) var(--r-md) 0;
  color: var(--texto-suave);
  font-size: var(--t-md);
}
.nota strong { color: var(--texto); }
/* Dentro de la nota el fondo es gris, y ahí --texto-tenue se queda en 4,28:1.
   El texto secundario de una nota sube un tono para pasar el mínimo. */
.nota .sutil,
.nota .ayuda,
.nota .sutil li { color: var(--texto-suave); }

/* -- notificaciones flotantes ------------------------------------------- */
.pila-avisos {
  position: fixed;
  top: calc(var(--alto-barra) + var(--e3));
  right: var(--e4);
  z-index: 60;
  display: flex;
  flex-direction: column;
  gap: var(--e2);
  width: min(380px, calc(100vw - var(--e6)));
  pointer-events: none;
}
.notificacion {
  display: flex;
  align-items: flex-start;
  gap: var(--e2);
  padding: var(--e3) var(--e4);
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-left: 3px solid var(--secundario);
  border-radius: var(--r-md);
  box-shadow: var(--elev-3);
  font-size: var(--t-md);
  pointer-events: auto;
  animation: entrar-aviso var(--mov-medio) var(--curva);
}
.notificacion.exito { border-left-color: var(--acento); color: var(--acento-fuerte); }
.notificacion.error { border-left-color: var(--destructivo); color: var(--destructivo-hundido); }
.notificacion .texto { flex: 1; min-width: 0; color: var(--texto); }
.notificacion .cerrar {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  margin: -2px -6px 0 0;
  padding: 0;
  color: var(--texto-tenue);
  background: none;
  border: none;
  border-radius: var(--r-sm);
  cursor: pointer;
}
.notificacion .cerrar:hover { background: var(--apagado); color: var(--texto); }
.notificacion.saliendo { animation: salir-aviso var(--mov-medio) var(--curva) forwards; }

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

.clave-nueva .clave-valor {
  display: block;
  padding: var(--e3);
  margin-top: var(--e2);
  word-break: break-all;
  font-family: var(--fuente-mono);
  background: var(--superficie);
  border: 1px dashed var(--acento);
  border-radius: var(--r-md);
  color: var(--texto);
}
.bloque-codigo {
  padding: var(--e3) var(--e4);
  background: var(--texto);
  color: #e2e8f0;
  border-radius: var(--r-md);
  overflow-x: auto;
  font-size: var(--t-md);
  line-height: 1.5;
}
/* L6 · «Conectar a Claude»: los comandos llevan una llave de 50 caracteres
   y en el teléfono una línea así ensanchaba toda la página. Se parten donde
   haga falta; el botón «copiar» copia el texto entero igual. */
#claude .bloque-codigo {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.pasos { padding-left: var(--e5); }
.pasos li { margin-bottom: var(--e3); }

/* ===========================================================================
   14 · MODAL Y PANEL LATERAL
   ---------------------------------------------------------------------------
   Sobre <dialog> nativo a propósito. `showModal()` ya trae la trampa de foco,
   el cierre con Escape, el aislamiento del fondo (todo lo de atrás queda inerte
   para el teclado y el lector de pantalla) y el ::backdrop. Reimplementar eso a
   mano son 150 líneas de JavaScript que se rompen en cuanto aparece un campo
   nuevo; el navegador ya lo hace bien.
   Lo único que se agrega en JS es cerrar al hacer clic en el fondo, que el
   elemento nativo no da.
   ======================================================================== */
.modal {
  width: min(560px, calc(100vw - var(--e6)));
  max-width: none;
  max-height: calc(100vh - var(--e8));
  padding: 0;
  border: 1px solid var(--borde);
  border-radius: var(--r-lg);
  box-shadow: var(--elev-4);
  color: var(--texto);
  background: var(--superficie);
}
.modal.ancho { width: min(880px, calc(100vw - var(--e6))); }
.modal::backdrop { background: rgba(15, 23, 42, 0.5); }
.modal[open] { animation: entrar-modal var(--mov-medio) var(--curva); }
.modal[open]::backdrop { animation: entrar-velo var(--mov-medio) var(--curva); }

@keyframes entrar-modal {
  from { opacity: 0; transform: translateY(8px) scale(0.985); }
  to { opacity: 1; transform: none; }
}
@keyframes entrar-velo { from { opacity: 0; } to { opacity: 1; } }

.modal-encabezado {
  display: flex;
  align-items: flex-start;
  gap: var(--e3);
  padding: var(--e4);
  border-bottom: 1px solid var(--borde);
}
.modal-encabezado h2 { margin: 0; flex: 1; }
.modal-cuerpo { padding: var(--e4); overflow-y: auto; }
.modal-cuerpo > :last-child { margin-bottom: 0; }

/* 05-oct-2026 · el resumen antes de emitir: el total se lee de lejos. */
.resumen-emision-total {
  display: flex;
  flex-direction: column;
  gap: var(--e1);
  margin: 0 0 var(--e3);
}
.resumen-emision-total strong {
  font-size: var(--t-2xl);
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.resumen-emision select { max-width: 100%; }
/* B48 · «Revertir aplicación»: la lista de lo que se deshace y el motivo. */
.lista-revertir { padding-left: var(--e4); }
.lista-revertir > li + li { margin-top: var(--e2); }
.lista-revertir .tabla-envoltorio { margin-top: var(--e2); }
.formulario-revertir label { display: block; margin: var(--e3) 0 var(--e1); font-weight: 600; }
.formulario-revertir .acciones-fila { justify-content: flex-end; margin-top: var(--e3); }
.modal-pie {
  display: flex;
  justify-content: flex-end;
  gap: var(--e2);
  padding: var(--e3) var(--e4);
  background: var(--fondo);
  border-top: 1px solid var(--borde);
  border-radius: 0 0 var(--r-lg) var(--r-lg);
}
.modal-cerrar {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  margin: -6px -6px 0 0;
  padding: 0;
  color: var(--texto-tenue);
  background: none;
  border: none;
  border-radius: var(--r-md);
  cursor: pointer;
}
.modal-cerrar:hover { background: var(--apagado); color: var(--texto); }

/* Confirmación de acción destructiva: el icono en rojo y el botón que confirma
   en rojo relleno. La acción que destruye nunca es la que está preseleccionada. */
.modal .marca-peligro {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  flex: none;
  border-radius: 50%;
  background: var(--destructivo-suave);
  color: var(--destructivo);
}

/* -- panel lateral (mismo componente, otra forma) ------------------------ */
/* OJO con el `display`: el navegador esconde un <dialog> cerrado con
   `display: none` desde su hoja de estilo de usuario-agente, y cualquier
   `display` que se le ponga al elemento GANA sobre esa regla. Con
   `display: flex` a secas, el panel cerrado seguía ocupando 100vh al final de
   la página: se veía como un bloque blanco gigante debajo del pie. Por eso el
   flex va en `[open]` y no en el elemento. */
.panel-lateral {
  width: min(460px, 100vw);
  max-width: none;
  height: 100vh;
  max-height: 100vh;
  margin: 0 0 0 auto;
  padding: 0;
  flex-direction: column;
  border: none;
  border-left: 1px solid var(--borde);
  border-radius: 0;
  box-shadow: var(--elev-4);
  background: var(--superficie);
  color: var(--texto);
}
.panel-lateral::backdrop { background: rgba(15, 23, 42, 0.5); }
.panel-lateral[open] { display: flex; animation: entrar-panel var(--mov-lento) var(--curva); }
.panel-lateral .modal-cuerpo { flex: 1; }

@keyframes entrar-panel {
  from { transform: translateX(100%); }
  to { transform: none; }
}

/* ===========================================================================
   15 · ESQUELETOS DE CARGA
   ---------------------------------------------------------------------------
   Para lo que sí carga en el navegador: el buscador de CABYS, la consulta al
   padrón de Hacienda y los lotes de una línea de movimiento. Un esqueleto con la
   forma del resultado en vez de un girador: la página no salta cuando llega la
   respuesta.
   ======================================================================== */
.esqueleto {
  border-radius: var(--r-sm);
  background: linear-gradient(90deg, var(--apagado) 25%, #e9ebec 50%, var(--apagado) 75%);
  background-size: 200% 100%;
  animation: brillo 1.4s ease-in-out infinite;
}
.esqueleto-linea { height: 12px; margin-bottom: var(--e2); }
.esqueleto-linea.corta { width: 40%; }
.esqueleto-linea.media { width: 65%; }
.esqueleto-fila {
  display: flex;
  gap: var(--e3);
  align-items: center;
  padding: var(--e2) var(--e1);
  border-bottom: 1px solid var(--borde);
}
.esqueleto-fila .esqueleto { height: 12px; margin: 0; }
.esqueleto-fila .esqueleto:nth-child(1) { flex: 0 0 90px; }
.esqueleto-fila .esqueleto:nth-child(2) { flex: 1; }
.esqueleto-fila .esqueleto:nth-child(3) { flex: 0 0 48px; }

@keyframes brillo {
  from { background-position: 200% 0; }
  to { background-position: -200% 0; }
}

/* ===========================================================================
   16 · EDITOR DE LÍNEAS
   ---------------------------------------------------------------------------
   Isla de JavaScript que comparten movimientos, cotizaciones y facturas. No
   trae lenguaje visual propio: se apoya en los mismos tokens de color, radio,
   sombra y espaciado que el resto del sistema. Lo único suyo es la densidad,
   porque es una pantalla de digitación seguida.
   ======================================================================== */

/* Etiqueta que solo leen los lectores de pantalla. En una tabla el encabezado
   de columna no queda asociado al <input>, y en móvil el <thead> se oculta. */
.solo-lectores {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------------------------------------------------------------------------
   El editor de líneas (rediseño 25-sep-2026).

   La queja fue «se ve feo»: nueve columnas apretadas, el lote recortado a «Sir»,
   la descripción y la ampliada sueltas debajo del artículo y el descuento en
   cuatro controles. Ahora cada línea es un `<tbody>` con una fila PRINCIPAL
   limpia (artículo · cantidad · precio · descuento · importe) y una fila de
   DETALLES plegable con lo demás, a todo el ancho y con etiquetas visibles.

   Anchos MÍNIMOS por columna y no porcentajes: con `table-layout: auto` el
   navegador reparte lo que sobra al artículo, que es la única columna que lo
   aprovecha; las numéricas no crecen y ningún control queda por debajo de lo
   que necesita para leerse. Si la pantalla no alcanza, la tabla se desplaza
   DENTRO de su envoltorio y la página no.
   --------------------------------------------------------------------------- */
.editor-lineas .tabla-envoltorio {
  /* El editor no lleva el alto máximo de las tablas de consulta: con doble
     barra de desplazamiento el pie de totales quedaba escondido. */
  max-height: none;
}
.editor-lineas .tabla-lineas { table-layout: auto; }
.editor-lineas .tabla-lineas thead th { vertical-align: bottom; padding: var(--e2) var(--e3); }
.editor-lineas .tabla-lineas td { vertical-align: top; padding: var(--e2) var(--e3); }
/* `.tabla td` alinea a la izquierda con más peso que `.der`: sin esto el
   importe y su encabezado quedaban corridos y no se leían como columna. */
.editor-lineas .tabla-lineas th.der,
.editor-lineas .tabla-lineas td.der { text-align: right; }

/* Una línea = un bloque. El separador va ENTRE líneas, no entre la principal y
   sus detalles, que son la misma cosa. */
.editor-lineas .tabla-lineas tbody.linea > tr > td { border-bottom: none; }
.editor-lineas .tabla-lineas tbody.linea { border-bottom: 1px solid var(--borde); }
.editor-lineas .tabla-lineas tbody.linea:hover,
.editor-lineas .tabla-lineas tbody.linea tr:hover { background: none; }
.editor-lineas .tabla-lineas tbody.linea:focus-within { background: var(--fondo); }

.editor-lineas .col-articulo { min-width: 240px; }
.editor-lineas .col-detalle { min-width: 200px; }
.editor-lineas .col-cantidad { width: 104px; min-width: 104px; }
.editor-lineas .col-disponible { width: 96px; white-space: nowrap; }
.editor-lineas .col-unitario { width: 136px; min-width: 136px; }
.editor-lineas .col-descuento { width: 176px; min-width: 176px; }
.editor-lineas .col-origen-precio { min-width: 192px; }
.editor-lineas .col-tarifa { min-width: 200px; }
.editor-lineas .col-lote { min-width: 200px; }
.editor-lineas .col-vence { min-width: 168px; }
.editor-lineas .col-importe { width: 144px; min-width: 144px; white-space: nowrap; }
.editor-lineas .col-acciones { width: 1%; white-space: nowrap; }

.editor-lineas input,
.editor-lineas select { width: 100%; min-height: 40px; }
.editor-lineas textarea { width: 100%; }

/* Las cifras se comparan de un vistazo alineadas a la derecha y con dígitos de
   ancho fijo; el nombre del artículo no. */
.editor-lineas input[type="number"] {
  font-size: var(--t-tabla);
  text-align: right;
  font-variant-numeric: tabular-nums;
  /* Sin las flechitas del navegador: con `step="0.00001"` cada clic restaba
     0,00001 y un 10 terminaba grabado como 9,99998. El número se digita. */
  -moz-appearance: textfield;
  appearance: textfield;
}
.editor-lineas input[type="number"]::-webkit-inner-spin-button,
.editor-lineas input[type="number"]::-webkit-outer-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

/* El artículo: el buscador y, debajo, el nombre en dos tonos. */
.editor-lineas .combo { position: relative; min-width: 0; }
.editor-lineas .combo-campo { width: 100%; font-family: var(--fuente-mono); font-size: var(--t-md); }
.editor-lineas .combo-elegido {
  margin: var(--e1) 0 0;
  font-size: var(--t-md);
  line-height: 1.4;
  color: var(--texto-tenue);
}
.editor-lineas .combo-elegido:empty { display: none; }
.editor-lineas .elegido-nombre { color: var(--texto); font-weight: 600; }
.editor-lineas .elegido-extra { color: var(--texto-tenue); }

/* El descuento es UN control: el número y, pegado, el interruptor «₡ | %».
   Debajo, la equivalencia a dos decimales («= 10 %»). */
.descuento-linea {
  display: flex;
  align-items: stretch;
  gap: 0;
}
.descuento-linea input[type="number"] {
  flex: 1 1 auto;
  min-width: 0;
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}
.descuento-linea select { flex: 0 0 auto; width: auto; margin-left: var(--e1); }
.descuento-linea select[hidden] { display: none; }
.interruptor-descuento {
  display: inline-flex;
  flex: 0 0 auto;
  border: 1px solid var(--boton-borde);
  border-left: 0;
  border-radius: 0 var(--r-md) var(--r-md) 0;
  overflow: hidden;
}
.interruptor-descuento[hidden] { display: none; }
.interruptor-descuento button {
  min-width: 36px;
  min-height: 40px;
  padding: 0 var(--e2);
  font: inherit;
  font-size: var(--t-md);
  font-weight: 600;
  color: var(--texto-suave);
  background: var(--superficie);
  border: 0;
  cursor: pointer;
}
.interruptor-descuento button + button { border-left: 1px solid var(--borde-fuerte); }
.interruptor-descuento button[aria-pressed="true"] {
  color: var(--superficie);
  background: var(--acento-fuerte);
}
.interruptor-descuento button:hover:not([aria-pressed="true"]) { background: var(--apagado); }
.interruptor-descuento button:focus-visible { outline-offset: -3px; }
.descuento-equivalente {
  display: block;
  margin-top: var(--e1);
  font-size: var(--t-xs);
  color: var(--texto-tenue);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.descuento-equivalente:empty { display: none; }

/* El importe: la cifra que se viene a buscar, y el IVA debajo en gris. */
.editor-lineas .importe-linea {
  display: block;
  padding-top: var(--e2);
  font-size: var(--t-tabla);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--texto);
}
.editor-lineas .importe-iva {
  display: block;
  margin-top: var(--e1);
  font-size: var(--t-xs);
  color: var(--texto-tenue);
  font-variant-numeric: tabular-nums;
}
.editor-lineas .importe-iva:empty { display: none; }
.editor-lineas .col-disponible [data-disponible] {
  display: block;
  padding-top: var(--e2);
  font-variant-numeric: tabular-nums;
}

/* Las acciones de la línea: «Detalles» (secundario, con chevron) y la papelera. */
.acciones-linea { display: flex; align-items: flex-start; gap: var(--e1); }
.boton-detalles {
  display: inline-flex;
  align-items: center;
  gap: var(--e1);
  min-height: 40px;
  padding: 0 var(--e2);
  font: inherit;
  font-size: var(--t-md);
  font-weight: 600;
  color: var(--texto-suave);
  background: none;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  cursor: pointer;
  white-space: nowrap;
}
.boton-detalles:hover { color: var(--texto); background: var(--apagado); border-color: var(--borde); }
.boton-detalles[hidden] { display: none; }
.boton-detalles .icono { transition: transform var(--mov-rapido) var(--curva); }
.boton-detalles[aria-expanded="true"] { color: var(--acento-fuerte); }
.boton-detalles[aria-expanded="true"] .icono { transform: rotate(180deg); }
.editor-lineas .boton-quitar { min-height: 40px; min-width: 40px; padding: 0 var(--e2); }

/* La fila de DETALLES: una rejilla de campos con etiqueta visible, sobre un
   fondo apenas distinto para que se lea como parte de la línea de arriba. */
.editor-lineas .linea-detalles > td {
  padding: 0 var(--e3) var(--e3);
}
.detalles-rejilla {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: var(--e3) var(--e4);
  padding: var(--e3);
  background: var(--fondo);
  border: 1px solid var(--borde);
  border-radius: var(--r-md);
}
.campo-detalle { display: flex; flex-direction: column; gap: var(--e1); min-width: 0; }
.campo-detalle[hidden] { display: none; }
.campo-detalle.ancho-completo { grid-column: 1 / -1; }
.campo-detalle > label,
.campo-detalle .rotulo-detalle {
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--texto-suave);
}
.campo-detalle .ayuda { margin: 0; }
.dato-detalle > span:last-child { min-height: 24px; font-size: var(--t-md); }
.editor-lineas [data-descuento-otro] { margin-top: var(--e1); }
.editor-lineas [data-descuento-otro][hidden] { display: none; }
.editor-lineas .linea-ampliada textarea {
  min-height: 60px;
  font-size: var(--t-md);
  line-height: 1.5;
  /* Se conserva aunque el editor ajuste el alto solo: quien está leyendo un
     alcance largo tiene que poder agrandarlo. */
  resize: vertical;
}
.editor-lineas .detalle-contador,
.editor-lineas .ampliada-contador {
  margin-left: var(--e1);
  font-variant-numeric: tabular-nums;
  color: var(--alerta);
}

/* El AVISO de la línea (faltante, error del servidor), a todo el ancho. El
   color no va solo: la línea lleva además la barra roja a la izquierda y el
   motivo está escrito. */
.editor-lineas .linea-aviso > td { padding: 0 var(--e3) var(--e2); }
.editor-lineas .linea-aviso p {
  margin: 0;
  font-size: var(--t-md);
  font-weight: 600;
  color: var(--destructivo-hundido);
}
.editor-lineas tbody.linea.linea-alerta { background: var(--destructivo-suave); }
.editor-lineas tbody.linea.linea-alerta > tr > td:first-child { box-shadow: inset 3px 0 0 var(--destructivo); }

/* El faltante de existencia es una NOTA, no un error: ámbar y con la barra
   del mismo color. El rojo (`.linea-alerta`) queda para lo que el servidor
   rechazó al guardar. */
.editor-lineas tbody.linea.linea-nota > tr > td:first-child { box-shadow: inset 3px 0 0 var(--alerta-borde); }
.editor-lineas tbody.linea.linea-nota .linea-aviso p { color: var(--alerta); }

/* El número VESTIDO: con el campo sin foco, una capa de texto encima muestra
   «₡18,637.00» en vez de «18637». El `<input>` no cambia (su valor exacto es
   el que viaja en el POST): solo se vuelve transparente mientras la capa está
   a la vista. Al enfocarlo, la capa se aparta y queda el número crudo. */
.campo-vestido { position: relative; display: block; flex: 1 1 auto; min-width: 0; }
.campo-vestido-texto {
  position: absolute;
  inset: 1px var(--e3) 1px var(--e2);
  display: none;
  align-items: center;
  justify-content: flex-end;
  overflow: hidden;
  pointer-events: none;
  font-size: var(--t-tabla);
  font-variant-numeric: tabular-nums;
  color: var(--texto);
  white-space: nowrap;
}
.campo-vestido.con-texto:not(:focus-within) .campo-vestido-texto { display: flex; }
.campo-vestido.con-texto:not(:focus-within) input { color: transparent; }
.campo-vestido input[readonly] + .campo-vestido-texto { color: var(--texto-suave); }
.descuento-linea .campo-vestido input { border-top-right-radius: 0; border-bottom-right-radius: 0; }

/* El aviso de la conversión de moneda, con su «Deshacer». */
.aviso-conversion {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e1) var(--e3);
  margin: var(--e2) 0 0;
  padding: var(--e2) var(--e3);
  font-size: var(--t-md);
  color: var(--info);
  background: var(--info-suave);
  border: 1px solid var(--info-borde);
  border-radius: var(--r-md);
}
.aviso-conversion[hidden] { display: none; }

/* Los TOTALES en el pie de la tabla, con la cifra debajo del importe. */
.editor-lineas .totales-lineas th,
.editor-lineas .totales-lineas td {
  padding: var(--e1) var(--e3);
  border-bottom: none;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.editor-lineas .totales-lineas th {
  font-family: var(--fuente-cuerpo);
  font-size: var(--t-md);
  font-weight: 600;
  color: var(--texto-suave);
  white-space: nowrap;
}
.editor-lineas .totales-lineas tr:first-child > * { padding-top: var(--e3); border-top: 1px solid var(--borde-fuerte); }
.editor-lineas .totales-lineas tr:last-child > * { padding-bottom: var(--e3); }
.editor-lineas .totales-lineas .fuerte th,
.editor-lineas .totales-lineas .fuerte td {
  font-size: var(--t-lg);
  font-weight: 700;
  color: var(--acento-fuerte);
}
.editor-lineas .totales-lineas tr[hidden] { display: none; }

/* Migración 1190 · el bloque «Descuento global», entre las líneas y los
   totales: el número con su «₡ | %», el tipo y el motivo en una fila que se
   parte sola cuando no cabe. El monto repartido va en la columna del importe. */
.editor-lineas .descuento-global > tr > td {
  padding: var(--e3);
  border-top: 1px solid var(--borde-fuerte);
  border-bottom: none;
  vertical-align: middle;
}
.editor-lineas .descuento-global [data-descuento-global-monto] {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  white-space: nowrap;
}
.descuento-global-fila {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e2) var(--e3);
}
.descuento-global-rotulo { margin: 0; font-weight: 600; white-space: nowrap; }
.descuento-global-fila .descuento-linea { flex: 0 1 12rem; min-width: 9rem; }
.descuento-global-tipo { flex: 1 1 14rem; min-width: 0; }
.descuento-global-tipo select { width: 100%; }
.descuento-global-motivo { flex: 1 1 12rem; min-width: 0; }
.descuento-global-motivo input { width: 100%; }
.editor-lineas .descuento-global .ayuda { margin: var(--e1) 0 0; }

/* El pie del editor: «Agregar línea» se pega al borde inferior de la ventana
   mientras el editor está a la vista, para no tener que bajar a buscarlo. */
.editor-pie {
  position: sticky;
  bottom: 0;
  z-index: 6;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--e2) var(--e4);
  margin-top: var(--e2);
  padding: var(--e2) 0;
  background: var(--fondo);
}
.editor-pie .editor-acciones { display: flex; flex-wrap: wrap; gap: var(--e2); margin: 0; }
.editor-conteo {
  margin: 0;
  font-size: var(--t-md);
  color: var(--texto-suave);
  font-variant-numeric: tabular-nums;
}
.editor-conteo[hidden] { display: none; }
.editor-atajo { margin: 0; font-size: var(--t-sm); color: var(--texto-tenue); }
.editor-atajo[hidden] { display: none; }
.editor-atajo kbd {
  padding: 0 .3em;
  border: 1px solid var(--borde);
  border-radius: 3px;
  font-family: inherit;
  font-size: .95em;
}

/* La lista va en el flujo, no flotando: dentro de `.tabla-envoltorio`, que
   recorta con overflow-x, una capa absoluta quedaría cortada por el borde. */
.combo-lista {
  list-style: none;
  margin: var(--e1) 0 0;
  padding: 0;
  max-height: 264px;
  overflow-y: auto;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--r-md);
  box-shadow: var(--elev-2);
}
.combo-lista[hidden] { display: none; }
.combo-opcion {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--e1) var(--e2);
  min-height: 44px;
  padding: var(--e2) var(--e3);
  cursor: pointer;
  border-bottom: 1px solid var(--borde);
}
.combo-opcion:last-child { border-bottom: none; }
.combo-opcion:hover,
.combo-opcion.activa { background: var(--acento-suave); }
/* La opción marcada con el teclado no se distingue solo por el fondo: lleva
   además un anillo, que se ve también en alto contraste. */
.combo-opcion.activa { outline: 2px solid var(--acento); outline-offset: -2px; }
.combo-sku {
  font-family: var(--fuente-mono);
  font-size: 0.9em;
  font-weight: 700;
  color: var(--acento-fuerte);
}
.combo-nombre { flex: 1 1 140px; }
.combo-extra { flex-basis: 100%; font-size: var(--t-xs); color: var(--texto-tenue); }

/* Buscador del catálogo CABYS de «Artículos sin CABYS» (13-oct-2026). Usa la
   lista de los combos de arriba; acá solo lo propio: el «Usar este» al final de
   cada opción, la fila de páginas y el aire entre el buscador y lo demás. */
.buscador-cabys { margin: var(--e3) 0; }
.buscador-cabys[hidden] { display: none; }
.buscador-cabys input[type="search"] { width: 100%; max-width: 640px; }
.buscador-cabys .combo-lista { max-height: 420px; max-width: 900px; }
.buscador-cabys-estado { margin: var(--e1) 0 0; min-height: 1.4em; }
.buscador-cabys-estado:empty { min-height: 0; }
.buscador-cabys-opcion .combo-nombre { flex: 1 1 260px; }
.buscador-cabys-usar { margin-left: auto; pointer-events: none; }
.buscador-cabys-paginas {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e2);
  margin-top: var(--e2);
}
.buscador-cabys-paginas[hidden] { display: none; }
@media (max-width: 760px) {
  /* En el teléfono el «Usar este» baja a su propia línea, a lo ancho: es el
     blanco que se toca con el pulgar. */
  .buscador-cabys-usar { flex-basis: 100%; justify-content: center; margin-left: 0; }
  .buscador-cabys-paginas .boton { flex: 1 1 0; }
}

/* La miniatura del artículo en el desplegable del buscador de líneas (B22).
   Tamaño FIJO en CSS y no solo en el atributo: así la fila no salta de alto
   mientras las imágenes cargan, que con veinte resultados se nota. */
.combo-miniatura {
  flex: 0 0 auto;
  width: 32px;
  height: 32px;
  object-fit: cover;
  border: 1px solid var(--borde);
  border-radius: var(--r-sm);
  background: var(--apagado);
}

/* ---------------------------------------------------------------------------
   Fotos de los artículos (B22)
   --------------------------------------------------------------------------- */
.subir-foto {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--e2);
  margin-bottom: var(--e3);
}
.subir-foto input[type="file"] { max-width: 100%; }

.galeria-fotos {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e3);
  margin: 0;
  padding: 0;
  list-style: none;
}
.foto-articulo {
  display: flex;
  flex-direction: column;
  gap: var(--e1);
  width: 150px;
  padding: var(--e2);
  border: 1px solid var(--borde);
  border-radius: var(--r-md);
  background: var(--superficie);
}
/* La principal se distingue por el borde, no solo por la etiqueta: de un
   vistazo se ve cuál va a salir en la cotización. */
.foto-articulo.es-principal { border-color: var(--acento); box-shadow: inset 0 0 0 1px var(--acento); }
.foto-articulo img {
  width: 100%;
  height: 120px;
  object-fit: contain;
  background: var(--apagado);
  border-radius: var(--r-sm);
}
.foto-pie { display: flex; flex-wrap: wrap; align-items: center; gap: var(--e1); font-size: var(--t-xs); }
.foto-acciones { display: flex; flex-wrap: wrap; gap: var(--e1); }
.foto-acciones form { display: inline; }

/* Miniatura en la lista de artículos. */
.miniatura-articulo {
  width: 40px;
  height: 40px;
  object-fit: cover;
  border: 1px solid var(--borde);
  border-radius: var(--r-sm);
  background: var(--apagado);
}

.boton-quitar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--e1);
  min-height: 44px;
  min-width: 44px;
  padding: var(--e2) var(--e3);
  font: inherit;
  font-size: var(--t-md);
  font-weight: 600;
  color: var(--destructivo);
  background: none;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  cursor: pointer;
  transition: background var(--mov-rapido) var(--curva),
    border-color var(--mov-rapido) var(--curva);
}
.boton-quitar:hover {
  color: var(--destructivo-hundido);
  background: var(--destructivo-suave);
  border-color: var(--destructivo-borde);
}
.boton-quitar[hidden] { display: none; }

/* Fila que pide más de lo que hay: se avisa, no se bloquea. Quien decide es el
   servidor, que revisa la existencia dentro de la transacción.
   El fondo rojo no va solo: la fila lleva además una barra en el borde, y el
   editor escribe el motivo en texto, porque el color no es un indicador. */
.tabla tbody tr.linea-alerta,
.tabla tbody tr.linea-alerta:hover { background: var(--destructivo-suave); }
.tabla tbody tr.linea-alerta > td:first-child { box-shadow: inset 3px 0 0 var(--destructivo); }

.editor-acciones { margin: var(--e3) 0 0; }

.editor-totales {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e2) var(--e6);
  margin: var(--e4) 0 var(--e1);
  padding: var(--e3) var(--e4);
  background: var(--fondo);
  border: 1px solid var(--borde);
  border-radius: var(--r-md);
}
.editor-totales[hidden] { display: none; }
.editor-totales > div { display: flex; flex-direction: column; }
.editor-totales dt {
  color: var(--texto-suave);
  font-size: var(--t-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.editor-totales dd {
  margin: 0;
  font-size: var(--t-lg);
  font-variant-numeric: tabular-nums;
}
.editor-totales .fuerte dd { font-weight: 700; color: var(--acento-fuerte); }

/* ---------------------------------------------------------------------------
   El editor ANGOSTO (consulta de CONTENEDOR, no de pantalla).

   Lo que decide si las columnas caben no es la ventana sino el ancho que le
   queda al editor: en 1024 px con la barra lateral abierta son ~700 px, y la
   fila principal pide ~900. Por debajo de 900 px de editor cada línea pasa a
   ser una TARJETA: el artículo arriba (con sus acciones al lado) y debajo los
   números en rejilla, cada uno con su rótulo visible. Es la misma disposición
   que en el teléfono, así que hay una sola regla para las dos.
   --------------------------------------------------------------------------- */
.editor-lineas { container: editor / inline-size; }
@container editor (max-width: 899px) {
  .editor-lineas .tabla-envoltorio {
    overflow: visible;
    contain: none;
    background: none;
    border: none;
    box-shadow: none;
  }
  .editor-lineas .tabla-lineas thead { display: none; }
  .editor-lineas .tabla-lineas,
  .editor-lineas .tabla-lineas tbody,
  .editor-lineas .tabla-lineas tfoot { display: block; width: 100%; }
  .editor-lineas .tabla-lineas tbody.linea {
    margin-bottom: var(--e3);
    padding: var(--e2) 0;
    background: var(--superficie);
    border: 1px solid var(--borde);
    border-radius: var(--r-md);
  }
  .editor-lineas .tabla-lineas tbody.linea > tr {
    display: block;
    margin: 0;
    padding: 0 var(--e1);
    border: none;
    border-radius: 0;
    background: none;
  }
  .editor-lineas .tabla-lineas tbody.linea > tr[hidden] { display: none; }
  .editor-lineas .tabla-lineas td { display: block; width: auto; min-width: 0; }
  .editor-lineas .tabla-lineas td[data-etiqueta]::before {
    content: attr(data-etiqueta);
    display: block;
    margin-bottom: var(--e1);
    font-size: var(--t-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--texto-tenue);
  }
  .editor-lineas .tabla-lineas td.der { text-align: left; }
  .editor-lineas .tabla-lineas tbody.linea > tr.linea-principal {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
    gap: 0 var(--e2);
  }
  .editor-lineas .linea-principal > td.col-articulo,
  .editor-lineas .linea-principal > td.col-detalle { grid-column: 1 / -2; }
  .editor-lineas .linea-principal > td.col-acciones { grid-column: -2 / -1; grid-row: 1; }
  .editor-lineas .linea-principal > td.col-descuento,
  .editor-lineas .linea-principal > td.col-origen-precio,
  .editor-lineas .linea-principal > td.col-tarifa,
  .editor-lineas .linea-principal > td.col-lote,
  .editor-lineas .linea-principal > td.col-vence { grid-column: span 2; }
  .editor-lineas .acciones-linea { justify-content: flex-end; padding-top: var(--e5); }
  .editor-lineas .col-importe .importe-linea { padding-top: var(--e2); }
  .editor-lineas .col-disponible [data-disponible] { padding-top: var(--e2); }
  .editor-lineas .linea-detalles > td,
  .editor-lineas .linea-aviso > td { padding: 0 var(--e3) var(--e2); }
  /* El pie de totales: pares rótulo/monto, alineados a la derecha. */
  /* El descuento global: una tarjeta más, con el monto repartido debajo. */
  .editor-lineas .tabla-lineas tbody.descuento-global > tr { display: block; }
  .editor-lineas .tabla-lineas tbody.descuento-global > tr > td { padding: var(--e2) var(--e1); border-top: none; }
  .editor-lineas .tabla-lineas tbody.descuento-global > tr > td.col-acciones { display: none; }
  .descuento-global-fila .descuento-linea { flex: 1 1 100%; }
  .editor-lineas .totales-lineas { margin-top: var(--e2); }
  .editor-lineas .totales-lineas tr {
    display: grid;
    grid-template-columns: 1fr minmax(128px, auto);
    gap: 0 var(--e3);
    /* Sin la tarjeta que la regla general del teléfono le pone a cada fila. */
    margin: 0;
    padding: 0;
    background: none;
    border: none;
    border-radius: 0;
  }
  .editor-lineas .totales-lineas th,
  .editor-lineas .totales-lineas td { display: block; width: auto; }
  .editor-lineas .totales-lineas td.col-acciones { display: none; }
}
/* Con OCHO columnas o más (la factura registrada a mano y la orden de compra,
   que suman el detalle y la tarifa), o con siete y el botón «Detalles» (el
   contrato y la factura interna, que suman el origen del precio), la fila no
   cabe ni en 1280 px: la tarjeta
   arranca antes. Son las MISMAS reglas de arriba, limitadas a `.editor-ancho`:
   una consulta de contenedor no admite un umbral variable. */
@container editor (min-width: 900px) and (max-width: 1279px) {
  .editor-lineas.editor-ancho .tabla-envoltorio {
    overflow: visible;
    contain: none;
    background: none;
    border: none;
    box-shadow: none;
  }
  .editor-lineas.editor-ancho .tabla-lineas thead { display: none; }
  .editor-lineas.editor-ancho .tabla-lineas,
  .editor-lineas.editor-ancho .tabla-lineas tbody,
  .editor-lineas.editor-ancho .tabla-lineas tfoot { display: block; width: 100%; }
  .editor-lineas.editor-ancho .tabla-lineas tbody.linea {
    margin-bottom: var(--e3);
    padding: var(--e2) 0;
    background: var(--superficie);
    border: 1px solid var(--borde);
    border-radius: var(--r-md);
  }
  .editor-lineas.editor-ancho .tabla-lineas tbody.linea > tr {
    display: block;
    margin: 0;
    padding: 0 var(--e1);
    border: none;
    border-radius: 0;
    background: none;
  }
  .editor-lineas.editor-ancho .tabla-lineas tbody.linea > tr[hidden] { display: none; }
  .editor-lineas.editor-ancho .tabla-lineas td { display: block; width: auto; min-width: 0; }
  .editor-lineas.editor-ancho .tabla-lineas td[data-etiqueta]::before {
    content: attr(data-etiqueta);
    display: block;
    margin-bottom: var(--e1);
    font-size: var(--t-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--texto-tenue);
  }
  .editor-lineas.editor-ancho .tabla-lineas td.der { text-align: left; }
  .editor-lineas.editor-ancho .tabla-lineas tbody.linea > tr.linea-principal {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
    gap: 0 var(--e2);
  }
  .editor-lineas.editor-ancho .linea-principal > td.col-articulo,
  .editor-lineas.editor-ancho .linea-principal > td.col-detalle { grid-column: 1 / -2; }
  .editor-lineas.editor-ancho .linea-principal > td.col-acciones { grid-column: -2 / -1; grid-row: 1; }
  .editor-lineas.editor-ancho .linea-principal > td.col-descuento,
  .editor-lineas.editor-ancho .linea-principal > td.col-origen-precio,
  .editor-lineas.editor-ancho .linea-principal > td.col-tarifa,
  .editor-lineas.editor-ancho .linea-principal > td.col-lote,
  .editor-lineas.editor-ancho .linea-principal > td.col-vence { grid-column: span 2; }
  .editor-lineas.editor-ancho .acciones-linea { justify-content: flex-end; padding-top: var(--e5); }
  .editor-lineas.editor-ancho .col-importe .importe-linea { padding-top: var(--e2); }
  .editor-lineas.editor-ancho .col-disponible [data-disponible] { padding-top: var(--e2); }
  .editor-lineas.editor-ancho .linea-detalles > td,
  .editor-lineas.editor-ancho .linea-aviso > td { padding: 0 var(--e3) var(--e2); }
  /* El pie de totales: pares rótulo/monto, alineados a la derecha. */
  .editor-lineas.editor-ancho .totales-lineas { margin-top: var(--e2); }
  .editor-lineas.editor-ancho .totales-lineas tr {
    display: grid;
    grid-template-columns: 1fr minmax(128px, auto);
    gap: 0 var(--e3);
    /* Sin la tarjeta que la regla general del teléfono le pone a cada fila. */
    margin: 0;
    padding: 0;
    background: none;
    border: none;
    border-radius: 0;
  }
  .editor-lineas.editor-ancho .totales-lineas th,
  .editor-lineas.editor-ancho .totales-lineas td { display: block; width: auto; }
  .editor-lineas.editor-ancho .totales-lineas td.col-acciones { display: none; }
}
/* En el teléfono el artículo necesita todo el ancho: las acciones bajan al
   lado del importe. */
@container editor (max-width: 479px) {
  .editor-lineas .linea-principal > td.col-articulo,
  .editor-lineas .linea-principal > td.col-detalle { grid-column: 1 / -1; }
  .editor-lineas .linea-principal > td.col-acciones { grid-column: auto; grid-row: auto; align-self: end; }
  .editor-lineas .acciones-linea { padding-top: 0; }
  .editor-lineas .linea-principal > td.col-descuento { grid-column: 1 / -1; }
  .detalles-rejilla { grid-template-columns: minmax(0, 1fr); padding: var(--e2); }
}

/* ===========================================================================
   17 · PAGINACIÓN
   ======================================================================== */
/* El destino del ancla `#lista` que llevan los enlaces del paginador. La barra
   fija ya la descuenta `scroll-padding-top` del `html`; este margen extra deja
   ver el borde de arriba de la tabla en vez de pegarla contra la barra. */
#lista { scroll-margin-top: var(--e2); }
/* El ancla `#guardado` de los formularios largos (Mi empresa, Correo a clientes,
   las fichas): apunta al aviso de éxito que queda junto al botón «Guardar». Se
   deja a un tercio de la pantalla y no pegado arriba para que se vean también
   los campos que se acaban de guardar, que están encima. */
#guardado { scroll-margin-top: 30vh; }
.paginado {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--e2);
  margin-top: var(--e3);
  font-size: var(--t-md);
  color: var(--texto-suave);
}
.paginado-botones { display: flex; align-items: center; gap: var(--e2); }
.paginado a {
  display: inline-flex;
  align-items: center;
  gap: var(--e1);
  min-height: 36px;
  padding: var(--e1) var(--e3);
  text-decoration: none;
  font-weight: 600;
  color: var(--texto);
  border: 1px solid var(--boton-borde);
  border-radius: var(--r-md);
  background: var(--superficie);
}
.paginado a:hover { background: var(--apagado); border-color: var(--secundario); }
.paginado .inactivo {
  display: inline-flex;
  align-items: center;
  gap: var(--e1);
  min-height: 36px;
  padding: var(--e1) var(--e3);
  /* Apagado, pero legible: en --borde-fuerte daba 1,4:1 y la palabra
     «Anterior» no se leía. Lo que lo distingue del enlace activo no es que no
     se vea, es que no tiene ni recuadro ni fondo. */
  color: var(--texto-tenue);
  border: 1px solid transparent;
}
.paginado-actual { font-variant-numeric: tabular-nums; }
.paginado-info { margin: 0; font-variant-numeric: tabular-nums; }
/* El selector de tamaño y el salto de página son formularios GET de verdad: sin
   JavaScript se aprietan y funcionan; con JavaScript el selector se envía solo y
   el botón «Aplicar» se calla. */
.paginado-tamano,
.paginado-salto {
  display: flex;
  align-items: center;
  gap: var(--e2);
  margin: 0;
}
.paginado-tamano label,
.paginado-salto label { margin: 0; font-size: var(--t-md); color: var(--texto-suave); }
.paginado-tamano select { min-height: 36px; padding: var(--e1) var(--e2); }
.paginado-salto input[type="number"] {
  width: 5.5rem;
  min-height: 36px;
  padding: var(--e1) var(--e2);
  font-variant-numeric: tabular-nums;
}

/* ===========================================================================
   18 · ACCESO Y ERROR
   ======================================================================== */
.pantalla-acceso {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--e5) var(--e4);
  /* Un degradado muy leve: lo justo para que la tarjeta blanca se despegue del
     papel y la pantalla de entrada no parezca un formulario sin terminar. */
  background:
    radial-gradient(900px 480px at 50% -10%, var(--acento-suave), transparent 70%),
    var(--fondo);
}
.tarjeta-acceso {
  width: 100%;
  max-width: 400px;
  padding: var(--e6) var(--e5);
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--r-lg);
  box-shadow: var(--elev-3);
}
.marca-grande {
  margin: 0 0 var(--e1);
  font-family: var(--fuente-titulo);
  font-size: var(--t-3xl);
  font-weight: 600;
  letter-spacing: -0.03em;
  color: var(--primario);
}
.marca-grande span { color: var(--acento-fuerte); }
/* Logo del emisor en la página pública del comprobante (migración 360): quien
   abre ese enlace viene de un correo de SU proveedor, no de Kiwi. */
.logo-publico {
  display: block;
  max-width: 220px;
  max-height: 80px;
  width: auto;
  height: auto;
  margin: 0 0 var(--e3);
}
.tarjeta-acceso .subtitulo { margin-bottom: var(--e4); color: var(--texto-tenue); font-size: var(--t-md); }
.tarjeta-acceso .boton.ancho { margin-top: var(--e4); min-height: 44px; }
.acceso-pie { margin: var(--e4) 0 0; text-align: center; font-size: var(--t-md); }
/* C14 · el estado de cuenta público del cliente. Misma tarjeta que el
   comprobante público, más ancha porque lleva una lista; cada documento es un
   renglón-tarjeta y no una fila de tabla, para que en 375 px nada se desplace
   de lado. */
.tarjeta-acceso.estado-cuenta-publico { max-width: 640px; }
.estado-cuenta-casa { margin: 0 0 var(--e2); font-family: var(--fuente-titulo); font-size: var(--t-lg); font-weight: 600; }
.estado-cuenta-titulo { margin: 0 0 var(--e1); font-size: var(--t-xl); }
.estado-cuenta-sub { margin: var(--e5) 0 var(--e2); font-size: var(--t-lg); }
.estado-cuenta-lista { list-style: none; margin: 0; padding: 0; }
.estado-cuenta-doc { padding: var(--e3) 0; border-top: 1px solid var(--borde); }
.estado-cuenta-doc:last-child { border-bottom: 1px solid var(--borde); }
.estado-cuenta-doc-fila { display: flex; justify-content: space-between; align-items: center; gap: var(--e2); flex-wrap: wrap; }
.estado-cuenta-doc-fila .mono { min-width: 0; }
.estado-cuenta-doc-enlaces { margin-top: var(--e1); }
.estado-cuenta-doc-enlaces a { display: inline-block; min-height: 44px; line-height: 44px; }
.estado-cuenta-pago { white-space: pre-line; overflow-wrap: anywhere; }
/* 1.39.0 · El estado de cuenta público como estado de cuenta: encabezado, resumen, tabla. */
.tarjeta-acceso.estado-cuenta-publico { max-width: 960px; }
.ec-encabezado { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--e4); flex-wrap: wrap; }
.ec-emisor-datos { margin: var(--e1) 0 0; }
.ec-titulo { text-align: right; }
.ec-titulo p { margin: 0; }
.ec-cliente { margin: var(--e4) 0 var(--e3); padding-top: var(--e3); border-top: 1px solid var(--borde); }
.ec-cliente p { margin: 0; }
.ec-resumen { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--e3); margin-bottom: var(--e3); }
.ec-cifra { display: flex; flex-direction: column; gap: var(--e1); padding: var(--e3); border: 1px solid var(--borde); border-radius: 8px; }
.ec-cifra strong { font-size: var(--t-lg); font-variant-numeric: tabular-nums; }
.ec-vencido strong, .ec-rojo { color: var(--destructivo-hundido); }
.ec-documentos td, .ec-tramos td { font-variant-numeric: tabular-nums; }
.ec-acciones { display: flex; gap: var(--e2); flex-wrap: wrap; margin: var(--e3) 0; }
.ec-descargas a { display: inline-block; min-height: 44px; line-height: 44px; margin-right: var(--e2); white-space: nowrap; }
.ec-notas { padding-left: var(--e4); }
@media (max-width: 600px) {
  .ec-titulo { text-align: left; }
  .ec-resumen { grid-template-columns: 1fr; }
}
@media print {
  .no-imprimir { display: none !important; }
  .pantalla-acceso { padding: 0; background: #fff; }
  .tarjeta-acceso.estado-cuenta-publico { max-width: none; box-shadow: none; border: 0; padding: 0; }
  .ec-documentos, .ec-tramos { font-size: 10pt; }
}

/* 09-oct-2026 · Ctrl+P DE CUALQUIER PANTALLA DEL PANEL. Grudelsa: en las
   pantallas sin hoja propia, imprimir sacaba el menú lateral, la barra y los
   botones. Acá se esconde el armazón (menú, barra, pie, avisos), los filtros,
   la paginación y los botones, y la tabla se suelta de su caja con scroll para
   que salga entera. No reemplaza a «Imprimir / PDF» (la hoja limpia con TODAS
   las filas, `reportes/hoja-imprimir`): esto imprime lo que se ve, la página
   actual. La franja del carril de pruebas SÍ se imprime, sin su botón: una
   pantalla de pruebas en papel tiene que decirlo. Y también los «Filtros
   activos» (sin sus ✕ ni «Quitar todos»), donde la pantalla los tiene: una
   tabla impresa tiene que decir de qué es. */
@media print {
  .lateral,
  .barra,
  .pie,
  .saltar,
  .velo,
  .pila-avisos,
  .banda-impersonacion,
  .aviso-version,
  .migas,
  .filtros,
  .filtros-activos-todos,
  .filtros-activos a > span[aria-hidden="true"],
  .paginado,
  .ayuda-pantalla,
  .acciones-fila,
  .selector-vista,
  .banda-carril-pruebas form,
  dialog,
  .modal,
  .boton,
  button {
    display: none !important;
  }

  body { background: #fff; color: #000; }

  .armazon {
    display: block;
    min-height: 0;
    background: none;
  }

  .contenido,
  .armazon.vista-lista > .contenido {
    max-width: none;
    padding: 0;
  }

  /* La tabla con scroll propio se imprimiría recortada a la altura de su caja. */
  .tabla-envoltorio {
    overflow: visible;
    max-height: none;
    contain: none;
    border: 0;
    box-shadow: none;
  }

  .tabla { font-size: 10pt; }
  .tabla thead { display: table-header-group; }
  .tabla thead th { position: static; }
  .tabla tr { break-inside: avoid; }

  a { color: inherit; text-decoration: none; }
}
/* C14 · la vista previa del recordatorio en texto: se parte en renglones y no
   empuja la página de lado en el teléfono. */
.vista-previa-texto {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  padding: var(--e3);
  border: 1px solid var(--borde);
  border-radius: 6px;
  background: var(--fondo);
  font-size: var(--t-md);
}
.codigo-error {
  margin: var(--e2) 0 0;
  font-family: var(--fuente-titulo);
  font-size: var(--t-3xl);
  font-weight: 600;
  color: var(--borde-fuerte);
  font-variant-numeric: tabular-nums;
}
/* 16-oct-2026 · los botones del 403 «La sesión cambió»: volver, entrar y
   «Salir y entrar con mi usuario» (un formulario), en fila y que bajen solos
   en un teléfono. */
.acciones-error {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e2);
  margin: var(--e4) 0 0;
}

/* ===========================================================================
   19 · RESPONSIVO
   ---------------------------------------------------------------------------
   Probado en 375, 768, 1024 y 1440. Ninguno desborda de lado: lo ancho (tablas)
   se desplaza dentro de su propio envoltorio, nunca el cuerpo de la página.
   ======================================================================== */
@media (max-width: 1100px) {
  /* En pantalla chica la barra cede algo, pero no por debajo de 264: con 224 al
     rótulo le quedaban 92 px y «Transformaciones» —que mide 123 px de una sola
     palabra— se habría partido a la mitad, que es peor que el recorte que
     venimos a quitar. 264 deja 132 px: ningún título se parte ni se recorta,
     solo bajan más de ellos a la segunda línea. */
  :root { --lateral: 264px; }
  .panel-doble,
  .panel-doble.principal-izquierda { grid-template-columns: minmax(0, 1fr); }

  /* Con el menú lateral puesto, a la barra le quedan ~800 px y el nombre del
     usuario se los come. Se calla acá: el mismo dato está en el bloque de
     contexto del menú, a la vista, y en «Mi perfil». */
  .usuario-actual { display: none; }
  .barra-buscar { min-width: 160px; }

  /* -- la barra con el menú DESPLEGADO ------------------------------------
     La compactación vivía solo en el corte de 760 px, y ahí llega tarde: entre
     761 y 1100 la barra no empieza en el borde de la pantalla, empieza donde
     termina el menú (264 px), así que en una tableta de 768 le quedan 504 px
     para el buscador, el distintivo de FE, el selector de caja, «Cambiar»,
     «Cambiar empresa», la campana y «Salir». Medido antes del arreglo: la
     página se salía 256 px en 768 × 1024 y 135 px en 390 × 740.

     La condición es el menú ABIERTO: contraído mide 64 px y los rótulos caben.
     Es lo mismo que decide la consulta de contenedor de la sección 5 —esta es
     la red para el navegador que no entienda `@container`— y por eso apaga el
     mismo interruptor en vez de repetir la lista de selectores. */
  .armazon:not(.menu-contraido) .barra { --rotulos-barra: none; }
  .armazon:not(.menu-contraido) .barra-buscar {
    min-width: 44px;
    width: 44px;
    justify-content: center;
    padding: var(--e1);
  }
  .armazon:not(.menu-contraido) .barra-punto select { max-width: 130px; }
}

@media (max-width: 900px) {
  .contenido { padding: var(--e4) var(--e4) var(--e7); }
}

@media (max-width: 760px) {
  .armazon,
  /* En el teléfono el menú es un CAJÓN, no una columna: contraerlo a iconos no
     significa nada ahí, así que la preferencia de escritorio se ignora en vez
     de dejar un cajón de 64 px con los rótulos escondidos. */
  .armazon.menu-contraido {
    grid-template-columns: 1fr;
    grid-template-areas: "barra" "contenido" "pie";
    background: none;
  }
  .lateral.contraida { padding: 0 var(--e2) var(--e6); }
  .lateral.contraida .marca-texto,
  .lateral.contraida .lateral-contexto,
  .lateral.contraida .lateral-filtro,
  .lateral.contraida .lateral-estrella,
  .lateral.contraida .lateral-grupo .texto,
  .lateral.contraida .lateral-grupo .flecha,
  .lateral.contraida .lateral-rotulo,
  .lateral.contraida .lateral-enlace .texto { display: revert; }
  .lateral.contraida .lateral-reciente .lateral-enlace .texto { display: flex; }
  .lateral.contraida .lateral-estrella { display: flex; }
  .lateral.contraida .lateral-enlace,
  .lateral.contraida .lateral-grupo,
  .lateral.contraida .lateral-herramientas,
  .lateral.contraida .lateral-marca { justify-content: flex-start; }
  .lateral.contraida .lateral-enlace { padding: var(--e2) var(--e3); }
  .lateral.contraida .lateral-grupo { padding: var(--e1) var(--e3) 0; }
  .lateral.contraida .lateral-marca { padding: 0 var(--e4); }
  .boton-menu { display: inline-flex; }
  .barra-empresa { display: block; }
  .barra { padding: var(--e2) var(--e3); }
  /* En 375 px no caben empresa, rol, nombre y salir sin que todo quede cortado
     en "Administrad…". Manda la empresa: el nombre del usuario está en el menú
     lateral y en «Mi perfil». */
  .usuario-actual { display: none; }

  /* Los controles de la barra se reducen al icono: en 375 px el rótulo del
     buscador y el del selector de empresa comen todo el ancho de la empresa,
     que es el dato que no puede faltar. La etiqueta accesible se queda: quien
     usa lector de pantalla sigue oyendo «Buscar en todo el sistema». */
  .barra-buscar {
    min-width: 44px;
    width: 44px;
    justify-content: center;
    padding: var(--e1);
  }
  /* Los rótulos se apagan con el interruptor de la sección 5, que es el mismo
     que usan la consulta de contenedor y el corte de 1100: una sola lista de
     selectores para las tres condiciones. Acá lo que queda es el TAMAÑO, que sí
     es propio del teléfono (44 px de objetivo táctil). */
  .barra { --rotulos-barra: none; }
  .barra-boton { padding: var(--e1); min-width: 44px; }
  /* «Salir» pasa a ser solo el icono; el nombre accesible vive en aria-label. */
  .barra .boton.chico { min-width: 44px; padding: var(--e1); }
  .barra { gap: var(--e2); }
  /* En un teléfono no hay dos ambientes abiertos a la vez ni pestañas donde
     confundirse, y el rótulo de producción es el que menos aporta: se calla el
     discreto y se deja el que avisa. */
  .distintivo-ambiente.produccion { display: none; }

  /* Con el dedo no hay precisión de puntero, así que acá los objetivos táctiles
     van al mínimo de 44 px aunque el navegador no declare `pointer: coarse`. */
  .boton, .boton.chico, .boton-texto, .paginado a, .paginado .inactivo, .saltar,
  .barra-buscar, .barra-boton, .enlace-mas, .modal-cerrar,
  .resultado, .opcion-empresa, .combo-opcion, .boton-quitar { min-height: 44px; }
  /* El de copiar es cuadrado: crece de los dos lados o el icono queda flotando. */
  .boton-copiar { width: 44px; height: 44px; }
  .modal-cerrar { min-width: 44px; }
  .saltar, .enlace-mas { display: inline-flex; align-items: center; }
  input[type="text"], input[type="email"], input[type="password"], input[type="search"],
  input[type="date"], input[type="number"], input[type="tel"], select, textarea { min-height: 44px; }
  label.casilla { min-height: 44px; }
  label.casilla input { width: 22px; height: 22px; }

  .lateral {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    /* El mismo ancho que en escritorio: si los rótulos entran allá, entran acá.
       El `max-width` es el que manda en un teléfono angosto —en 320 px la barra
       mide 275 y deja ver el velo, que es lo que dice que hay una página
       detrás—, y con 280 px sigue sin activarse en los 375 de un iPhone SE. */
    width: var(--lateral);
    max-width: 86vw;
    z-index: 40;
    transform: translateX(-100%);
    transition: transform var(--mov-medio) var(--curva);
    box-shadow: var(--elev-4);
  }
  .lateral.abierto { transform: translateX(0); }
  .velo {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 30;
    background: rgba(15, 23, 42, 0.5);
  }
  .velo[hidden] { display: none; }

  /* Tablas → tarjetas: cada fila se apila y usa data-etiqueta como encabezado.
     Se conserva tal cual estaba: es el patrón que hace usable el sistema en el
     teléfono y no había nada que arreglarle. */
  .tabla thead { display: none; }
  /* `th` iba faltando en esta lista: en el cuadro de totales del comprobante los
     encabezados de FILA seguían siendo celdas de tabla dentro de un `tr` en
     bloque, el navegador armaba una tabla anónima y la página se desplazaba de
     lado en 375 px. */
  .tabla, .tabla tbody, .tabla tr, .tabla td, .tabla tbody th { display: block; width: 100%; }
  .tabla tr {
    margin-bottom: var(--e2);
    padding: var(--e2) var(--e1);
    background: var(--superficie);
    border: 1px solid var(--borde);
    border-radius: var(--r-md);
  }
  .tabla tbody tr:last-child td { border-bottom: none; }
  .tabla td { border: none; padding: var(--e1) var(--e3); }
  .tabla td[data-etiqueta]::before {
    content: attr(data-etiqueta);
    display: block;
    font-size: var(--t-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--texto-tenue);
  }
  .tabla td.der { text-align: left; }

  /* Los totales son pares rótulo/monto: en dos columnas se leen; apilados en
     cuatro bloques, no. */
  .tabla.totales tr {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: baseline;
    gap: 0 var(--e3);
  }
  .tabla.totales td.der { text-align: right; }
  /* En tarjetas no hay nada que desplazar ni encabezado que pegar: el alto
     máximo solo estorbaría. */
  .tabla-envoltorio,
  .bloque .tabla-envoltorio {
    max-height: none;
    overflow: visible;
    background: none;
    border: none;
    box-shadow: none;
  }

  /* En 375 px dos columnas dejan 150 px para el valor: se apila. */
  .definiciones { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .definiciones dt { margin-top: var(--e2); }
  .definiciones dt:first-child { margin-top: 0; }

  .filtros > div, .filtros > div.crece { flex: 1 1 100%; min-width: 0; }
  .filtros-acciones { flex: 1 1 100%; }
  .filtros-acciones .boton { flex: 1; }

  /* Las acciones del formulario se apilan a ancho completo: en un teléfono el
     pulgar acierta mejor un botón ancho que dos angostos pegados. */
  .acciones-formulario { flex-direction: column; align-items: stretch; }
  .acciones-formulario .boton { width: 100%; }
  .acciones-formulario .separa { margin-left: 0; }

  .pila-avisos { top: auto; bottom: var(--e3); right: var(--e3); left: var(--e3); width: auto; }

  /* El modal sube desde abajo como hoja, que es el gesto que espera cualquiera
     en un teléfono, y se apoya en el borde inferior para quedar al alcance del
     pulgar. */
  .modal {
    width: 100vw;
    max-width: 100vw;
    max-height: 90vh;
    margin: auto auto 0;
    border-radius: var(--r-lg) var(--r-lg) 0 0;
    border-bottom: none;
  }
  .modal[open] { animation: subir-hoja var(--mov-lento) var(--curva); }
  .modal-pie { border-radius: 0; }
  .modal-pie .boton { flex: 1; }
  .panel-lateral { width: 100vw; }

  @keyframes subir-hoja {
    from { transform: translateY(100%); }
    to { transform: none; }
  }

  /* El editor de líneas se apila como el resto de las tablas. El envoltorio
     deja de recortar: en tarjetas ya no hay desborde horizontal que esconder. */
  .editor-lineas .tabla-envoltorio { overflow-x: visible; margin: 0; }
  .editor-totales { gap: var(--e2) var(--e5); }
  .editor-totales > div { flex: 1 1 40%; }
  .editor-atajo { display: none; }
}

/* Dedo en vez de puntero: todo lo que se toca mide al menos 44 px y queda
   separado de lo de al lado. En escritorio con mouse no hace falta y solo
   costaría densidad. */
@media (pointer: coarse) {
  .boton, .boton.chico, .boton-texto, .saltar,
  .paginado a, .paginado .inactivo, .lateral a,
  .barra-buscar, .barra-boton, .enlace-mas, .modal-cerrar,
  .resultado, .opcion-empresa, .combo-opcion, .boton-quitar { min-height: 44px; }
  /* El de copiar es cuadrado: crece de los dos lados o el icono queda flotando. */
  .boton-copiar { width: 44px; height: 44px; }
  .modal-cerrar { min-width: 44px; }
  .saltar { display: inline-flex; align-items: center; }
  /* La casilla se toca por la ETIQUETA, no por el cuadrito: es la etiqueta la
     que tiene que medir 44 px. El cuadrito crece igual para que se vea. */
  label.casilla { min-height: 44px; }
  label.casilla input { width: 22px; height: 22px; }
  .filtros-acciones { gap: var(--e2); }
  .encabezado-accion .acciones { gap: var(--e2); }
  input[type="text"], input[type="email"], input[type="password"], input[type="search"],
  input[type="date"], input[type="number"], select, textarea { min-height: 44px; }
}

/* ===========================================================================
   20 · MOVIMIENTO REDUCIDO
   ---------------------------------------------------------------------------
   Quien pidió menos movimiento no lo pidió por gusto. Se apaga todo lo que se
   desplaza o gira; lo que queda es la aparición instantánea, que no marea.
   ======================================================================== */
@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;
  }
  .esqueleto { background: var(--apagado); }
}

/* Buscador de cliente de la cabecera de los documentos de venta.
   Reusa tal cual las clases del combo del editor de líneas: lo único que le
   falta es el contexto de posicionamiento, porque acá el combo NO está dentro
   de `.editor-lineas`. Tres reglas, ningún rediseño. */
.combo[data-buscador-cliente] { position: relative; }
.combo[data-buscador-cliente] .combo-campo { width: 100%; }
.combo[data-buscador-cliente] .combo-elegido { margin: 3px 0 0; color: var(--tinta-suave); }

/* Buscador de ubicación de Hacienda (dirección fiscal). Mismas tres reglas y
   por la misma razón que el de cliente: reusa el combo del editor de líneas y
   solo le falta el contexto de posicionamiento. */
.combo[data-ubicacion-bloque-busqueda] { position: relative; }
.combo[data-ubicacion-bloque-busqueda] .combo-campo { width: 100%; }
.combo[data-ubicacion-bloque-busqueda] .combo-elegido { margin: 3px 0 0; color: var(--tinta-suave); }

/* Roles y permisos (B10). Dos piezas y ninguna más: el grupo de casillas de un
   módulo, que tiene que leerse como una unidad, y el rol plegado, que evita que
   la pantalla nazca con doscientas casillas abiertas. */
.grupo-permisos {
  margin: var(--e3) 0;
  padding: var(--e2) var(--e3);
  border: 1px solid var(--borde);
  border-radius: var(--r-md);
  background: var(--superficie);
}
.grupo-permisos legend {
  padding: 0 var(--e2);
  font-size: var(--t-md);
  font-weight: 600;
  color: var(--texto-suave);
}
.rol {
  margin: var(--e2) 0;
  padding: var(--e3);
  border: 1px solid var(--borde);
  border-radius: var(--r-md);
  background: var(--superficie);
}
.rol > summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e2);
  cursor: pointer;
}

/* Matriz de permisos (migración 970). Filas = permisos, columnas = roles.
   La cabecera ya es sticky arriba por `.tabla thead th`; acá se pega además la
   primera COLUMNA a la izquierda, porque con ocho roles la tabla se desplaza de
   lado y sin el nombre del permiso a la vista las casillas no dicen nada. El
   envoltorio ya desplaza en los dos ejes (`overflow: auto`), así que en el
   teléfono la matriz se recorre con el dedo sin correr la página. */
.matriz-nuevo {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--e2);
}
.matriz-nuevo .crece { flex: 1 1 240px; }
.matriz-permisos { width: auto; min-width: 100%; }
.matriz-permisos th.matriz-permiso {
  position: sticky;
  left: 0;
  z-index: 4;
  background: var(--superficie);
  min-width: 220px;
  max-width: 300px;
  white-space: normal;
  font-weight: 500;
  color: var(--texto);
}
.matriz-permisos th.matriz-permiso .mono { display: block; font-size: var(--t-xs); font-weight: 400; }
/* La esquina: pegada arriba Y a la izquierda, por encima de las dos. */
.matriz-permisos thead th.matriz-permiso { z-index: 6; background: var(--apagado); }
.matriz-permisos thead th.matriz-rol {
  min-width: 96px;
  max-width: 140px;
  white-space: normal;
  text-align: center;
  vertical-align: bottom;
}
.matriz-rol-nombre { display: block; color: var(--texto); }
.matriz-rol-meta {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 2px 4px;
  margin-top: 2px;
  font-weight: 400;
  font-size: var(--t-xs);
}
.matriz-rol.inactivo .matriz-rol-nombre { color: var(--texto-tenue); }
/* «Restablecer a fábrica» es un enlace de verdad a la vista pero un botón de un
   formulario aparte (`form=`): sin fondo ni borde, subrayado al pasar. */
.matriz-restablecer {
  margin-top: 4px;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: var(--t-xs);
  font-weight: 400;
  color: var(--acento-fuerte);
  cursor: pointer;
  white-space: normal;
}
.matriz-restablecer:hover { text-decoration: underline; }
.matriz-celda { text-align: center !important; vertical-align: middle !important; }
.matriz-celda input[type="checkbox"] { width: 18px; height: 18px; cursor: pointer; }
.matriz-celda input[type="checkbox"]:disabled { cursor: not-allowed; }
/* La fila de módulo separa los bloques («Inventario», «Reportes»…): se pega a
   la izquierda para que el nombre no se vaya con el desplazamiento lateral. */
.matriz-permisos tr.matriz-modulo th {
  background: var(--apagado);
  font-family: var(--fuente-titulo);
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--texto-suave);
  text-align: left;
}
.matriz-permisos tr.matriz-modulo:hover { background: transparent; }
/* KIWIERP-37 · la aclaración de un módulo («De fábrica solo el administrador
   ve las metas de todos»): en su propio renglón y partiéndose, para que no
   estire la matriz ni quede cortada a la derecha. */
.matriz-permisos .matriz-nota {
  display: block;
  max-width: 60rem;
  white-space: normal;
  font-family: var(--fuente-cuerpo);
  font-weight: 400;
  margin-top: var(--e1);
}
.matriz-permisos tbody tr:hover th.matriz-permiso { background: var(--acento-suave); }
/* El «Guardar» queda a la vista al final de la pantalla aunque la matriz sea
   larga: es el único botón que persiste todo. */
.matriz-acciones {
  position: sticky;
  bottom: 0;
  z-index: 7;
  display: flex;
  align-items: center;
  gap: var(--e3);
  padding: var(--e2) 0;
  background: var(--fondo);
}
/* Líneas de cobro de la factura (B6).
   La lista del detalle y de la conciliación: sin viñeta, porque cada renglón ya
   se lee como una fila («Tarjeta BAC · ₡30.000 · ref. 004512») y la viñeta solo
   agrega ruido a un dato que se escanea con la vista. */
.lista-cobros { list-style: none; margin: 0; padding: 0; }
.lista-cobros > li + li { margin-top: 2px; }
/* Permisos de una api key (B18). El `facturas:emitir` necesita una frase de
   contexto al lado —no es lo mismo subir comprobantes que firmar a nombre del
   contribuyente— y `label.casilla` centra verticalmente, que con dos renglones
   de texto deja el cuadrito a media altura. Acá se alinea arriba y se deja
   respirar. Tres reglas, ningún rediseño. */
.permisos-llave { border: 0; padding: 0; margin: var(--e3) 0 0; }
.permisos-llave legend { padding: 0; font-weight: 600; color: var(--texto-suave); }
.permisos-llave label.casilla { display: flex; align-items: flex-start; max-width: 70ch; }

/* ===========================================================================
   21 · AYUDA Y MANUAL DE USUARIO (B21)
   ---------------------------------------------------------------------------
   Lo único que hacía falta y no estaba: un ANCHO DE LECTURA. El resto de las
   pantallas son tablas y formularios, que quieren todo el ancho; un capítulo de
   manual con renglones de 140 caracteres no se lee, porque el ojo pierde el
   comienzo de la línea siguiente. `.ayuda-articulo` lo acota a ~70 caracteres,
   que es la medida de siempre para texto corrido.

   El índice va al LADO en escritorio y ARRIBA en móvil, y eso sale solo del
   orden del HTML (el <nav> viene antes del <article>): abajo de 900 px la
   rejilla pasa a una columna y el índice queda donde ya estaba escrito.

   Todo lo demás —bloques, tablas, avisos, código— reusa las clases que ya
   existen; el HTML que produce `src/util/markdown.js` las emite a propósito.
   ======================================================================== */
.ayuda-armazon {
  display: grid;
  grid-template-columns: 260px minmax(0, 1fr);
  gap: var(--e6);
  align-items: start;
}

.ayuda-indice {
  /* Pegado mientras se baja por el capítulo: en un manual de once capítulos,
     saltar de uno a otro es lo que más se hace. */
  position: sticky;
  top: var(--e4);
  padding: var(--e3);
  border: 1px solid var(--borde);
  border-radius: var(--r-md);
  background: var(--superficie);
}
.ayuda-indice-titulo {
  margin: 0 0 var(--e2);
  font-size: var(--t-md);
  font-weight: 600;
  color: var(--texto-tenue);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.ayuda-buscador { margin-bottom: var(--e3); }
.ayuda-buscador .ayuda { margin-bottom: 0; min-height: 1em; }

.ayuda-lista { list-style: none; margin: 0; padding: 0; }
.ayuda-lista a {
  display: block;
  padding: var(--e1) var(--e2);
  border-radius: var(--r-md);
  color: var(--texto-suave);
  font-size: var(--t-md);
  text-decoration: none;
}
.ayuda-lista a:hover { background: var(--acento-suave); color: var(--acento-fuerte); }
.ayuda-lista a.activo {
  background: var(--acento-suave);
  color: var(--acento-fuerte);
  font-weight: 600;
}

.ayuda-tarjetas { list-style: none; margin: 0; padding: 0; }
.ayuda-tarjetas > li { margin-bottom: var(--e3); }
.ayuda-tarjetas h2 { font-size: 1.0625rem; }
.ayuda-tarjetas h2 a { text-decoration: none; }
.ayuda-tarjetas h2 a:hover { text-decoration: underline; }
.ayuda-tarjetas p { margin-bottom: 0; }

/* El ancho de lectura. Es la única regla de esta sección que no es acomodo. */
.ayuda-articulo { max-width: 70ch; }
.ayuda-articulo > h1:first-child { margin-top: 0; }
.ayuda-articulo h2 {
  margin-top: var(--e6);
  padding-top: var(--e3);
  border-top: 1px solid var(--borde);
}
.ayuda-articulo h3 { margin-top: var(--e5); }
.ayuda-articulo li { margin-bottom: var(--e2); }
.ayuda-articulo li > ul,
.ayuda-articulo li > ol { margin-top: var(--e2); }
.ayuda-articulo blockquote {
  margin: var(--e4) 0;
  padding: var(--e2) var(--e4);
  border-left: 3px solid var(--acento-borde);
  background: var(--acento-suave);
}
.ayuda-articulo blockquote > :last-child { margin-bottom: 0; }
.ayuda-articulo code {
  padding: 1px 4px;
  border-radius: 4px;
  background: var(--fondo);
  font-size: 0.9em;
}
.ayuda-articulo pre code { padding: 0; background: none; font-size: inherit; }
.ayuda-articulo hr { margin: var(--e5) 0; border: 0; border-top: 1px solid var(--borde); }

/* Enlace «¿Cómo funciona esta pantalla?» de las cabeceras de módulo. */
.enlace-ayuda {
  display: inline-flex;
  align-items: center;
  gap: var(--e1);
  font-size: var(--t-md);
  color: var(--texto-suave);
  text-decoration: none;
}
.enlace-ayuda:hover { color: var(--acento-fuerte); text-decoration: underline; }

@media (max-width: 900px) {
  .ayuda-armazon { grid-template-columns: minmax(0, 1fr); gap: var(--e4); }
  .ayuda-indice { position: static; }
  /* Sin límite de ancho: en móvil el ancho de la pantalla ya es el de lectura. */
  .ayuda-articulo { max-width: none; }
}

/* ---------------------------------------------------------------------------
   Logo de la empresa y vista previa del correo al cliente (migración 360)
   --------------------------------------------------------------------------- */

/* El logo se sube reducido a 600 px de lado mayor; acá se muestra a tamaño de
   pantalla sin deformarlo. El cuadro a cuadros hace visible la transparencia:
   un PNG con fondo blanco y uno recortado se ven igual sobre una tarjeta
   blanca, y el que importa saber cuál es, es el que va al correo. */
.logo-empresa {
  display: block;
  max-width: 260px;
  max-height: 110px;
  width: auto;
  height: auto;
  padding: var(--e2);
  border: 1px solid var(--borde);
  border-radius: 6px;
  background-color: var(--superficie);
  background-image:
    linear-gradient(45deg, var(--fondo) 25%, transparent 25%),
    linear-gradient(-45deg, var(--fondo) 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, var(--fondo) 75%),
    linear-gradient(-45deg, transparent 75%, var(--fondo) 75%);
  background-size: 14px 14px;
  background-position: 0 0, 0 7px, 7px -7px, -7px 0;
}

/* El correo se dibuja dentro de un `iframe` con su propia CSP: el alto no se
   puede medir desde afuera sin scripts, así que se fija uno generoso y el
   documento de adentro hace su propio desplazamiento. */
.vista-previa-correo {
  display: block;
  width: 100%;
  height: 520px;
  border: 1px solid var(--borde);
  border-radius: 6px;
  background: #f5f7fa;
}

/* -- formato de documentos (migración 840) --------------------------------
   La vista previa del documento es un PDF de verdad dentro de un `iframe`: lo
   dibuja el mismo motor que la factura, así que lo que se ve es lo que sale.
   Alto generoso y fijo por lo mismo que el del correo: no se puede medir el
   documento de adentro sin scripts. */
.vista-previa-documento {
  display: block;
  width: 100%;
  height: 640px;
  border: 1px solid var(--borde);
  border-radius: 6px;
  background: #f5f7fa;
}

/* Las tres tarjetas de diseño. Cada una es un `<label>` con su radio adentro:
   así toda la tarjeta —miniatura incluida— selecciona, sin un manejador en
   línea que la CSP descartaría. */
.disenos {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: var(--e3);
  margin-top: var(--e2);
}

.diseno {
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-areas: "radio nombre" "mini resumen";
  align-items: start;
  gap: var(--e1) var(--e2);
  padding: var(--e3);
  border: 1px solid var(--borde-fuerte);
  border-radius: 8px;
  background: var(--superficie);
  cursor: pointer;
}

.diseno:hover { border-color: var(--acento); }
.diseno:has(input:checked) {
  border-color: var(--acento);
  box-shadow: inset 0 0 0 1px var(--acento);
  background: var(--acento-suave);
}

.diseno input[type="radio"] { grid-area: radio; margin: 0.2rem 0 0; }
.diseno-nombre { grid-area: nombre; font-weight: 600; }
.diseno-resumen { grid-area: resumen; font-size: var(--t-md); color: var(--texto-tenue); }

/* La miniatura es un esquema de la hoja, no una captura: dice dónde queda cada
   cosa, que es lo que se está eligiendo. Geometría en el SVG y color en clases,
   como en los indicadores del panel. */
.miniatura {
  grid-area: mini;
  width: 68px;
  height: 88px;
}

.mini-hoja { fill: #ffffff; stroke: var(--borde-fuerte); stroke-width: 1; }
.mini-hoja-angosta { fill: #ffffff; stroke: var(--borde-fuerte); stroke-width: 1; }
.mini-acento { fill: var(--acento); }
.mini-palido { fill: var(--acento-suave); }
.mini-gris { fill: var(--borde-fuerte); }

/* Los interruptores, en rejilla para que los quince no sean una lista larga. */
.interruptores {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--e2) var(--e3);
  margin-bottom: var(--e3);
}

.interruptor .ayuda { margin-top: 0.15rem; }

/* -- barras de los indicadores (E2) ---------------------------------------
   SVG y no un `style="width:…"`: la CSP prohíbe los estilos en línea, así que
   el ancho tiene que ser geometría (un atributo de <rect>) y el color, una
   clase de acá. Sin librería de gráficos: una barra es un rectángulo. */
.barra-indicador {
  display: block;
  width: 100%;
  min-width: 80px;
  height: 8px;
  border-radius: var(--r-pastilla);
  overflow: hidden;
}
.barra-indicador .fondo { fill: var(--apagado); }
.barra-indicador .valor { fill: var(--acento); }
/* Lo vencido se pinta distinto de lo que está al día. Es la única barra con
   color propio: acá el rojo no es una opinión sobre el negocio, es el dato. */
.barra-indicador .valor.riesgo { fill: var(--destructivo); }


/* -- verificación en dos pasos (E8) ---------------------------------------
   El QR lo dibuja el servidor como SVG en línea (`src/util/qr.js`): la CSP
   prohíbe cargar imágenes de afuera y prohíbe los estilos en línea, así que el
   color no puede ir en el atributo `fill` de cada módulo. Va por
   `currentColor`, o sea que el código hereda el color del texto y se ve bien en
   tema claro y en tema oscuro sin tener que pintar dos.

   El recuadro blanco NO es decoración: un lector de QR necesita contraste entre
   los módulos oscuros y el fondo, y sobre el gris de la tarjeta el margen
   tranquilo se confunde con la página. */
.mfa-alta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e5);
  align-items: flex-start;
  margin-bottom: var(--e5);
}
.mfa-qr {
  flex: 0 0 auto;
  padding: var(--e3);
  background: #ffffff;
  border: 1px solid var(--borde);
  border-radius: 8px;
  color: #0f172a;
}
.mfa-qr .qr { display: block; max-width: 100%; height: auto; }
.mfa-clave { flex: 1 1 28ch; min-width: 0; }

/* La clave en texto, para quien no puede escanear. Grande y espaciada: se
   teclea a mano en un teléfono, mirando la pantalla de una computadora. */
.clave-mfa {
  font-size: 1.05rem;
  letter-spacing: 0.12em;
  word-break: break-all;
  padding: var(--e3);
  background: var(--fondo);
  border: 1px solid var(--borde);
  border-radius: 6px;
}

/* Los códigos de respaldo se muestran UNA sola vez, así que la lista tiene que
   poder imprimirse de un vistazo: columnas cortas y monoespaciado. */
.codigos-respaldo {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(14ch, 1fr));
  gap: var(--e2);
  margin: var(--e4) 0;
  padding: 0;
  list-style: none;
}
.codigos-respaldo li {
  padding: var(--e2) var(--e3);
  background: var(--fondo);
  border: 1px solid var(--borde);
  border-radius: 6px;
  letter-spacing: 0.06em;
  text-align: center;
}

/* ===========================================================================
   22 · PRIMEROS PASOS (B21)
   Guía de arranque del tablero. Es lo primero que ve alguien el primer día, así
   que se lee como una lista de trabajo y no como un adorno: cada paso dice qué
   es, por qué importa y adónde ir.
   ======================================================================== */
.primeros-pasos {
  background: var(--superficie);
  border: 1px solid var(--acento-borde);
  border-left: 3px solid var(--acento);
  border-radius: var(--r-lg);
  box-shadow: var(--elev-1);
  padding: var(--e5);
  margin-bottom: var(--e5);
}

.pp-encabezado {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--e3);
}
.pp-encabezado h2 { margin-bottom: var(--e1); }
.pp-encabezado p { margin-bottom: 0; }

/* Barra de avance. Es un <div> y no un <progress>: el elemento nativo no se
   puede pintar igual en todos los navegadores sin apéndices de proveedor, y acá
   lo único que hace falta es una franja. El dato va ADEMÁS escrito al lado
   —«3 de 8»—, que es lo que de verdad se lee.

   El ancho lo pone `panel.js` leyendo `data-progreso` (ver
   `parciales/barra-avance.ejs`): la CSP prohíbe los estilos en línea y el
   `style="width:…%"` que había antes lo descartaba el navegador, así que la
   franja no se movía nunca. Las clases `progreso-N` de más abajo son el
   respaldo para quien no tenga JavaScript. */
.pp-avance {
  display: flex;
  align-items: center;
  gap: var(--e3);
  margin: var(--e4) 0 var(--e5);
}
.pp-barra {
  flex: 1;
  height: 8px;
  background: var(--apagado);
  border-radius: var(--r-pastilla);
  overflow: hidden;
}
.pp-barra span {
  display: block;
  height: 100%;
  /* Sin ancho declarado, un bloque ocupa TODO el ancho del padre: la barra se
     vería llena en la pantalla de quien no tiene JavaScript y en la de quien lo
     tiene hasta que corra el script. Arranca en cero y crece. */
  width: 0;
  background: var(--acento);
  border-radius: var(--r-pastilla);
  transition: width var(--mov-medio) var(--curva);
}
/* Respaldo sin JavaScript: el porcentaje redondeado hacia abajo a la decena. La
   plantilla siempre pinta una de estas clases; `panel.js`, si corre, escribe el
   valor exacto en `style.width` y gana por especificidad de estilo en línea. */
.pp-barra.progreso-0 span { width: 0; }
.pp-barra.progreso-10 span { width: 10%; }
.pp-barra.progreso-20 span { width: 20%; }
.pp-barra.progreso-30 span { width: 30%; }
.pp-barra.progreso-40 span { width: 40%; }
.pp-barra.progreso-50 span { width: 50%; }
.pp-barra.progreso-60 span { width: 60%; }
.pp-barra.progreso-70 span { width: 70%; }
.pp-barra.progreso-80 span { width: 80%; }
.pp-barra.progreso-90 span { width: 90%; }
.pp-barra.progreso-100 span { width: 100%; }
.pp-cifra {
  font-variant-numeric: tabular-nums;
  font-size: var(--t-sm);
  color: var(--texto-suave);
  white-space: nowrap;
}

.pp-lista {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--e1);
}
.pp-lista li {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: start;
  gap: var(--e3);
  padding: var(--e3);
  border-radius: var(--r-md);
  border: 1px solid transparent;
}
.pp-lista li:not(.hecho) { border-color: var(--borde); background: var(--fondo); }
/* Lo hecho se apaga pero NO se esconde: ver lo que ya se resolvió es la mitad
   de la razón por la que una lista de arranque tranquiliza. */
.pp-lista li.hecho { color: var(--texto-tenue); }
.pp-lista li.hecho .pp-texto h3 { color: var(--texto-suave); }
.pp-lista li.hecho > .icono { color: var(--acento-fuerte); }
.pp-lista li > .icono { width: 20px; height: 20px; margin-top: 2px; color: var(--texto-tenue); }

.pp-texto h3 {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e2);
  margin-bottom: var(--e1);
}
.pp-texto p { margin: 0; font-size: var(--t-md); color: var(--texto-suave); }

.pp-acciones {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--e1);
  white-space: nowrap;
}

@media (max-width: 760px) {
  .primeros-pasos { padding: var(--e4); }
  /* En un teléfono la tercera columna deja al texto en dos palabras por línea:
     las acciones pasan abajo, a lo ancho. */
  .pp-lista li { grid-template-columns: auto 1fr; }
  .pp-acciones {
    grid-column: 1 / -1;
    flex-direction: row;
    align-items: center;
    justify-content: flex-start;
    gap: var(--e3);
  }
}

/* ===========================================================================
   20 · LAUNCHPAD (página de inicio)
   ---------------------------------------------------------------------------
   Tres bloques y en este orden: lo que se viene a HACER (acciones), lo que hay
   que ATENDER (mosaicos con su número) y lo que ya pasó (indicadores y
   recientes). Ver `docs/NAVEGACION.md`.
   ======================================================================== */
.titulo-seccion {
  margin: var(--e6) 0 var(--e3);
  font-family: var(--fuente-titulo);
  font-size: var(--t-lg);
  font-weight: 600;
}
.titulo-con-accion {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e3);
  flex-wrap: wrap;
}
.titulo-con-accion .titulo-seccion { margin-bottom: var(--e3); }

/* -- acciones rápidas ----------------------------------------------------
   Botones, no mosaicos: no llevan número porque no hay nada que contar. */
.acciones-rejilla {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: var(--e3);
}
.accion-rapida {
  display: flex;
  align-items: center;
  gap: var(--e3);
  min-height: 56px;
  padding: var(--e3) var(--e4);
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--r-lg);
  box-shadow: var(--elev-1);
  color: var(--texto);
  text-decoration: none;
  font-weight: 600;
  transition: border-color var(--mov-rapido) var(--curva),
    box-shadow var(--mov-rapido) var(--curva), transform var(--mov-rapido) var(--curva);
}
.accion-rapida .icono { color: var(--acento-fuerte); flex: 0 0 auto; }
.accion-rapida:hover {
  border-color: var(--acento-borde);
  box-shadow: var(--elev-2);
  transform: translateY(-1px);
}

/* -- mosaicos ------------------------------------------------------------
   El `nivel` pinta la franja de la izquierda, pero NUNCA es lo único que
   informa: el detalle dice con palabras qué pasa. */
.mosaicos-rejilla {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: var(--e3);
}
.mosaico {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-left: 4px solid var(--borde-fuerte);
  border-radius: var(--r-lg);
  box-shadow: var(--elev-1);
  transition: box-shadow var(--mov-rapido) var(--curva), transform var(--mov-rapido) var(--curva);
}
.mosaico:hover { box-shadow: var(--elev-2); transform: translateY(-1px); }
.mosaico.alerta { border-left-color: var(--alerta-borde); }
.mosaico.error { border-left-color: var(--destructivo); }
.mosaico.arrastrando { opacity: 0.5; }
.mosaico-enlace {
  display: flex;
  flex-direction: column;
  gap: var(--e1);
  flex: 1 1 auto;
  min-height: 120px;
  padding: var(--e4);
  color: inherit;
  text-decoration: none;
}
.mosaico-cabecera { display: flex; align-items: center; gap: var(--e2); }
.mosaico-cabecera .icono { color: var(--texto-tenue); flex: 0 0 auto; }
.mosaico.alerta .mosaico-cabecera .icono { color: var(--alerta); }
.mosaico.error .mosaico-cabecera .icono { color: var(--destructivo); }
.mosaico-titulo { font-size: var(--t-md); font-weight: 600; color: var(--texto-suave); }
.mosaico-numero {
  font-family: var(--fuente-titulo);
  font-size: var(--t-2xl);
  font-weight: 600;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
}
.mosaico-detalle { font-size: var(--t-md); color: var(--texto-tenue); }

.mosaico-controles {
  display: flex;
  gap: var(--e1);
  padding: 0 var(--e2) var(--e2);
  border-top: 1px solid var(--borde);
  padding-top: var(--e2);
}
.boton-icono {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  color: var(--texto-tenue);
  background: none;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  cursor: pointer;
}
.boton-icono:hover { color: var(--texto); background: var(--apagado); }
.lanzador-mosaicos.acomodando .mosaico { cursor: grab; }

.mosaicos-escondidos { margin-top: var(--e4); font-size: var(--t-md); }
.mosaicos-escondidos summary { cursor: pointer; min-height: 44px; padding: var(--e3) 0; }
.lista-simple { margin: 0; padding: 0; list-style: none; }
.lista-simple li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e3);
  padding: var(--e2) 0;
  border-bottom: 1px solid var(--borde);
}

/* -- indicadores y recientes --------------------------------------------- */
.indicadores-rejilla {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: var(--e3);
}
.indicador {
  display: flex;
  flex-direction: column;
  gap: var(--e1);
  padding: var(--e4);
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--r-lg);
}
.indicador-titulo { font-size: var(--t-md); color: var(--texto-suave); }
.indicador-valor {
  font-family: var(--fuente-titulo);
  font-size: var(--t-xl);
  font-variant-numeric: tabular-nums;
}
.indicador-variacion { font-size: var(--t-xs); color: var(--texto-tenue); }
.indicador-variacion.sube { color: var(--acento-fuerte); }
.indicador-variacion.baja { color: var(--destructivo); }

/* ===========================================================================
   21 · VENTANAS: vista previa
   ---------------------------------------------------------------------------
   El botón de vista previa que `vista-previa.js` agrega a cada fila de lista
   vieja, y el cajón donde cae el detalle. Ver `docs/NAVEGACION.md` §5.3.
   1.70.1 · «Abrir en pestaña nueva» ya no se pega (decisión de Adán, como en
   las filas con el estándar desde el 01-oct): el enlace de la fila es un
   enlace normal y Ctrl/⌘+clic o el botón del medio hacen lo mismo.

   14-sep-2026 · SE LLAMABAN `acciones-fila`, QUE YA ERA DE OTRA COSA. Ese
   nombre es el de la hilera de botones de acción de §12 (`.acciones-fila`, más
   arriba en este archivo), que usan cuarenta vistas. Como esta regla viene
   DESPUÉS y tiene la misma especificidad, le ganaba: toda barra de acciones del
   sistema quedaba en `opacity: 0.35` salvo dentro de un `tr` con el mouse
   encima. Donde más se notó fue en la columna «Archivos» de las facturas
   recibidas, cuyos botones de descarga se veían fantasma. Renombrados a
   `acciones-previa`, que es lo que son: los de la vista previa.
   ======================================================================== */
.acciones-previa {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  margin-left: var(--e2);
  vertical-align: middle;
  /* Discreto hasta que hace falta: en una tabla de doscientas filas, un
     icono a todo color en cada renglón compite con los datos. Al pasar por la
     fila o al llegar con el teclado se ve entero. */
  opacity: 0.35;
  transition: opacity var(--mov-rapido) var(--curva);
}
tr:hover .acciones-previa,
tr:focus-within .acciones-previa,
.resultado-fila .acciones-previa { opacity: 1; }
.boton-fila {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  color: var(--texto-tenue);
  background: none;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  cursor: pointer;
}
.boton-fila:hover { color: var(--acento-fuerte); background: var(--apagado); }

/* El fragmento se pinta adentro del cajón con sus propias clases; lo único que
   hay que acotar es que no se desborde de lado y que los encabezados de página
   del detalle no griten dentro de un panel de 460 px. */
.previa-cuerpo { overflow-x: hidden; }
.previa-cuerpo .encabezado-pagina h1,
.previa-cuerpo .encabezado-accion h1 { font-size: var(--t-lg); }
.previa-cuerpo .tabla-envoltorio { overflow-x: auto; }
.previa-cuerpo .tabla { font-size: var(--t-md); }
.panel-lateral .modal-encabezado { gap: var(--e2); }

@media (pointer: coarse) {
  /* Con el dedo no hay «pasar por encima»: los botones de fila se ven siempre y
     crecen al mínimo táctil. */
  .acciones-previa { opacity: 1; }
  .boton-fila { width: 44px; height: 44px; }
}

/* B5 ampliado · el mini formulario de «crear el artículo que falta».
   Vive en una fila propia de la tabla de aplicar —con `colspan`— para que sus
   siete campos quepan sin estrujar las ocho columnas de arriba. El fondo
   apagado es lo que dice, sin escribirlo, que es un panel de la fila anterior
   y no una línea más de la factura. */
.fila-crear > td { background: var(--apagado); }
.crear-articulo { margin: 0; }
.crear-articulo .rejilla { gap: var(--e1) var(--e4); }
/* Los campos de la tabla previa del lote: sin esto, un <input> dentro de una
   celda hereda el ancho del contenido y el nombre se lee de tres letras. */
[data-lote-articulos] td input,
[data-lote-articulos] td select { min-width: 8rem; }
/* ===========================================================================
   FRANJA DE ESTADO DE UNA FACTURA RECIBIDA (migración 610)
   ---------------------------------------------------------------------------
   Cuatro puertas en el orden en que se cruzan: entró, se le respondió a
   Hacienda, se aplicó adentro, nació la deuda. El color NUNCA es el único
   indicador: cada paso lleva su icono —un visto, una admiración, un candado, un
   número— y su texto, igual que los chips de la sección 9.

   `alerta` (rojo) y `bloqueado` (candado) no se pintan igual a propósito: lo
   primero es algo que pasó mal y hay que mirar; lo segundo es una puerta que
   este documento ya no puede cruzar nunca. Pintarlos iguales hacía pensar que
   el candado se arregla.
   ======================================================================== */
.franja-estado {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: var(--e2);
  margin: 0 0 var(--e5);
  padding: 0;
  list-style: none;
  counter-reset: paso;
}
.franja-estado .paso {
  display: flex;
  gap: var(--e2);
  align-items: flex-start;
  padding: var(--e3);
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-left: 3px solid var(--borde-fuerte);
  border-radius: var(--r-lg);
}
.franja-estado .paso-marca {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--apagado);
  color: var(--texto-tenue);
}
.franja-estado .paso-numero { font-size: var(--t-sm); font-weight: 700; }
.franja-estado .paso-cuerpo { min-width: 0; }
.franja-estado .paso-titulo { display: block; font-size: var(--t-md); }
.franja-estado .paso-estado { display: block; font-size: var(--t-sm); font-weight: 600; }
.franja-estado .paso-detalle { display: block; margin-top: var(--e1); font-size: var(--t-sm); }

.franja-estado .paso-hecho { border-left-color: var(--acento); }
.franja-estado .paso-hecho .paso-marca { background: var(--acento-suave); color: var(--acento-fuerte); }
.franja-estado .paso-hecho .paso-estado { color: var(--acento-fuerte); }

.franja-estado .paso-alerta { border-left-color: var(--destructivo); background: var(--destructivo-suave); }
.franja-estado .paso-alerta .paso-marca { background: var(--destructivo-borde); color: var(--destructivo-hundido); }
.franja-estado .paso-alerta .paso-estado { color: var(--destructivo-hundido); }

/* El candado va en ámbar y no en rojo: no es un error, es una puerta cerrada. */
.franja-estado .paso-bloqueado { border-left-color: var(--alerta); background: var(--alerta-suave); }
.franja-estado .paso-bloqueado .paso-marca { background: var(--alerta-borde); color: var(--alerta); }
.franja-estado .paso-bloqueado .paso-estado { color: var(--alerta); }

.franja-estado .paso-no_aplica { opacity: 0.75; }

/* Los mismos cuatro pasos en una celda de tabla. Solo la forma del icono y el
   color: el texto entero vive en el `title` y en el `.solo-lectores`. */
.pasos-mini { display: inline-flex; gap: 2px; }
.paso-mini {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: var(--r-sm);
  background: var(--apagado);
  color: var(--texto-tenue);
}
.paso-mini.paso-hecho { background: var(--acento-suave); color: var(--acento-fuerte); }
.paso-mini.paso-alerta { background: var(--destructivo-suave); color: var(--destructivo-hundido); }
.paso-mini.paso-bloqueado { background: var(--alerta-suave); color: var(--alerta); }
.paso-mini.paso-no_aplica { opacity: 0.5; }

/* ---------------------------------------------------------------------------
   LOS TRES ARCHIVOS
   Cada renglón dice el archivo, su estado y qué hacer si falta. Los tres están
   SIEMPRE: antes solo aparecían los que existían, así que la factura sin PDF se
   veía igual que la que nunca lo recibió.
   ------------------------------------------------------------------------ */
.lista-archivos { margin: 0; padding: 0; list-style: none; }
.lista-archivos > li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e2);
  padding: var(--e3) 0;
  border-bottom: 1px solid var(--borde);
}
.lista-archivos > li:last-child { border-bottom: 0; }
.lista-archivos .archivo-nombre {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e2);
  min-width: 220px;
  flex: 1 1 260px;
  font-weight: 600;
}
.lista-archivos .archivo-nombre .sutil { flex-basis: 100%; font-weight: 400; }

/* EL BOTÓN DEL ARCHIVO QUE NO LLEGÓ. Vale para los dos lados: la columna
   «Archivos» de la lista y el bloque de los tres archivos del detalle.

   Tres decisiones, y ninguna es cosmética:

   · SE VE. La regla general de `.boton[aria-disabled]` lo baja a `opacity:
     .55`, que sobre blanco deja el texto en 4,2:1 —por debajo del mínimo— y en
     la lista, dentro de una fila, se leía como una mancha. Acá el color se
     escribe entero: `--texto-tenue` sobre la superficie son 4,8:1.
   · NO SE CONFUNDE CON UNO VIVO. El borde punteado dice «acá no hay nada» sin
     depender del color, que es la regla de §9: el color nunca solo.
   · RECIBE EL MOUSE. `pointer-events: none` —que la regla general pone para que
     nadie apriete un botón apagado— también apaga el globo del navegador, y el
     `title` es justamente donde está escrito POR QUÉ falta el archivo. Se
     devuelve el puntero y se deja `not-allowed`: no hay nada que apretar (es un
     `<button>` sin acción), pero el motivo se lee. */
.archivos-comprobante .boton[aria-disabled="true"] {
  opacity: 1;
  color: var(--texto-tenue);
  background: var(--superficie);
  border-style: dashed;
  pointer-events: auto;
  cursor: not-allowed;
}
.archivos-comprobante .boton[aria-disabled="true"] .icono { color: var(--texto-tenue); }

/* --------------------------------------------------------------------------
   F8 · el desglose por ubicación dentro de un renglón de existencias.

   Un `<details>` y nada más: la CSP prohíbe scripts externos y este repositorio
   no usa JavaScript para abrir y cerrar cosas que el HTML ya sabe abrir y
   cerrar. Va alineado a la derecha porque vive dentro de la celda del número.
   -------------------------------------------------------------------------- */
.desglose-ubicaciones { margin-top: 2px; font-weight: 400; text-align: right; }
.desglose-ubicaciones > summary {
  cursor: pointer;
  font-size: var(--t-sm);
  color: var(--texto-tenue);
  list-style-position: inside;
}
.desglose-ubicaciones > summary:hover { color: var(--texto); }
.desglose-ubicaciones .lista-simple { margin-top: 4px; font-size: var(--t-sm); }
.desglose-ubicaciones .lista-simple li { border: 0; padding: 1px 0; }

/* ==========================================================================
   LOS DOS ESTADOS de una factura recibida (14-sep-2026)
   --------------------------------------------------------------------------
   Dos bloques lado a lado, justo debajo de la franja: «Hacienda» (qué dice la
   DGT del documento del proveedor) y «Mensaje receptor» (qué respondió esta
   empresa). Son dos hechos de dos autores distintos y hasta hoy se mostraban
   revueltos en una sola columna: el `EstadoMensaje` que manda el agente —que es
   del primero— se pintaba como si fuera del segundo, y la lista decía «Aceptado
   · respondido en otro sistema» sobre facturas que nadie había respondido.

   La franja de color va en el BORDE IZQUIERDO y no en el fondo: el fondo de
   `.bloque` es el mismo de toda la pantalla y cambiarlo por estado convertía dos
   tarjetas informativas en dos alarmas. El color nunca decide solo (§9): el
   nombre del estado está escrito adentro, siempre.
   ========================================================================== */
.dos-estados { margin-bottom: var(--e5); }
.dos-estados .bloque { margin-bottom: 0; }
.dos-estados .eje-estado { border-left: 3px solid var(--borde); }
.dos-estados .eje-exito { border-left-color: var(--acento); }
.dos-estados .eje-alerta { border-left-color: var(--destructivo); }
.dos-estados .eje-aviso { border-left-color: var(--alerta); }
.dos-estados .eje-estado h2 { margin-top: 0; }

/* ==========================================================================
   LOTE DE VENTAS TESTI-40..48 (24-sep-2026)
   --------------------------------------------------------------------------
   · `.cabecera-compacta` (TESTI-43): el encabezado de la cotización en una
     cuadrícula horizontal, con la etiqueta chica ARRIBA del valor. Antes era
     una lista vertical de once filas que se comía media pantalla. En el
     teléfono cae sola a una o dos columnas: el `auto-fill` no necesita media
     query.
   · `.mas-detalles` y `.plegable-seccion`: lo de trastienda, plegado con un
     `<details>` nativo (sin JavaScript para abrir y cerrar).
   · `.acciones-documento` (TESTI-47): los iconos de acción por fila de las
     listas de venta. Cada uno es un `.boton-icono` de 44 px (tamaño táctil)
     con su tooltip; en fila y sin romper la tabla.
   ========================================================================== */
.cabecera-compacta {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
  gap: var(--e3) var(--e4);
}
.dato-compacto { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.dato-compacto.doble { grid-column: span 2; }
.etiqueta-dato {
  font-size: var(--t-sm);
  color: var(--texto-tenue);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
.valor-dato { overflow-wrap: anywhere; }
@media (max-width: 480px) {
  .dato-compacto.doble { grid-column: 1 / -1; }
}
.mas-detalles { margin-top: var(--e3); }
.mas-detalles > summary,
.plegable-seccion > summary {
  cursor: pointer;
  min-height: 44px;
  display: flex;
  align-items: center;
  color: var(--texto-tenue);
  font-weight: 600;
}
.mas-detalles > summary:hover,
.plegable-seccion > summary:hover { color: var(--texto); }
.mas-detalles[open] > summary { margin-bottom: var(--e2); }
/* 25-sep-2026 · el «Más detalles» del formulario se lee como BOTÓN: chevron,
   rótulo en el color del texto y el resumen de lo que hay adentro en gris. */
.mas-detalles > summary {
  gap: var(--e2);
  list-style: none;
  padding: 0 var(--e3);
  color: var(--texto);
  background: var(--superficie);
  border: 1px solid var(--boton-borde);
  border-radius: var(--r-md);
  width: fit-content;
  max-width: 100%;
  flex-wrap: wrap;
}
.mas-detalles > summary::-webkit-details-marker { display: none; }
.mas-detalles > summary:hover { background: var(--apagado); }
.mas-detalles .flecha-plegable { transition: transform var(--mov-rapido) var(--curva); }
.mas-detalles[open] .flecha-plegable { transform: rotate(180deg); }
.mas-detalles .resumen-plegable { font-weight: 400; color: var(--texto-tenue); font-size: var(--t-md); }
.campo-observaciones { margin: var(--e4) 0 var(--e2); }
.campo-observaciones textarea { width: 100%; }
.plegable-seccion > summary { font-size: 1.05rem; color: var(--texto); }
.resumen-credito { margin: 0 0 var(--e1); }
/* 05-oct-2026 · «Se emitirá: Factura electrónica · cambiar»: una línea discreta
   bajo el cliente, no una pregunta. El aviso del cliente sin cédula lleva sus
   dos salidas en una fila que se parte en el teléfono. */
.documento-a-emitir { margin-top: var(--e2); }
.documento-a-emitir .se-emitira { margin: 0; }
.documento-a-emitir .se-emitira .boton-texto { padding: 0 var(--e1); }
.documento-a-emitir .acciones-aviso { display: flex; flex-wrap: wrap; gap: var(--e2); margin: var(--e2) 0 0; }
.acciones-documento { display: flex; flex-wrap: nowrap; gap: 2px; align-items: center; }
.acciones-documento .en-linea { display: inline-flex; }
.motivo-emision { max-width: 28ch; font-size: var(--t-sm); }

/* Migración 1010 · captura de errores. La pila y el contexto del detalle en
   /admin/errores: texto preformateado que puede ser muy ancho, así que se
   desplaza dentro de su caja en vez de romper el ancho de la página. */
.pila-error {
  max-height: 24rem;
  overflow: auto;
  padding: 12px;
  font-size: 0.8125rem;
  line-height: 1.4;
  white-space: pre;
  background: var(--apagado);
  border: 1px solid var(--borde);
  border-radius: 6px;
}
/* La referencia de la página de error: el usuario la tiene que poder copiar. */
.referencia-error code { user-select: all; }

/* TESTI-50 · condiciones de clientes en lote: la fila que la persona tocó y
   todavía no guardó (la marca `condiciones-clientes.js`). Además del fondo va
   el contador en texto junto al botón: el color solo no se lee en todas partes. */
tr.fila-tocada > td { background: var(--acento-suave); }

/* ===========================================================================
   FICHA DE CLIENTE Y PROVEEDOR (rediseño 25-sep-2026)
   ---------------------------------------------------------------------------
   Barra fija con nombre, chips del padrón y «Guardar»; índice de secciones
   ancladas; Identificación y Contacto lado a lado desde 1024 px; ayudas
   largas plegadas detrás de un «?».
   ======================================================================== */
.ficha-tercero { display: flex; flex-direction: column; gap: var(--e4); }
/* QA 26-sep-2026 · la barra fija sale del <form> y queda como hermana de él
   dentro de `.ficha-tercero-pagina`, que abarca TODAS las secciones: así el
   sticky dura hasta el final de la ficha y no hasta el final del form. El
   margen reemplaza el `gap` del form, que ya no la separa. */
.ficha-tercero-pagina > .ficha-barra { margin-bottom: var(--e4); }

/* La barra se pega DEBAJO de la barra superior, no detrás: `--alto-barra` es
   el alto real que publica `panel.js`. */
.ficha-barra {
  position: sticky;
  top: var(--alto-barra);
  z-index: 15;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--e2) var(--e4);
  align-items: center;
  padding: var(--e3) var(--e4) 0;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--r-lg);
  box-shadow: var(--elev-2);
}
.ficha-barra-datos { min-width: 0; }
.ficha-barra-nombre {
  margin: 0;
  font-family: var(--fuente-titulo);
  font-size: var(--t-lg);
  font-weight: 600;
  overflow-wrap: anywhere;
}
.ficha-barra-chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e1) var(--e2);
  margin: var(--e1) 0 0;
  font-size: var(--t-md);
}
.ficha-barra-acciones { display: flex; flex-wrap: wrap; gap: var(--e2); justify-content: flex-end; }
.ficha-barra-acciones [hidden] { display: none; }
.ficha-indice {
  grid-column: 1 / -1;
  display: flex;
  gap: var(--e1);
  overflow-x: auto;
  margin: 0 calc(var(--e4) * -1);
  padding: 0 var(--e3);
  border-top: 1px solid var(--borde);
  scrollbar-width: thin;
}
.ficha-indice a {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 var(--e3);
  font-size: var(--t-md);
  font-weight: 600;
  color: var(--texto-suave);
  text-decoration: none;
  border-bottom: 3px solid transparent;
}
.ficha-indice a:hover { color: var(--texto); }
.ficha-indice a[aria-current] { color: var(--acento-fuerte); border-bottom-color: var(--acento); }

/* Cada sección es una tarjeta, y al saltar a su ancla queda visible bajo la
   barra superior y la de la ficha. */
.ficha-seccion,
.ficha-tercero ~ .bloque[id] {
  scroll-margin-top: calc(var(--alto-barra) + 150px);
}
.ficha-seccion {
  padding: var(--e4) var(--e5);
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--r-lg);
}
.ficha-seccion > h2 { margin-top: 0; }
.ficha-columnas { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--e4); }
@media (min-width: 1024px) {
  .ficha-columnas { grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); }
}
.ficha-grupo {
  min-width: 0;
  margin: 0 0 var(--e4);
  padding: 0;
  border: 0;
}
.ficha-columnas .ficha-grupo { margin-bottom: 0; }
.ficha-grupo > legend {
  margin-bottom: var(--e2);
  padding: 0;
  font-family: var(--fuente-titulo);
  font-size: var(--t-base);
  font-weight: 600;
  color: var(--texto);
}
.panel-padron { padding: var(--e3); background: var(--fondo); border-radius: var(--r-md); }
.panel-padron > p { margin: 0 0 var(--e1); }
.panel-padron[hidden] { display: none; }

/* KIWIERP-29 · formularios plegados de los módulos contratados (ficha de la
   empresa en el panel de Kiwi y «Mi empresa»). */
.modulo-plegable { margin-top: var(--e3); }
.modulo-plegable > summary {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-weight: 600;
  color: var(--acento-fuerte);
  cursor: pointer;
}
.modulo-plegable[open] > summary { margin-bottom: var(--e2); }
.lista-modulos { list-style: none; margin: 0; padding: 0; }
.lista-modulos > li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--e2) var(--e3);
  padding: var(--e2) 0;
  border-top: 1px solid var(--boton-borde);
}
.lista-modulos > li > div { flex: 1 1 280px; min-width: 0; }
.lista-modulos p { margin: var(--e1) 0 0; }

/* La ayuda LARGA, plegada detrás de un «?» junto a la etiqueta. `<details>`
   nativo: se abre con teclado y sin JavaScript. La ayuda corta que hace falta
   ver siempre sigue como `.ayuda` debajo del campo. */
.etiqueta-con-ayuda { display: flex; flex-wrap: wrap; align-items: center; gap: 0 var(--e2); margin-bottom: var(--e1); }
.etiqueta-con-ayuda > label { margin: 0; }
.ayuda-plegable { display: contents; }
.ayuda-plegable > summary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  font-size: var(--t-sm);
  font-weight: 700;
  color: var(--texto-suave);
  border: 1px solid var(--boton-borde);
  border-radius: 50%;
  cursor: pointer;
  list-style: none;
  position: relative;
}
.ayuda-plegable > summary::-webkit-details-marker { display: none; }
.ayuda-plegable > summary::after { content: "?"; }
/* Área de toque de 40 px aunque el círculo mida 24. */
.ayuda-plegable > summary::before { content: ""; position: absolute; inset: -8px; }
.ayuda-plegable[open] > summary { color: var(--superficie); background: var(--acento-fuerte); border-color: var(--acento-fuerte); }
.ayuda-plegable > p {
  flex: 1 1 100%;
  order: 3;
  margin: var(--e1) 0 var(--e2);
  padding: var(--e2) var(--e3);
  font-size: var(--t-md);
  color: var(--texto-suave);
  background: var(--fondo);
  border-left: 3px solid var(--acento);
  border-radius: var(--r-sm);
}

@media (max-width: 760px) {
  .ficha-barra { grid-template-columns: minmax(0, 1fr); padding: var(--e2) var(--e3) 0; }
  /* En el teléfono la barra fija se queda con lo mínimo —Guardar y el índice—:
     el nombre y los chips ya están en el encabezado de la página, y una barra
     de 230 px pegada arriba se comería media pantalla. */
  .ficha-barra-datos,
  .ficha-barra-acciones [data-proxy-padron] { display: none; }
  .ficha-barra-acciones { justify-content: stretch; }
  .ficha-barra-acciones .boton { flex: 1 1 auto; }
  .ficha-indice { margin: 0 calc(var(--e3) * -1); }
  .ficha-seccion { padding: var(--e3); }
}

/* ---------------------------------------------------------------------------
   B49 · LA FICHA DEL TERCERO EN LECTURA (benchmark de usabilidad #8)
   ---------------------------------------------------------------------------
   Misma barra que la edición (`.ficha-barra`), pero con el nombre como <h1>,
   chips de estado, las acciones del día a día y las PESTAÑAS en el lugar del
   índice de secciones. Debajo, el «Resumen»: tarjetas, facturas abiertas,
   últimos movimientos y notas.
   --------------------------------------------------------------------------- */
.ficha-lectura { margin-bottom: var(--e4); }
.ficha-lectura .ficha-barra-nombre { font-size: var(--t-xl); }
.ficha-lectura .ficha-barra-acciones { align-items: center; }
.ficha-lectura .ficha-barra-acciones .acciones-por-fila { display: inline-flex; }

/* Encabezado de un bloque con su acción a la derecha («Ver todas», «Editar»). */
.bloque-encabezado {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--e2) var(--e3);
  margin-bottom: var(--e3);
}
.bloque-encabezado > h2 { margin: 0; }

.tarjeta-dato .comparacion {
  display: block;
  margin-top: var(--e1);
  font-size: var(--t-md);
  color: var(--texto-suave);
}
.tarjeta-dato.alerta .comparacion { color: var(--texto); }

/* La serie de 12 meses: barras en el tono de fondo y el mes en curso en el
   acento. Cada barra lleva detrás una zona transparente de toda la altura con
   su <title>, para que el mes sin ventas también diga «₡0» al pasar encima. */
.barras-mes { display: block; margin-top: var(--e2); overflow: visible; }
.barras-mes-base { stroke: var(--borde); stroke-width: 1; }
.barras-mes-barra { fill: var(--borde-fuerte); }
.barras-mes-barra.actual { fill: var(--acento); }
.barras-mes-zona { fill: transparent; }
.barras-mes g:hover .barras-mes-zona { fill: var(--apagado); fill-opacity: 0.6; }

.ficha-resumen .bloque,
.ficha-actividad .bloque { margin-bottom: var(--e4); }
.ficha-resumen-lateral .definiciones { grid-template-columns: minmax(96px, auto) 1fr; }

/* Notas y bitácora: fecha a la izquierda, texto a la derecha. */
.linea-tiempo { margin: 0; padding: 0; list-style: none; }
.linea-tiempo li {
  display: grid;
  grid-template-columns: minmax(84px, auto) minmax(0, 1fr);
  gap: var(--e3);
  padding: var(--e2) 0;
  border-top: 1px solid var(--borde);
  font-size: var(--t-md);
}
.linea-tiempo li:first-child { border-top: 0; padding-top: 0; }
.linea-tiempo time { color: var(--texto-tenue); white-space: nowrap; }
.linea-tiempo .nota-texto { white-space: pre-line; overflow-wrap: anywhere; }
.nota-fija {
  margin: 0 0 var(--e3);
  padding: var(--e2) var(--e3);
  background: var(--fondo);
  border-left: 3px solid var(--borde-fuerte);
  border-radius: var(--r-sm);
  white-space: pre-line;
  overflow-wrap: anywhere;
}

/* ---------------------------------------------------------------------------
   1170 · Factura de compra rápida. Pensada primero para el teléfono: una sola
   columna de campos a lo ancho, y el botón «Registrar y emitir» pegado al
   borde de abajo mientras se llena, para que no haya que buscarlo al final.
   En pantallas anchas la columna se limita a 640 px: es un formulario de
   seis campos y a 1440 px de ancho se leería como una tabla vacía.
   --------------------------------------------------------------------------- */
.compra-rapida { max-width: 640px; }
.compra-rapida .rejilla { grid-template-columns: minmax(0, 1fr); }
.compra-rapida input[type="text"],
.compra-rapida select { min-height: 44px; width: 100%; }
.compra-rapida .cr-casilla,
.compra-rapida .cr-confirmacion { display: flex; gap: var(--e2); align-items: flex-start; min-height: 44px; margin: var(--e2) 0; }
.compra-rapida .cr-confirmacion[hidden] { display: none; }
.compra-rapida .cr-casilla input,
.compra-rapida .cr-confirmacion input { width: 22px; height: 22px; flex-shrink: 0; margin-top: 2px; }
.compra-rapida .cr-direccion { border: 0; padding: 0; margin: 0; min-width: 0; }
.compra-rapida .cr-direccion legend { font-weight: 600; padding: 0; margin-bottom: var(--e2); }
.compra-rapida .cr-opcional > summary { min-height: 44px; display: flex; align-items: center; cursor: pointer; }
.compra-rapida .cr-desglose strong { white-space: nowrap; }
.cr-barra-enviar {
  position: sticky;
  bottom: 0;
  z-index: 5;
  margin: var(--e4) calc(-1 * var(--e4)) 0;
  padding: var(--e3) var(--e4) calc(var(--e3) + env(safe-area-inset-bottom, 0px));
  background: var(--superficie);
  border-top: 1px solid var(--borde);
}
.cr-barra-enviar .boton { width: 100%; min-height: 48px; justify-content: center; }
.cr-barra-enviar .ayuda { margin: var(--e1) 0 0; text-align: center; }
@media (min-width: 761px) {
  .cr-barra-enviar { margin-left: 0; margin-right: 0; border: 1px solid var(--borde); border-radius: var(--r-md); }
  .cr-barra-enviar .boton { width: auto; }
  .cr-barra-enviar .ayuda { text-align: left; }
}

/* La pestaña «Datos»: los grupos en dos columnas desde 1024 px. */
.ficha-datos {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0 var(--e4);
  align-items: start;
}
@media (min-width: 1024px) {
  .ficha-datos { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}
.ficha-datos .bloque { margin-bottom: var(--e4); min-width: 0; }
.lista-ficha { margin: 0; padding: 0; list-style: none; }
.lista-ficha li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 44px;
  border-top: 1px solid var(--borde);
}
.lista-ficha li:first-child { border-top: 0; }

/* ---------------------------------------------------------------------------
   Ficha y lista de ARTÍCULOS (25-sep-2026): misma barra fija que la ficha del
   tercero, chips de estado, acciones por fila y barra de acciones en lote.
   --------------------------------------------------------------------------- */
.ficha-articulo-barra { margin-bottom: var(--e4); }
.ficha-barra-acciones form.en-linea { display: inline-flex; margin: 0; }
.ficha-ancla { scroll-margin-top: calc(var(--alto-barra) + 150px); }

.chips-estado { display: flex; flex-wrap: wrap; gap: var(--e2); margin: 0 0 var(--e3); }
.chips-estado a {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  padding: 0 var(--e4);
  font-weight: 600;
  font-size: var(--t-md);
  color: var(--texto-suave);
  text-decoration: none;
  background: var(--superficie);
  border: 1px solid var(--boton-borde);
  border-radius: var(--r-pastilla);
}
.chips-estado a:hover { background: var(--apagado); color: var(--texto); }
.chips-estado a[aria-current] { color: var(--superficie); background: var(--acento-fuerte); border-color: var(--acento-fuerte); }

/* El CABYS (13 dígitos) y el IVA no se parten en dos renglones, y los chips
   de «Control» van en una sola línea. */
.tabla td.celda-cabys,
.tabla td.celda-iva { white-space: nowrap; font-variant-numeric: tabular-nums; }
/* Los chips de «Control» envuelven entre sí (cada chip entero, nunca
   partido): en una sola línea empujaban la tabla y la columna de acciones,
   que es fija a la derecha, les tapaba el final a 1440 px. */
.tabla td.celda-control { white-space: normal; min-width: 128px; }
.tabla td.celda-control .chip { white-space: nowrap; margin: 0 2px 2px 0; }

/* Benchmark #11 (28-sep-2026) · LISTA DE ARTÍCULOS LIVIANA.
   Filas de un renglón (44 px): SKU y nombre en la misma celda, el nombre largo
   cortado con «…» (entero en el `title`) y nada que envuelva. Los chips de
   control ya no apilan: «Lote + vence» es un solo chip. */
.barra-lista { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: var(--e2); }
.barra-lista .chips-estado { flex: 1 1 auto; }
.chips-articulos a { min-height: 44px; gap: var(--e1); }
.control-columnas { position: relative; margin: 0 0 var(--e3); }
.control-columnas > summary {
  display: inline-flex;
  align-items: center;
  gap: var(--e1);
  min-height: 44px;
  padding: 0 var(--e3);
  cursor: pointer;
  font-weight: 600;
  color: var(--texto-suave);
  border: 1px solid var(--boton-borde);
  border-radius: var(--r-md);
  background: var(--superficie);
  list-style: none;
}
.control-columnas > summary::-webkit-details-marker { display: none; }
.control-columnas-panel {
  position: absolute;
  right: 0;
  z-index: 20;
  min-width: 240px;
  margin-top: var(--e1);
  padding: var(--e3);
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--r-lg);
  box-shadow: var(--elev-2, 0 8px 24px rgba(15, 23, 42, 0.14));
}
.control-columnas-panel label.casilla { min-height: 44px; }
.tabla.tabla-articulos td { height: 44px; padding-top: 0; padding-bottom: 0; vertical-align: middle; white-space: nowrap; }
.tabla.tabla-articulos td.celda-articulo { max-width: 26rem; }
/* 09-oct-2026 · la columna «Código»: color de texto normal (no el gris tenue
   de antes), monoespaciada con cifras tabulares (`.mono`) y cortada con «…»
   cuando no cabe; el código entero va en el `title`. 10rem alcanza para un
   EAN-13 y para claves como «ASU650SS-256GT-R» sin cortarlas. */
.tabla .col-codigo-articulo { width: 1%; }
.tabla.tabla-articulos td.celda-codigo { max-width: 10rem; color: var(--texto); }
.celda-codigo .codigo-articulo {
  display: inline-block;
  max-width: 10rem;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: bottom;
}
.celda-articulo .articulo-nombre {
  display: inline-block;
  max-width: 20rem;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: bottom;
}
/* La casilla de fusionar sin el margen de formulario: con él la fila medía 49 px. */
.tabla.tabla-articulos label.casilla { margin: 0; min-height: 44px; }
.tabla.tabla-articulos .miniatura-articulo { width: 32px; height: 32px; display: block; }
.tabla.tabla-articulos td.celda-control .chip { margin: 0 2px 0 0; }
.tabla.tabla-articulos .estado-con-chips { display: inline-flex; gap: var(--e1); flex-wrap: nowrap; }
/* El rótulo «Precio» solo hace falta en la tarjeta del teléfono, donde el
   dato no tiene encabezado de columna encima. */
.rotulo-tarjeta { display: none; }
.tabla .col-acciones-fila { width: 1%; white-space: nowrap; }
/* La columna de acciones queda PEGADA a la derecha: si la tabla no cabe y se
   desplaza dentro de su envoltorio, las acciones siguen a la vista. */
.tabla td.col-acciones-fila,
.tabla th.col-acciones-fila {
  position: sticky;
  right: 0;
  background: var(--superficie);
  box-shadow: -6px 0 6px -6px rgba(15, 23, 42, 0.18);
}
.tabla thead th.col-acciones-fila { background: var(--apagado); z-index: 6; }
/* La tabla que se desplaza aun con las acciones compactadas (acciones-fila.js):
   la columna pegada tapa otra, y con la sombra suave de siempre se leía como un
   encabezado cortado. Borde y sombra más marcados dicen «sigue debajo». */
.tabla.se-desplaza td.col-acciones-fila,
.tabla.se-desplaza th.col-acciones-fila {
  border-left: 1px solid var(--borde-fuerte);
  box-shadow: -10px 0 10px -8px rgba(15, 23, 42, 0.3);
}
@media (max-width: 760px) {
  .tabla td.col-acciones-fila { position: static; box-shadow: none; }
}
.tabla .col-nombre-articulo { min-width: 180px; }

/* ===========================================================================
   COLUMNAS DE TEXTO DE LAS LISTAS (05-oct-2026, §8 de docs/DESIGN_PATTERNS.md)
   ---------------------------------------------------------------------------
   Con `width: 100%` la tabla reparte lo que falta entre las columnas que se
   pueden partir, y las que se parten son las de texto: el emisor quedaba en
   cinco renglones («EXTREME / TECHNOLOGY / CORP E T C / SOCIEDAD / ANONIMA»).
   Cada columna de texto tiene un mínimo; lo que no quepa lo resuelve el
   desplazamiento de `.tabla-envoltorio`, no el aplastamiento.
   ======================================================================== */
/* Razón social: dos renglones como mucho y el nombre entero en el `title`. */
/* El tope va en el texto y no en la celda: un `max-width` en un `<td>` el
   navegador lo ignora al repartir la tabla, en un bloque de adentro no. */
.tabla .col-nombre { min-width: 14rem; }
.nombre-2-lineas {
  max-width: 22rem;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  overflow-wrap: anywhere;
}
/* Un chip con su fecha debajo: el chip no se parte y la fecha va en un
   renglón chico. */
.tabla .col-estado-texto { min-width: 11rem; }
.tabla .col-estado-texto .chip { white-space: nowrap; }
.linea-chica { display: block; font-size: var(--t-sm); white-space: nowrap; }

.barra-masiva { position: sticky; top: var(--alto-barra); z-index: 6; }
.barra-masiva[hidden] { display: none; }
.barra-masiva select { width: auto; max-width: 260px; }

/* El «⋯» de cada fila: un `<details>` que despliega una lista corta. */
.menu-mas { position: relative; display: inline-block; }
.menu-mas > summary { list-style: none; cursor: pointer; }
.menu-mas > summary::-webkit-details-marker { display: none; }
.menu-mas > ul {
  position: absolute;
  right: 0;
  z-index: 10;
  min-width: 200px;
  margin: var(--e1) 0 0;
  padding: var(--e1) 0;
  list-style: none;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--r-md);
  box-shadow: var(--elev-3);
}
.menu-mas > ul a { display: flex; align-items: center; min-height: 40px; padding: 0 var(--e3); color: var(--texto); text-decoration: none; }
.menu-mas > ul a:hover { background: var(--apagado); }

/* ===========================================================================
   ESTÁNDAR · ACCIONES POR FILA (26-sep-2026, parciales/acciones-fila.ejs)
   ---------------------------------------------------------------------------
   Hasta cuatro iconos rápidos de 44 px y UN menú «⋯» con texto para todo lo
   demás. Reglas completas en docs/DESIGN_PATTERNS.md §1. El `.menu-mas` de
   arriba es el patrón viejo del #193: queda para lo que todavía no migró.

   El panel tiene tres formas:
     · sin JavaScript: absoluto bajo el «⋯» (como el `.menu-mas`);
     · `.flotante`: `acciones-fila.js` lo muda fuera de la tabla y lo fija a
       la ventana, pegado al «⋯». Hace falta porque `.tabla-envoltorio` lleva
       `contain: paint`, que recorta hasta lo `fixed` que viva adentro;
     · `.hoja`: en el teléfono (≤ 600 px) sube desde abajo, a todo el ancho,
       con un velo detrás: ni se corta ni obliga a apuntar a un menú chiquito.
   ======================================================================== */
.acciones-por-fila { display: flex; flex-wrap: nowrap; gap: 2px; align-items: center; }
.acciones-por-fila .en-linea { display: inline-flex; margin: 0; }
/* El «⋯» siempre en el mismo lugar, al final: con tres iconos en una fila y
   cuatro en la de abajo, un «⋯» que se corre de columna no se encuentra. */
.acciones-por-fila > .menu-acciones { margin-left: auto; }
/* Los 44 px no se negocian: sin esto, en la tarjeta del teléfono (la celda
   con `width: 1%` de `.col-acciones-fila`) los iconos se encogían a 16 px. */
.acciones-por-fila > * { flex-shrink: 0; }
@media (max-width: 760px) {
  .tabla td.col-acciones-fila { width: auto; }
}
/* La celda pegada lleva fondo propio (tapa lo que se desplaza por debajo): al
   pasar el mouse por la fila tiene que teñirse igual que el resto. */
.tabla tbody tr:hover td.col-acciones-fila { background: var(--acento-suave); }
/* 30-sep-2026 · todas las acciones a la vista (parciales/acciones-fila.ejs):
   en renglones de hasta seis iconos, pegados a la derecha, como el sistema
   anterior. Sin el tope, diez iconos de 44 px empujaban la tabla de lado. El
   «⋯» de la lista existe solo para el teléfono (public/css/movil.css). */
/* 05-oct-2026 · UN SOLO RENGLÓN, NUNCA DOS. Los renglones de seis partían las
   ocho acciones de Facturas recibidas en dos filas y la de abajo, con la celda
   pegada a la derecha, se montaba sobre la columna de al lado. La celda mide
   lo que miden sus iconos; si la tabla no cabe, `acciones-fila.js` la pasa a
   `.acciones-compactas` (abajo): quedan los rápidos y lo demás va al «⋯». */
.acciones-por-fila.todo-visible {
  flex-wrap: nowrap;
  justify-content: flex-end;
  /* `max-content` y no `auto`: la celda mide `width: 1%` y, sin esto, el
     renglón se encogía a un icono y los apilaba en columna. */
  width: max-content;
  margin-left: auto;
}
.menu-acciones.solo-movil-menu { display: none; }
.boton-icono.peligro { color: var(--destructivo-hundido); }
.boton-icono.peligro:hover { color: var(--destructivo-hundido); background: var(--destructivo-suave); }
/* Los tonos de las acciones a la vista («¿no podemos hacerlos coloridos?»):
   fondo suave, borde y dibujo del mismo color de la paleta. Con un tono los
   renglones llevan un poco más de aire, para que no se lean como una tira. */
.acciones-por-fila.todo-visible { gap: 4px; }
/* La tabla que no cabe ni con todo a la vista: lo mismo que el teléfono
   (public/css/movil.css), pero con la tabla de escritorio. Los iconos que
   venían del menú se esconden y vuelve el «⋯» con su texto; los rápidos (ver,
   el PDF, la acción del estado) siguen a un clic. */
.tabla.acciones-compactas .acciones-por-fila > .desde-menu { display: none; }
.tabla.acciones-compactas .menu-acciones.solo-movil-menu { display: inline-block; }
.boton-icono.tono-info { color: var(--info); background: var(--info-suave); border-color: var(--info-borde); }
.boton-icono.tono-acento { color: var(--acento-fuerte); background: var(--acento-suave); border-color: var(--acento-borde); }
.boton-icono.tono-alerta { color: var(--alerta); background: var(--alerta-suave); border-color: var(--alerta-borde); }
.boton-icono.tono-peligro { color: var(--destructivo-hundido); background: var(--destructivo-suave); border-color: var(--destructivo-borde); }
.boton-icono.tono-apagado { color: var(--texto-tenue); background: var(--fondo); border-color: var(--borde-fuerte); }
.boton-icono.tono-apagado:not([aria-disabled="true"]):hover { color: #fff; background: var(--texto-suave); border-color: var(--texto-suave); }
.boton-icono.tono-info:not([aria-disabled="true"]):hover { color: #fff; background: var(--info); border-color: var(--info); }
.boton-icono.tono-acento:not([aria-disabled="true"]):hover { color: #fff; background: var(--acento-fuerte); border-color: var(--acento-fuerte); }
.boton-icono.tono-alerta:not([aria-disabled="true"]):hover { color: #fff; background: var(--alerta); border-color: var(--alerta); }
.boton-icono.tono-peligro:not([aria-disabled="true"]):hover { color: #fff; background: var(--destructivo); border-color: var(--destructivo); }
.boton-icono[aria-disabled="true"] { opacity: 0.45; cursor: not-allowed; }
.boton-icono[aria-disabled="true"]:not([class*="tono-"]):hover { background: none; color: var(--texto-tenue); }

.menu-acciones { position: relative; display: inline-block; }
.menu-acciones > summary { list-style: none; cursor: pointer; }
.menu-acciones > summary::-webkit-details-marker { display: none; }
.menu-acciones[open] > summary { color: var(--texto); background: var(--apagado); border-color: var(--borde); }

.menu-acciones-panel {
  position: absolute;
  top: 100%;
  right: 0;
  z-index: 30;
  min-width: 232px;
  max-width: min(320px, calc(100vw - 16px));
  max-height: min(70vh, 440px);
  overflow-y: auto;
  margin-top: var(--e1);
  padding: var(--e1) 0;
  text-align: left;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--r-md);
  box-shadow: var(--elev-3);
}
.menu-acciones-panel.flotante { position: fixed; right: auto; margin: 0; z-index: 50; }
.menu-acciones-panel.hoja {
  top: auto;
  right: 0;
  bottom: 0;
  left: 0;
  width: 100%;
  max-width: none;
  max-height: 75vh;
  padding: 0 0 calc(var(--e3) + env(safe-area-inset-bottom, 0px));
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  box-shadow: var(--elev-4);
  animation: hoja-sube var(--mov-medio) var(--curva);
}
@keyframes hoja-sube { from { transform: translateY(24px); opacity: 0; } to { transform: none; opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .menu-acciones-panel.hoja { animation: none; } }
.menu-acciones-velo { position: fixed; inset: 0; z-index: 49; background: rgba(15, 23, 42, 0.4); }
.menu-acciones-velo[hidden] { display: none; }

/* El nombre del documento solo se lee en la hoja: en escritorio el menú está
   pegado a su fila y repetirlo sería ruido. */
.menu-acciones-titulo { display: none; }
.menu-acciones-panel.hoja .menu-acciones-titulo {
  display: block;
  margin: 0;
  padding: var(--e3) var(--e4);
  font-weight: 600;
  border-bottom: 1px solid var(--borde);
}
.menu-acciones-grupo {
  margin: var(--e2) 0 0;
  padding: var(--e1) var(--e4);
  font-size: var(--t-sm);
  color: var(--texto-tenue);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
.menu-acciones-panel > .menu-acciones-grupo:first-child { margin-top: 0; }
.menu-acciones-panel form { display: block; margin: 0; }
.menu-acciones-item {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  width: 100%;
  min-height: 44px;
  padding: var(--e1) var(--e4);
  color: var(--texto);
  font: inherit;
  font-size: 0.9375rem;
  text-align: left;
  text-decoration: none;
  background: none;
  border: 0;
  border-radius: 0;
  cursor: pointer;
}
/* `display: flex` le gana al atributo `hidden`: sin esto la «Vista previa»
   aparecería aunque no haya script que la atienda. */
.menu-acciones-item[hidden] { display: none; }
.menu-acciones-item:hover { background: var(--apagado); color: var(--texto); }
.menu-acciones-item:focus-visible { background: var(--apagado); outline-offset: -2px; }
.menu-acciones-item.peligro { color: var(--destructivo-hundido); }
.menu-acciones-item[aria-disabled="true"] { color: var(--texto-tenue); cursor: not-allowed; }
.menu-acciones-item[aria-disabled="true"]:hover { background: none; }
.menu-acciones-motivo { font-size: var(--t-sm); color: var(--texto-tenue); }

/* Primera columna de las listas de documentos: número y serie en UNA línea,
   para que todas las filas midan lo mismo (captura de cotizaciones, 26-sep). */
.doc-numero { white-space: nowrap; }
.doc-numero .sutil { margin-left: var(--e1); }
/* Un vacío es un vacío: el «—» va tenue para no competir con los datos. */
.vacio-celda { color: var(--texto-tenue); }
.chip-enlace { text-decoration: none; }
.chip-enlace:hover { text-decoration: underline; }
.estado-con-chips { display: flex; flex-wrap: wrap; gap: var(--e1); align-items: center; }
@media (min-width: 761px) { .estado-con-chips { flex-wrap: nowrap; } }

/* ===========================================================================
   APLICAR UNA COMPRA (rediseño 25-sep-2026)
   ---------------------------------------------------------------------------
   «La aceptación cuando cargamos inventario es complicada». Arriba, el resumen
   del comprobante y dos atajos grandes para el caso común; las líneas con el
   destino como control segmentado y solo los campos que ese destino pide; las
   automáticas plegadas; al pie, el resumen en palabras antes de aplicar.
   ======================================================================== */
.resumen-compra {
  display: flex;
  flex-direction: column;
  gap: var(--e4);
  margin: var(--e4) 0;
}
.resumen-compra-datos {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: var(--e3) var(--e4);
  margin: 0;
  padding: var(--e3) var(--e4);
  background: var(--fondo);
  border: 1px solid var(--borde);
  border-radius: var(--r-md);
}
.resumen-compra-datos > div { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.resumen-compra-datos dt {
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--texto-tenue);
}
.resumen-compra-datos dd { margin: 0; overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }
.resumen-compra-datos .fuerte dd { font-weight: 700; color: var(--acento-fuerte); }

.atajos-compra[hidden] { display: none; }
.atajos-titulo { margin: 0 0 var(--e2); font-weight: 600; }
.atajos-botones {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--e3);
}
/* Los atajos son la acción grande de la pantalla, pero NO la primaria: la
   primaria es «Aplicar». Por eso borde de acento y fondo claro, no relleno. */
.atajo-compra,
.atajo-compra-boton {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--e1);
  min-height: 64px;
  padding: var(--e3) var(--e4);
  font: inherit;
  text-align: left;
  color: var(--texto);
  background: var(--superficie);
  border: 1px solid var(--acento-borde);
  border-radius: var(--r-md);
  cursor: pointer;
}
.atajo-compra > span,
.atajo-compra-boton > span { font-size: var(--t-md); color: var(--texto-tenue); }
button.atajo-compra:hover,
.atajo-compra-boton:hover { background: var(--acento-suave); border-color: var(--acento); }
.atajo-inventario {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--e2);
  cursor: default;
}
.atajo-inventario .atajo-compra-boton { min-height: 0; padding: 0; border: 0; background: none; }
.atajo-inventario label { font-size: var(--t-sm); font-weight: 600; color: var(--texto-suave); }
.atajo-inventario select { width: 100%; }
.atajos-extra { margin: var(--e2) 0 0; }

.bloque-flete[hidden] { display: none; }

/* La tabla de líneas: un `<tbody>` por línea (con su mini formulario). */
.tabla-compra tbody.grupo-compra { border-bottom: 1px solid var(--borde); }
.tabla-compra tbody.grupo-compra > tr > td { border-bottom: none; }
.tabla-compra tbody.grupo-compra.pendiente > tr[data-linea-compra] > td:first-child {
  box-shadow: inset 3px 0 0 var(--alerta);
}
.tabla-compra tbody.grupo-compra.pendiente { background: var(--alerta-suave); }
.tabla-compra tbody.linea-automatica { background: var(--fondo); }
.tabla-compra td.der { text-align: right; }
.tabla-compra .celda-detalle-compra { min-width: 160px; }
.tabla-compra .detalle-compra { font-weight: 600; margin-right: var(--e1); }
.tabla-compra .codigo-compra { margin-top: var(--e1); }
.tabla-compra .orden-compra-linea { margin-top: var(--e1); font-size: var(--t-md); }
.tabla-compra .celda-destino { min-width: 260px; }
.tabla-compra .celda-articulo-compra { margin-top: var(--e2); max-width: 420px; }
.tabla-compra .celda-articulo-compra [hidden] { display: none; }
.tabla-compra .combo { position: relative; }
.tabla-compra .combo-campo,
.tabla-compra .celda-articulo-compra select { width: 100%; }
.tabla-compra [data-celda-bodega] { margin-top: var(--e2); }

/* El control segmentado del destino. Envuelve si no cabe: nunca se recorta. */
.segmentado-destino {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e1);
}
.segmentado-destino[hidden] { display: none; }
.segmentado-destino button {
  min-height: 40px;
  padding: 0 var(--e3);
  font: inherit;
  font-size: var(--t-md);
  font-weight: 600;
  color: var(--texto-suave);
  background: var(--superficie);
  border: 1px solid var(--boton-borde);
  border-radius: var(--r-pastilla);
  cursor: pointer;
  white-space: nowrap;
}
.segmentado-destino button:hover { background: var(--apagado); color: var(--texto); }
.segmentado-destino button[aria-checked="true"] {
  color: var(--superficie);
  background: var(--acento-fuerte);
  border-color: var(--acento-fuerte);
}
.resumen-automatica { margin: 0; font-size: var(--t-md); color: var(--texto-suave); }
.resumen-automatica[hidden] { display: none; }
.texto-error-linea { margin: var(--e1) 0 0; font-size: var(--t-md); font-weight: 600; color: var(--destructivo-hundido); }
.texto-error-linea[hidden] { display: none; }

.mas-opciones { margin-top: var(--e2); }
.mas-opciones > summary { cursor: pointer; min-height: 40px; display: flex; align-items: center; font-weight: 600; color: var(--texto-suave); }
.mas-opciones[open] > summary { margin-bottom: var(--e2); }

/* El pie: el resumen en palabras y el botón primario. */
.pie-aplicar { margin-top: var(--e4); }
.resumen-aplicar {
  margin: 0 0 var(--e3);
  padding: var(--e3) var(--e4);
  font-weight: 600;
  background: var(--acento-suave);
  border: 1px solid var(--acento-borde);
  border-radius: var(--r-md);
}
.resumen-aplicar[hidden] { display: none; }
.resumen-aplicar.falta { background: var(--alerta-suave); border-color: var(--alerta-borde); color: var(--alerta); }
.responder-al-aplicar {
  margin: 0 0 var(--e3);
  padding: var(--e2) var(--e3);
  border: 1px solid var(--info-borde);
  background: var(--info-suave);
  border-radius: var(--r-md);
}
.responder-al-aplicar .casilla { margin: 0; font-weight: 600; }
.responder-al-aplicar [hidden] { display: none; }
.pie-aplicar .boton.principal:disabled { opacity: 0.55; cursor: not-allowed; }

/* Aplicada: un solo resumen con los precios de venta adentro. */
.resumen-aplicacion > .resumen-compra-datos { margin-bottom: var(--e3); }
.precios-en-resumen { margin-top: var(--e5); padding-top: var(--e4); border-top: 1px solid var(--borde); }

@media (max-width: 760px) {
  .tabla-compra .celda-detalle-compra,
  .tabla-compra .celda-destino,
  .tabla-compra .celda-articulo-compra { min-width: 0; }
  .tabla-compra tbody.grupo-compra {
    display: block;
    margin-bottom: var(--e3);
    background: var(--superficie);
    border: 1px solid var(--borde);
    border-radius: var(--r-md);
  }
  .tabla-compra tbody.grupo-compra > tr { margin: 0; border: none; border-radius: 0; background: none; }
  .tabla-compra tbody.grupo-compra > tr[hidden] { display: none; }
  .tabla-compra td.der { text-align: left; }
  .atajos-botones { grid-template-columns: minmax(0, 1fr); }
}

/* B47 · la pestaña «Sus facturas» del proveedor: las opciones de la regla
   reusan las de «¿Qué es esta compra?» (recepcion.css, que la vista carga) y
   los campos van uno debajo del otro. */
.campo-regla { margin-top: var(--e3); max-width: 360px; }

/* B50 · «Desde foto o PDF»: el archivo a la izquierda y el formulario a la
   derecha. En el teléfono (≤ 760 px) van uno debajo del otro, la foto primero y
   acotada en alto para que el formulario no quede tres pantallas más abajo. */
.desde-archivo { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); gap: var(--e4); align-items: start; }
.desde-archivo-visor { position: sticky; top: var(--e4); }
.desde-archivo-imagen { display: block; width: 100%; height: auto; max-height: 80vh; object-fit: contain; border: 1px solid var(--borde); border-radius: var(--r-md); background: var(--superficie); }
.desde-archivo-texto { white-space: pre-wrap; word-break: break-word; max-height: 70vh; overflow: auto; font-size: var(--t-sm); background: var(--fondo); border: 1px solid var(--borde); border-radius: var(--r-md); padding: var(--e2); }
.desde-archivo-metodos { display: flex; flex-wrap: wrap; gap: var(--e2); align-items: center; }
.desde-archivo-metodos .etiqueta-sutil { color: var(--texto-suave); font-size: var(--t-sm); }
.desde-archivo-camino > summary { cursor: pointer; min-height: 44px; display: flex; align-items: center; }
.desde-archivo-descartar { display: flex; flex-wrap: wrap; gap: var(--e2); margin-top: var(--e3); }
.marca-leido { margin-left: var(--e1); vertical-align: middle; }
/* Lo leído pero sin confirmar se ve en ámbar: el color del «revíselo» del
   sistema. El texto del chip dice lo mismo, el color nunca va solo. */
input.campo-incierto, select.campo-incierto { border-color: var(--alerta-borde); background: var(--alerta-suave); }
input.campo-leido, select.campo-leido { border-color: var(--acento-borde); }
@media (max-width: 760px) {
  .desde-archivo { grid-template-columns: minmax(0, 1fr); }
  .desde-archivo-visor { position: static; }
  .desde-archivo-imagen { max-height: 45vh; }
}

/* §8 de DESIGN_PATTERNS (28-sep-2026) · FILTRAR AL CLIC Y ORDENAR POR
   ENCABEZADO. Un valor filtrable se lee igual que antes —mismo color, mismo
   chip— y dice que se puede tocar con el subrayado punteado al pasar y con el
   cursor. El que ya está filtrado lleva un anillo: «esto es lo que está
   mandando en la lista». */
a.valor-filtrable { color: inherit; text-decoration: none; cursor: pointer; }
a.valor-filtrable:hover,
a.valor-filtrable:focus-visible { text-decoration: underline dotted; text-underline-offset: 3px; }
a.valor-filtrable.chip:hover,
a.valor-filtrable.chip:focus-visible { text-decoration: none; box-shadow: 0 0 0 2px var(--acento-borde); }
a.valor-filtrable[aria-current] { box-shadow: 0 0 0 2px var(--acento); border-radius: var(--r-sm); }
a.valor-filtrable.chip[aria-current] { border-radius: var(--r-pastilla); }
/* Los iconos de los pasos (Recibidas) son enlaces: el área de toque crece sin
   cambiar el dibujo. */
.pasos-mini a.paso-mini { color: inherit; text-decoration: none; border-radius: var(--r-sm); }
.pasos-mini a.paso-mini:hover,
.pasos-mini a.paso-mini:focus-visible { background: var(--apagado); }
.pasos-mini a.paso-mini[aria-current] { box-shadow: 0 0 0 2px var(--acento); }

/* §8 · ENCABEZADOS QUE ORDENAN · 1.58.5 (09-oct-2026).
   Adán, con una captura de Artículos: «las acciones de ascender y descender en
   que quedamos». La flecha doble iba a 35 % de opacidad y a 3/4 del rótulo
   (~1,5:1 contra el gris del encabezado): no se veía que la columna ordena.
   Ahora:
   · en reposo, ↕ a tamaño completo y en `--texto-tenue` sin transparencia:
     4,3:1 sobre `--apagado`, por encima del 3:1 de un componente de
     interfaz;
   · al pasar o enfocar, el enlace se tiñe con el acento (fondo suave, texto y
     flecha en verde): dice «esto se toca» antes del clic. El relleno lateral
     se compensa con margen negativo para que el rótulo siga alineado con las
     celdas de abajo;
   · la columna ordenada (`.ordenado`, que pone el parcial junto con
     `aria-sort`) lleva ▲/▼ en el verde del acento y el rótulo en negrita.
   La flecha tiene ancho fijo: pasar de ↕ a ▲ no corre el rótulo. */
.tabla th a.orden-enlace {
  color: inherit;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 3px;
  min-height: 32px;
  padding: 0 6px;
  margin: 0 -6px;
  border-radius: var(--r-sm);
  vertical-align: middle;
  white-space: nowrap;
  transition: background-color var(--mov-rapido) var(--curva), color var(--mov-rapido) var(--curva);
}
/* Dos enlaces en el mismo encabezado (Recibidas: «Emitido» y «Subida»): sin
   esto, los márgenes negativos los montaban uno sobre otro. */
.tabla th a.orden-enlace + a.orden-enlace { margin-left: 2px; }
.tabla th a.orden-enlace:hover,
.tabla th a.orden-enlace:focus-visible {
  background: var(--acento-suave);
  color: var(--acento-fuerte);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.tabla th.der a.orden-enlace { justify-content: flex-end; }
.orden-flecha {
  display: inline-block;
  width: 1em;
  font-size: 1em;
  line-height: 1;
  text-align: center;
  color: var(--acento-fuerte);
}
.orden-flecha.tenue { color: var(--texto-tenue); }
.tabla th a.orden-enlace:hover .orden-flecha.tenue,
.tabla th a.orden-enlace:focus-visible .orden-flecha.tenue { color: var(--acento-fuerte); }
.tabla th a.orden-enlace.ordenado { color: var(--texto); font-weight: 700; }
.tabla th a.orden-enlace.ordenado:hover,
.tabla th a.orden-enlace.ordenado:focus-visible { color: var(--acento-fuerte); }
/* Todos los rótulos en la misma línea, con o sin enlace de orden. Con
   `vertical-align: top` (lo de `.tabla th`), el enlace de 32 px centraba su
   texto más abajo que el rótulo suelto de «Costo prom.», «Ubicación» o
   «Acciones», y la fila de encabezados se veía escalonada. Centrados los dos,
   comparten línea base. */
.tabla thead th { vertical-align: middle; line-height: 1.25; }

.filtros-activos { align-items: center; }
/* El ✕ separado del texto: en un inline-flex el espacio entre los dos se pierde. */
.chips-estado.filtros-activos a { gap: 6px; }
.filtros-activos-rotulo { color: var(--texto-suave); font-size: var(--t-sm); font-weight: 600; }
.chips-estado a.filtros-activos-todos { background: transparent; border-style: dashed; }

/* ---------------------------------------------------------------------------
   KIWIERP-20 · «Envíos al cliente» en una línea
   ---------------------------------------------------------------------------
   El estado del último envío es UNA línea con su chip; la explicación va en
   «¿Cómo funciona?» y la tabla de intentos detrás de «Ver los envíos». */
/* El flex es SOLO para chip + texto: el texto va en un span propio
   (`.estado-linea-texto`) y se parte dentro de él. Con cada trozo de texto
   suelto como elemento flex, el gap separaba «Cliente (», el enlace y «)». */
.estado-envio,
.estado-linea { display: flex; align-items: baseline; gap: var(--e2); margin: 0 0 var(--e3); }
.estado-envio > .chip,
.estado-linea > .chip { flex: none; }
.estado-linea-texto { flex: 1 1 auto; min-width: 0; }
/* Un dato que solo se pinta en la tarjeta del teléfono (movil.css §2): en la
   tabla de escritorio ya tiene su columna. */
.solo-en-tarjeta { display: none; }
.envios-historial,
.reenvio-otros-correos { margin-top: var(--e3); }
.envios-historial > summary,
.reenvio-otros-correos > summary {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--texto-suave);
  cursor: pointer;
}
.envios-historial > summary:hover,
.reenvio-otros-correos > summary:hover { color: var(--acento-fuerte); text-decoration: underline; }
/* 1.66.0 · el reenvío a otros correos: el formulario no ocupa todo el ancho. */
.reenvio-otros-correos > .formulario { max-width: 40rem; }
.aviso > .acciones-fila { margin-top: var(--e2); }

/* KIWIERP-20 · archivos del comprobante y reintento, junto al estado de Hacienda. */
.estado-archivos { margin: 0 0 var(--e4); }
.estado-archivos > .archivos-emitido { margin: 0 0 var(--e2); }

/* 1.37.4 · Un grupo de botones suelto entre bloques (el «Emitir de nuevo como
   factura nueva» de una rechazada) lleva el mismo aire que un `.aviso` o que
   `.estado-archivos`: antes quedaba pegado a «XML firmado · Respuesta · PDF». */
.acciones-bloque { margin: var(--e2) 0 var(--e5); }

/* Lo que dijo Hacienda, un error por renglón con su código adelante. */
.mensaje-hacienda { margin: var(--e2) 0 0; padding: 0; list-style: none; }
.aviso-grupo-lista .mensaje-hacienda li { padding: var(--e1) 0; border-top: 0; }
.mensaje-hacienda-codigo { display: inline-block; min-width: 4ch; font-weight: 600; }

/* ---------------------------------------------------------------------------
   KIWIERP-24 · Anterior y siguiente en la ficha del artículo
   ---------------------------------------------------------------------------
   Las flechas van en el encabezado, junto al nombre. La apagada ocupa el mismo
   lugar que la encendida: el encabezado no salta del primero al segundo. */
.encabezado-ficha-articulo {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--e2) var(--e4);
}
.encabezado-ficha-articulo h1 { flex: 1 1 24ch; min-width: 0; margin: 0; overflow-wrap: anywhere; }
.ficha-navegar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--e2); }
.ficha-navegar .boton { min-height: 44px; }
.ficha-flecha { min-width: 44px; padding: var(--e2); }
.ficha-flecha[aria-disabled="true"] {
  color: var(--texto-tenue);
  background: var(--apagado);
  border-color: var(--borde);
  cursor: not-allowed;
  opacity: 0.6;
}
.ficha-posicion { color: var(--texto-suave); font-size: var(--t-sm); white-space: nowrap; }
@media (max-width: 760px) {
  /* En el teléfono las flechas van a los extremos y «Comparar con…» al medio:
     el pulgar llega a las dos sin cruzar la pantalla. */
  .ficha-navegar { width: 100%; justify-content: space-between; }
  .ficha-comparar .solo-ancho { display: none; }
}

/* ---------------------------------------------------------------------------
   KIWIERP-25 · Comparar artículos lado a lado
   ---------------------------------------------------------------------------
   Tabla propia, sin `.tabla`: en el teléfono `.tabla` se apila en tarjetas y
   una comparación apilada no compara. El desplazamiento de lado vive dentro
   del envoltorio (la página no se mueve) y la columna del dato queda fija. */
.comparar-herramientas {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e2) var(--e3);
  margin-bottom: var(--e3);
}
.comparar-herramientas .boton { min-height: 44px; }
.comparar-envoltorio { max-height: none; }
.tabla-comparar {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: var(--t-tabla);
}
.tabla-comparar th,
.tabla-comparar td {
  padding: var(--e2) var(--e3);
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--borde);
  background: var(--superficie);
}
.tabla-comparar td.der { text-align: right; }
.tabla-comparar thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--apagado);
  border-bottom: 1px solid var(--borde-fuerte);
  min-width: 160px;
  max-width: 260px;
}
/* La primera columna, fija al desplazar de lado. */
.tabla-comparar .comparar-dato {
  position: sticky;
  left: 0;
  z-index: 1;
  width: 150px;
  min-width: 120px;
  max-width: 180px;
  font-weight: 600;
  color: var(--texto-suave);
  background: var(--apagado);
  border-right: 1px solid var(--borde-fuerte);
}
.tabla-comparar thead .comparar-dato { z-index: 3; }
.comparar-articulo { display: table-cell; }
.comparar-articulo .comparar-nombre {
  display: block;
  font-weight: 600;
  color: var(--texto);
  white-space: normal;
  overflow-wrap: anywhere;
}
.comparar-articulo .mono { display: block; margin-bottom: var(--e1); }
.comparar-quitar { min-height: 44px; }
/* El rótulo del grupo ocupa toda la fila: lo que se pega a la izquierda es su
   texto (una celda del ancho de la tabla no tiene dónde pegarse). */
.comparar-grupo-rotulo { position: sticky; left: var(--e3); display: inline-block; }
.tabla-comparar .comparar-grupo th {
  font-family: var(--fuente-titulo);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--texto-tenue);
  background: var(--superficie);
  padding-top: var(--e4);
}
.tabla-comparar tr[hidden] { display: none; }
/* Lo que difiere se nota con un fondo suave Y con la palabra «Difiere». */
.tabla-comparar .fila-difiere td { background: var(--info-suave); }
.comparar-difiere {
  display: block;
  margin-top: 2px;
  font-size: var(--t-xs);
  font-weight: 600;
  color: var(--info);
}
/* El mejor valor: borde, icono y rótulo (nunca solo el color). */
.tabla-comparar td.mejor-valor { box-shadow: inset 3px 0 0 var(--acento); }
.insignia-mejor {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-left: var(--e1);
  font-size: var(--t-xs);
  font-weight: 600;
  color: var(--acento-fuerte);
  white-space: nowrap;
}
.comparar-agregar { margin-top: var(--e4); }
.comparar-resultados li { gap: var(--e3); justify-content: space-between; }
.comparar-resultados .boton { min-height: 44px; flex-shrink: 0; }
@media (max-width: 760px) {
  /* En el teléfono `.tabla-envoltorio` pierde el desplazamiento (las `.tabla`
     se vuelven tarjetas). La comparación sí lo necesita: se desplaza ADENTRO
     del envoltorio y la página queda quieta. */
  .tabla-envoltorio.comparar-envoltorio {
    overflow-x: auto;
    overflow-y: visible;
    background: var(--superficie);
    border: 1px solid var(--borde);
  }
  .tabla-comparar .comparar-dato { width: 110px; min-width: 96px; max-width: 120px; }
  .tabla-comparar thead th { min-width: 140px; }
  .tabla-comparar td.der { text-align: left; }
  .insignia-mejor { display: flex; margin-left: 0; margin-top: 2px; }
}

/* ==========================================================================
   KIWIERP-30 · Guías rápidas del manual y capturas de pantalla
   --------------------------------------------------------------------------
   La portada de /ayuda tiene dos mitades: arriba las guías (una tarea cada
   una, agrupadas) y abajo el manual completo. Las capturas las pinta
   `src/util/markdown.js` como <figure class="captura">: ocupan el ancho del
   texto, con borde para que no se confundan con la página, y las del teléfono
   (nombre terminado en `-movil`) van angostas, que es como se ven de verdad.
   ========================================================================== */
.ayuda-portada { max-width: none; }
.ayuda-portada > section + section { margin-top: var(--e6, 2.5rem); }
.ayuda-guias-grupo { margin-top: var(--e4); }
.ayuda-guias-grupo h3 {
  margin: 0 0 var(--e2);
  font-size: var(--t-sm);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--texto-suave);
}
.ayuda-guias {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 16rem), 1fr));
  gap: var(--e2);
}
.ayuda-guias > li {
  display: flex;
  flex-direction: column;
  gap: var(--e1, 0.25rem);
  padding: var(--e3);
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: 8px;
}
.ayuda-guias > li > a { font-weight: 600; text-decoration: none; }
.ayuda-guias > li > a:hover { text-decoration: underline; }
.ayuda-guias > li > .sutil { font-size: var(--t-md); }
.ayuda-tarjetas h3 { font-size: 1.0625rem; margin-top: 0; }
.ayuda-tarjetas h3 a { text-decoration: none; }
.ayuda-tarjetas h3 a:hover { text-decoration: underline; }

.ayuda-articulo figure.captura { margin: var(--e3) 0 var(--e4); }
.ayuda-articulo figure.captura img {
  display: block;
  max-width: 100%;
  height: auto;
  border: 1px solid var(--borde-fuerte);
  border-radius: 8px;
  background: var(--superficie);
}
.ayuda-articulo figure.captura img[src$="-movil.webp"] { max-width: min(100%, 20rem); }

/* En el teléfono el índice lateral queda ARRIBA del contenido. En la portada
   las tarjetas ya son el índice: ahí solo se deja el buscador. En un capítulo
   se lee primero el capítulo y el índice queda al final. */
@media (max-width: 900px) {
  .ayuda-indice-portada .ayuda-lista,
  .ayuda-indice-portada .ayuda-indice-titulo { display: none; }
  .ayuda-armazon > .ayuda-indice:not(.ayuda-indice-portada) { order: 2; }
}

/* ---------------------------------------------------------------------------
   KIWIERP-26 · Listas de precios
   ---------------------------------------------------------------------------
   El formulario de la regla va al lado de su vista previa en escritorio y
   encima de ella en el teléfono: la vista previa es lo que contesta «¿esto
   da el precio que quiero?», y tiene que estar a la vista mientras se escribe. */
.lp-config { display: grid; gap: var(--e4); align-items: start; }
@media (min-width: 1100px) {
  .lp-config { grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); }
  .lp-previa { position: sticky; top: calc(56px + var(--e4)); }
}
.lp-reglas { border: 0; padding: 0; margin: 0 0 var(--e4); display: grid; gap: var(--e2); }
@media (min-width: 761px) { .lp-reglas { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.lp-opcion {
  display: flex;
  gap: var(--e2);
  align-items: flex-start;
  min-height: 44px;
  padding: var(--e3);
  border: 1px solid var(--borde-fuerte);
  border-radius: 8px;
  background: var(--superficie);
  cursor: pointer;
}
.lp-opcion:has(input:checked) { border-color: var(--acento); background: var(--acento-suave); }
.lp-opcion input { margin-top: 3px; accent-color: var(--acento-fuerte); }
.lp-opcion > span { display: flex; flex-direction: column; gap: var(--e1); }
.lp-regla { border: 1px solid var(--borde); border-radius: 8px; padding: var(--e3) var(--e4); margin: 0; }
.lp-regla[hidden] { display: none; }
.lp-regla > legend { font-weight: 600; padding: 0 var(--e1); }
.lp-ejemplo {
  margin-top: var(--e3);
  padding: var(--e3);
  border-left: 3px solid var(--info-borde);
  background: var(--info-suave);
  font-size: var(--t-md);
}
.lp-ejemplo p, .lp-ejemplo ul { margin: 0; }
.lp-ejemplo ul { padding-left: var(--e5); margin-top: var(--e1); }
.lp-rotulo { display: block; font-weight: 600; margin-bottom: var(--e1); }
.lp-previa-regla { margin: 0 0 var(--e2); font-size: var(--t-md); }
.lp-previa-lista { list-style: none; margin: 0; padding: 0; }
.lp-previa-lista li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--e1) var(--e3);
  padding: var(--e2) 0;
  border-top: 1px solid var(--borde);
}
.lp-previa-articulo { overflow-wrap: anywhere; }
.lp-previa-cuenta { grid-column: 1 / -1; font-size: var(--t-sm); }
/* La cuenta en palabras debajo del chip: se lee sin abrir nada. */
.lp-cuenta { display: block; font-size: var(--t-sm); margin-top: var(--e1); max-width: 60ch; }
.lp-editar { white-space: nowrap; }
.lp-editar input { width: 9rem; min-height: 44px; }
.lp-editar .boton { min-height: 44px; }
/* El triángulo del <details> se conserva (display: list-item): es lo que dice
   que se abre. El h2 va en línea con él. */
.lp-bitacora > summary { cursor: pointer; min-height: 44px; display: list-item; padding: var(--e2) 0; }
.lp-bitacora > summary h2 { display: inline; margin: 0; }
.lp-bitacora-lista { list-style: none; margin: var(--e2) 0 0; padding: 0; }
.lp-bitacora-lista li { display: flex; flex-wrap: wrap; gap: var(--e1) var(--e3); padding: var(--e1) 0; border-top: 1px solid var(--borde); }
@media (max-width: 760px) {
  .lp-editar { white-space: normal; }
  .lp-editar input { width: 100%; }
}
/* 1330 · los tres costos de la ficha (promedio, última compra, teórico), como
   en el sistema de Grudelsa, y el costo teórico editable debajo. */
.lp-costos { margin: 0 0 var(--e4); padding: var(--e3); border: 1px solid var(--borde); border-radius: 8px; background: var(--superficie); }
.lp-costos-lista { display: flex; flex-wrap: wrap; gap: var(--e3) var(--e6); margin: 0; }
.lp-costos-lista > div { display: flex; flex-direction: column; gap: var(--e1); }
.lp-costos-lista dt { font-size: var(--t-sm); color: var(--texto-tenue); }
.lp-costos-lista dd { margin: 0; }
.lp-costo-teorico { display: flex; flex-wrap: wrap; align-items: center; gap: var(--e2); margin-top: var(--e3); }
.lp-costo-teorico label { font-weight: 600; }
.lp-costo-teorico input { width: 10rem; min-height: 44px; }
.lp-costo-teorico .boton { min-height: 44px; }
.lp-costo-teorico .ayuda { flex-basis: 100%; margin: 0; }
.lp-impacto-ejemplos { margin: var(--e2) 0 0; padding-left: var(--e5); }
.lp-confirmar { display: flex; gap: var(--e2); align-items: center; margin-bottom: var(--e3); font-weight: 600; }
.lp-regla [data-modo-campo][hidden] { display: none; }
@media (max-width: 760px) {
  .lp-costo-teorico input { width: 100%; }
}
/* KIWIERP-26 · vista previa de la carga por Excel: el resumen en cifras y los
   cambios bruscos resaltados (texto + color, nunca solo color). */
.lp-resumen-carga { list-style: none; margin: 0 0 var(--e4); padding: 0; display: flex; flex-wrap: wrap; gap: var(--e2); }
.lp-resumen-carga li {
  display: flex; align-items: baseline; gap: var(--e1);
  padding: var(--e2) var(--e3); border: 1px solid var(--borde); border-radius: 8px; background: var(--superficie);
}
.lp-resumen-carga li strong { font-size: var(--t-base); }
.lp-resumen-carga li.lp-alerta { border-color: var(--destructivo-borde); background: var(--destructivo-suave); color: var(--destructivo-hundido); }
@media (min-width: 761px) {
  .tabla tr.lp-fila-brusca > td { background: var(--destructivo-suave); }
  .lp-rotulo-tarjeta { display: none; }
}
.lp-confirmar .casilla { margin-bottom: var(--e3); }

/* ===========================================================================
   KIWIERP-37 · Clasificaciones de artículo (fabricante, marca, familia y
   proveedor habitual) y metas de venta
   ---------------------------------------------------------------------------
   Bloque propio al final: la ficha, la lista, la administración de los
   catálogos y las pantallas de metas. Solo tokens del tema. */

/* La ficha: «Clasificación» es un subtítulo dentro de «Identificación». */
.subtitulo-ficha { margin: var(--e5) 0 var(--e1); font-size: var(--t-base); }
.rejilla-clasificacion { align-items: start; }
.campo-clasificacion select { width: 100%; }
.buscador-clasificacion { display: flex; flex-direction: column; gap: var(--e1); margin-bottom: var(--e1); }
.buscador-clasificacion .buscar-clasificacion { width: 100%; min-height: 44px; }
.crear-clasificacion { display: flex; flex-wrap: wrap; gap: var(--e1); }
.crear-clasificacion:empty { display: none; }
.crear-clasificacion .boton { min-height: 44px; white-space: normal; text-align: left; }

/* La lista: la subfamilia se lee al lado de su familia, en gris. */
.celda-familia { white-space: nowrap; }

/* Administración de los catálogos: el «Editar» de cada fila despliega sus
   formularios (renombrar, fabricante de la marca, activar, fusionar). */
.editar-clasificacion > summary { list-style: none; min-height: 44px; display: inline-flex; align-items: center; }
.editar-clasificacion > summary::-webkit-details-marker { display: none; }
.editar-clasificacion-panel {
  display: flex;
  flex-direction: column;
  gap: var(--e2);
  margin-top: var(--e2);
  padding: var(--e3);
  background: var(--apagado);
  border: 1px solid var(--borde);
  border-radius: var(--r-md);
  text-align: left;
  min-width: 280px;
}
.editar-clasificacion-panel form { display: flex; flex-wrap: wrap; align-items: center; gap: var(--e2); }
.editar-clasificacion-panel input[type="text"],
.editar-clasificacion-panel select { flex: 1 1 160px; min-height: 44px; }
.lista-compacta { margin: var(--e1) 0; padding-left: var(--e5); }

/* Metas: la barra de avance dentro de la tabla. Una sola serie, delgada y
   neutra (el estado lo dicen el chip con icono y el porcentaje escrito). */
.avance-meta { display: flex; align-items: center; gap: var(--e2); min-width: 140px; }
.avance-meta .pp-barra { height: 6px; }
.avance-meta-cifra { font-variant-numeric: tabular-nums; color: var(--texto-suave); white-space: nowrap; }
.avance-meta-grande .pp-barra { height: 10px; }
.chip-con-icono { display: inline-flex; align-items: center; gap: var(--e1); }
/* El icono ya dice el estado: el punto de color de `.chip` sobra. */
.chip-con-icono::before { display: none; }
.tabla-metas .fila-grupo-meta th,
.tabla-metas .fila-grupo-meta td { background: var(--apagado); font-weight: 600; }
.tabla-metas .fila-sin-meta td { color: var(--texto-suave); }
.sangria-meta { padding-left: var(--e4); display: inline-block; }
.ritmo-metas {
  margin: var(--e2) 0 var(--e3);
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--e2);
}
.ritmo-metas p { margin: 0; }
/* La semana laboral: una línea chica al lado del ritmo, no un campo de ancho
   completo (se cambia una vez y no es lo que se viene a mirar). */
.form-semana-laboral { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--e2); }
.form-semana-laboral select { width: auto; min-height: 44px; }
.form-semana-laboral .boton { min-height: 44px; }
/* El nombre del vendedor (y la explicación de la fila de notas) no se aprieta
   hasta ser una palabra por renglón. */
.tabla-metas td:first-child, .tabla-metas th:first-child { min-width: 8.5rem; }
.tabla-metas .avance-meta { min-width: 116px; }
/* El rótulo largo de la columna informativa se parte en renglones en vez de
   estirar la tabla. */
.tabla-metas th.col-devoluciones { white-space: normal; width: 8rem; }
.tabla-cuadricula-metas th[scope="row"] { white-space: nowrap; }
/* Una celda tiene que mostrar «2 000 000» entero: con doce meses la tabla se
   desplaza de lado dentro de su envoltorio antes que cortar los montos. */
.tabla-cuadricula-metas td { min-width: 8.5rem; padding-left: var(--e1); padding-right: var(--e1); }
.celda-meta { width: 100%; min-height: 44px; text-align: right; font-variant-numeric: tabular-nums; }
.filtro-periodo-metas input[type="month"] { min-height: 44px; font: inherit; }
.copiar-metas > summary { min-height: 44px; display: flex; align-items: center; cursor: pointer; }

/* «Mi meta»: pensada para el teléfono. */
.mi-meta-tarjeta {
  padding: var(--e4);
  border: 1px solid var(--borde);
  border-radius: var(--r-lg);
  background: var(--superficie);
  margin-bottom: var(--e4);
}
.mi-meta-nombre { margin: 0; color: var(--texto-suave); }
.mi-meta-cifra { margin: var(--e1) 0 var(--e2); display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--e2); }
.mi-meta-tarjeta .mi-meta-cifra strong { font-size: var(--t-xl); }
.mi-meta-datos { margin-top: var(--e3); }
.lista-mi-meta { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--e3); }
.lista-mi-meta li { padding-bottom: var(--e3); border-bottom: 1px solid var(--borde); }
.lista-mi-meta-cabeza { display: flex; justify-content: space-between; align-items: center; gap: var(--e2); flex-wrap: wrap; }
.lista-mi-meta p { margin: var(--e1) 0; }

/* ==========================================================================
   KIWIERP-32 · Central de Kiwi: soporte, transparencia y panel de la Central
   ========================================================================== */

/* Aviso de versión nueva: discreto si es opcional, visible si es obligatoria. */
.banda-version {
  display: flex;
  align-items: center;
  gap: var(--e2);
  padding: var(--e1) var(--e4);
  background: var(--info-suave);
  border-bottom: 1px solid var(--info-borde);
  color: var(--info);
  font-size: var(--t-md);
}
.banda-version.obligatoria {
  background: var(--alerta-suave);
  border-bottom-color: var(--alerta-borde);
  color: var(--texto);
  padding: var(--e2) var(--e4);
}

/* 15-oct-2026 · «La sesión cambió»: la pestaña vieja avisa antes de guardar
   (`sesion-vigilante.js`). Mismo tono que el aviso obligatorio de versión. */
.banda-sesion {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e2);
  padding: var(--e2) var(--e4);
  background: var(--alerta-suave);
  border-bottom: 1px solid var(--alerta-borde);
  color: var(--texto);
  font-size: var(--t-md);
}
.banda-sesion .boton { margin-left: auto; }

/* Formulario del tiquete: los tres tipos como opciones grandes, tocables. */
.soporte-formulario { max-width: 760px; }
.soporte-tipos {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--e2);
  margin: 0 0 var(--e4);
  padding: 0;
  border: 0;
}
.soporte-tipos legend { grid-column: 1 / -1; margin-bottom: var(--e2); font-weight: 600; }
.soporte-tipos .error-campo { grid-column: 1 / -1; }
.soporte-tipo {
  display: flex;
  align-items: center;
  gap: var(--e2);
  min-height: 44px;
  margin: 0;
  padding: var(--e2) var(--e3);
  border: 1px solid var(--borde-fuerte);
  border-radius: var(--r-md);
  background: var(--superficie);
  cursor: pointer;
  font-weight: 500;
}
.soporte-tipo:has(input:checked) { border-color: var(--acento); background: var(--acento-suave); }
.soporte-tipo input { margin: 0; }
.soporte-adjunto {
  margin: var(--e4) 0;
  padding: var(--e3);
  border: 1px dashed var(--borde-fuerte);
  border-radius: var(--r-md);
  background: var(--fondo);
}
.soporte-adjunto > summary { min-height: 44px; display: flex; align-items: center; cursor: pointer; font-weight: 600; }
.soporte-consentimiento {
  display: flex;
  align-items: flex-start;
  gap: var(--e2);
  margin: var(--e3) 0;
  color: var(--texto-suave);
  font-size: var(--t-md);
}

/* Conversación de un tiquete (instalación y Central). */
.soporte-conversacion { list-style: none; margin: 0 0 var(--e4); padding: 0; display: grid; gap: var(--e3); }
.soporte-mensaje {
  max-width: 72ch;
  padding: var(--e3);
  border: 1px solid var(--borde);
  border-radius: var(--r-md);
  background: var(--superficie);
}
.soporte-mensaje.de-kiwi { border-left: 4px solid var(--acento); background: var(--acento-suave); }
.soporte-mensaje.de-usuario { margin-left: auto; }
.soporte-mensaje-autor { margin: 0 0 var(--e1); color: var(--texto-suave); font-size: var(--t-md); }
.soporte-mensaje-autor .chip { margin-left: var(--e1); }
.soporte-texto,
.quiebre.soporte-texto { white-space: pre-wrap; margin: 0; word-break: normal; overflow-wrap: anywhere; }
.quiebre.soporte-resumen,
.soporte-resumen {
  word-break: normal;
  overflow-wrap: anywhere;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  max-width: 48ch;
}
.soporte-responder { max-width: 72ch; }
.soporte-alcance { display: flex; flex-wrap: wrap; gap: var(--e2); margin: 0 0 var(--e3); }
.soporte-alcance .chip { min-height: 44px; display: inline-flex; align-items: center; text-decoration: none; }

/* Transparencia: el JSON tal cual salió, con su propia barra si es largo. */
.soporte-json { max-height: 420px; overflow: auto; font-size: var(--t-sm); }
.soporte-enviado { border-bottom: 1px solid var(--borde); padding: var(--e2) 0; }
.soporte-enviado > summary {
  min-height: 44px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e1);
  cursor: pointer;
}

/* Panel de la Central. */
.soporte-codigo { font-size: var(--t-xl); letter-spacing: 0.08em; font-weight: 700; }
.soporte-versiones { align-items: start; }
.soporte-desde-error { margin: var(--e4) 0 0; }
.soporte-acceso { margin-top: var(--e5); }

/* Ronda 3 (N1): sin Central ni correo el tiquete no sale; se dice a qué
   correo escribir. El aviso lleva el icono al costado y párrafos propios. */
.aviso.soporte-sin-canal { display: flex; align-items: flex-start; gap: var(--e2); color: var(--texto); }
.aviso.soporte-sin-canal .icono { flex: none; margin: 2px 0 0; color: var(--alerta); }
.soporte-sin-canal p { margin: 0 0 var(--e2); overflow-wrap: anywhere; }
.soporte-sin-canal p:last-child { margin-bottom: 0; }
.soporte-sin-canal .boton { max-width: 100%; white-space: normal; }
.soporte-fecha-correo { font-size: var(--t-sm); }
.banda-version-origen { font-weight: 600; margin-right: var(--e1); }

/* Revisión de seguridad A3: la confirmación con contraseña + código, la
   publicación en espera (con su «Cancelar») y las acciones plegadas. */
.central-reautenticar {
  margin: var(--e3) 0 0;
  padding: var(--e3);
  border: 1px solid var(--alerta-borde);
  border-radius: var(--r-md);
  background: var(--alerta-suave);
}
.central-reautenticar legend { font-weight: 600; padding: 0 var(--e1); }
.central-espera {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e2);
  margin: 0 0 var(--e3);
  padding: var(--e2) var(--e3);
  border: 1px solid var(--alerta-borde);
  border-radius: var(--r-md);
  background: var(--alerta-suave);
}
.central-espera > span { flex: 1 1 280px; min-width: 0; overflow-wrap: anywhere; }
.central-accion { margin: var(--e3) 0; }
.central-accion > summary,
.central-kit > summary { min-height: 44px; display: flex; align-items: center; cursor: pointer; font-weight: 600; }
.central-kit { margin: var(--e3) 0; }

@media (max-width: 760px) {
  .banda-version, .banda-sesion { padding: var(--e1) var(--e3); }
  .soporte-mensaje.de-usuario { margin-left: 0; }
  .soporte-resumen { max-width: none; }
}

/* KIWIERP-38 (migración 1260) · autorizaciones al vender: el aviso de la
   factura retenida, el modal del PIN, la cola y la página del enlace. */
.aviso-autorizacion > p { margin: 0 0 var(--e2); }
.aviso-autorizacion > :last-child { margin-bottom: 0; }
.motivos-autorizacion { margin: 0 0 var(--e3); padding-left: var(--e4); }
.motivos-autorizacion li { margin-bottom: var(--e1); }
.acciones-autorizacion { display: flex; flex-wrap: wrap; gap: var(--e2); margin-top: var(--e2); }
.acciones-autorizacion .boton { min-height: 44px; }
.resolver-autorizacion { display: grid; gap: var(--e3); grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.modal-autorizacion h3 { margin: var(--e4) 0 var(--e2); }
.modal-autorizacion h3:first-of-type { margin-top: var(--e2); }
.modal-autorizacion input[name="pin"] { letter-spacing: 0.3em; font-size: var(--t-lg, 1.125rem); }
.historial-autorizacion { margin: var(--e2) 0 0; padding-left: var(--e4); }
.formulario-autorizar { display: grid; gap: var(--e2); margin-top: var(--e4); }
.formulario-autorizar .boton { min-height: 44px; width: 100%; }
.lineas-autorizacion { padding-left: var(--e4); }

/* 29-sep-2026 · campos de número (public/js/campos-numero.js). Debajo del campo:
   el error de lectura, con el mismo tono que .error-campo, y el monto que el
   sistema entendió con su moneda («Se guarda ₡2,500.00»), para que nadie guarde
   ₡2.50 creyendo que eran ₡2,500. En una celda angosta (editor de líneas) va en
   bloque y parte la línea, nunca empuja la tabla de lado. */
.campo-numero-nota {
  display: block;
  margin: var(--e1) 0 0;
  font-size: var(--t-sm, 0.8125rem);
  line-height: 1.35;
  overflow-wrap: anywhere;
  text-align: left;
}
.campo-numero-error { color: var(--destructivo-hundido); font-weight: 600; }
.campo-numero-entendido { color: var(--texto-suave, inherit); font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------------------------
   KIWIERP-40 · grupo de empresas (migración 1300)
   El candado de los campos que manda la sociedad maestra en la ficha de una
   hija, la lista de cambios de la bitácora del grupo y la carga inicial.
   ------------------------------------------------------------------------ */
.grupo-candado {
  font-weight: 400;
  font-size: var(--t-md);
  color: var(--texto-tenue);
}
/* Un campo de solo lectura no puede parecer editable: fondo apagado y sin el
   borde de foco del resto del formulario. */
#form-articulo input[readonly],
#form-articulo textarea[readonly] {
  background: var(--apagado);
  color: var(--texto-suave);
}
.grupo-aviso { margin-bottom: var(--e4); }
.grupo-cambios {
  list-style: none;
  margin: var(--e1) 0 0;
  padding: 0;
  font-size: var(--t-md);
}
.grupo-cambios li { overflow-wrap: anywhere; }
.grupo-bitacora td.nowrap { white-space: nowrap; }
.grupo-aplicar .acciones-formulario { display: flex; flex-wrap: wrap; gap: var(--e2); }
.grupo-aplicar .boton { min-height: 44px; }
/* 1330 · de dónde sale el precio de la línea: la lista del cliente o, si se
   cambió a mano, «Precio manual» (texto + color, nunca solo color). */
.precio-origen { display: block; margin-top: var(--e1); font-size: var(--t-sm); color: var(--texto-tenue); white-space: normal; max-width: 100%; overflow-wrap: anywhere; }
.precio-origen.manual { color: var(--alerta); font-weight: 600; }
.precio-origen[hidden] { display: none; }

/* 1420 · «Precio deseado»: el botón bajo el precio de la línea (nombre corto
   de la lista o «Manual», en ámbar como la leyenda), el panel con las listas
   y el control «Precio de las líneas» del documento. */
.precio-lista-boton {
  display: inline-flex;
  align-items: center;
  gap: var(--e1);
  margin-top: var(--e1);
  padding: 2px var(--e2);
  min-height: 28px;
  max-width: 100%;
  border: 1px solid var(--borde);
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--texto-suave);
  font: inherit;
  font-size: var(--t-sm);
  white-space: nowrap;
  cursor: pointer;
}
.precio-lista-boton span { overflow: hidden; text-overflow: ellipsis; }
.precio-lista-boton:hover { border-color: var(--borde-fuerte); background: var(--apagado); }
.precio-lista-boton[aria-expanded="true"] { border-color: var(--acento); background: var(--acento-suave); }
.precio-lista-boton.manual { color: var(--alerta); border-color: var(--alerta-borde); background: var(--alerta-suave); font-weight: 600; }
.precio-lista-boton[hidden] { display: none; }

.precio-selector {
  position: fixed;
  z-index: 60;
  width: min(320px, calc(100vw - 16px));
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--r-md);
  box-shadow: var(--elev-2);
}
.precio-selector[hidden] { display: none; }
.precio-selector-cabeza { display: grid; gap: 2px; padding: var(--e2) var(--e3); border-bottom: 1px solid var(--borde); }
.precio-selector-sub { font-size: var(--t-xs); color: var(--texto-tenue); overflow-wrap: anywhere; }
.precio-selector-lista { list-style: none; margin: 0; padding: var(--e1) 0; max-height: 280px; overflow-y: auto; }
.precio-selector-opcion {
  display: flex;
  align-items: baseline;
  gap: var(--e2);
  min-height: 44px;
  padding: var(--e2) var(--e3);
  cursor: pointer;
}
.precio-selector-opcion:hover,
.precio-selector-opcion:focus { background: var(--acento-suave); }
/* La opción con el foco lleva anillo además del fondo (alto contraste); la
   elegida, el texto en negrita. */
.precio-selector-opcion:focus { outline: 2px solid var(--acento); outline-offset: -2px; }
.precio-selector-opcion[aria-selected="true"] .precio-selector-nombre { font-weight: 700; }
.precio-selector-opcion[aria-disabled="true"] { cursor: not-allowed; color: var(--texto-tenue); }
.precio-selector-opcion[aria-disabled="true"]:hover { background: none; }
/* El nombre con la regla debajo, y el precio con el margen debajo (estos dos
   solo para quien ve costos). `min-width: 0` deja que la regla se parta en
   el teléfono en vez de empujar el precio fuera del panel. */
.precio-selector-texto { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.precio-selector-regla { font-size: var(--t-xs); color: var(--texto-tenue); overflow-wrap: anywhere; }
.precio-selector-cifras { flex: 0 0 auto; display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }
.precio-selector-margen { font-size: var(--t-xs); color: var(--texto-tenue); font-variant-numeric: tabular-nums; white-space: nowrap; }
.precio-selector-margen.bajo-costo { color: var(--alerta); font-weight: 600; }
.precio-selector-marca { font-size: var(--t-xs); color: var(--texto-tenue); }
.precio-selector-monto { font-family: var(--fuente-mono); font-variant-numeric: tabular-nums; white-space: nowrap; }
.precio-selector-opcion[aria-disabled="true"] .precio-selector-monto { font-family: inherit; font-style: italic; }

.precio-documento { display: flex; flex-wrap: wrap; align-items: center; gap: var(--e1) var(--e2); margin-bottom: var(--e2); }
.precio-documento[hidden] { display: none; }
.precio-documento-rotulo { font-weight: 600; }
.precio-documento .ayuda { margin: 0; font-size: var(--t-sm); color: var(--texto-tenue); }
.precio-documento kbd { font-family: var(--fuente-mono); font-size: 0.9em; padding: 0 4px; border: 1px solid var(--borde); border-radius: var(--r-sm); }
.precio-documento-aviso { flex-basis: 100%; display: flex; flex-wrap: wrap; align-items: center; gap: var(--e2); margin: 0; }
.precio-documento-aviso[hidden] { display: none; }

/* En el teléfono el panel es hoja inferior a todo el ancho, como el menú de
   las filas; botones de 44 px. */
@media (max-width: 600px) {
  .precio-selector {
    top: auto !important;
    left: 0 !important;
    right: 0;
    bottom: 0;
    width: 100%;
    border-radius: var(--r-lg) var(--r-lg) 0 0;
  }
  .precio-selector-lista { max-height: 60vh; }
  .precio-lista-boton { min-height: 44px; }
}

/* Tarjeta de «cliente seleccionado» en los formularios de venta
   (`parciales/cliente-documento.ejs`, 02-oct-2026). Mismo borde y radio que
   los campos del formulario: es el campo Cliente ya respondido, no un aviso.
   El nombre es el dato principal; cédula, correo y teléfono en gris debajo;
   lo tomado de la ficha en chips (§3: siempre con texto). */
.cliente-documento-rotulo {
  margin: var(--e3) 0 var(--e1);
  font-size: var(--t-md);
  font-weight: 600;
  color: var(--texto-suave);
}
.tarjeta-cliente {
  border: 1px solid var(--borde-fuerte);
  border-radius: var(--r-md);
  background: var(--superficie);
  padding: var(--e3);
}
.tarjeta-cliente > .cliente-documento-rotulo { margin-top: 0; }
.tarjeta-cliente[hidden],
.cliente-documento-buscador[hidden],
.cliente-documento .error-campo[hidden],
.tomado-ficha[hidden] { display: none; }
.tarjeta-cliente-cuerpo {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--e3);
}
.tarjeta-cliente-texto { min-width: 0; }
.tarjeta-cliente-nombre {
  margin: 0;
  font-size: var(--t-lg);
  line-height: 1.3;
  color: var(--texto);
  overflow-wrap: anywhere;
}
.tarjeta-cliente-datos {
  margin: 2px 0 0;
  font-size: var(--t-md);
  color: var(--texto-tenue);
  overflow-wrap: anywhere;
}
.tarjeta-cliente-cambiar { flex: none; }
.tarjeta-cliente-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e1) var(--e2);
  list-style: none;
  margin: var(--e2) 0 0;
  padding: 0;
}
.tarjeta-cliente-chips[hidden],
.tarjeta-cliente-chips:empty { display: none; }
/* Un nombre de lista largo no puede empujar la página de lado en 375 px. */
.tarjeta-cliente-chips .chip { white-space: normal; overflow-wrap: anywhere; }
.tarjeta-cliente.sin-receptor { background: var(--apagado); }
.cliente-documento-buscador .cliente-vacio { margin: 3px 0 0; color: var(--texto-tenue); }
.tomado-ficha { margin: var(--e1) 0 0; color: var(--texto-tenue); font-size: var(--t-sm); }
/* 02-oct-2026 · permisos de una api key en tarjetas por módulo
   (parciales/permisos-llave.ejs). Eran ~50 casillas corridas en filas de dos o
   tres; ahora cada módulo es una tarjeta que se lee como una unidad, con sus
   permisos en una columna. `minmax(min(280px, 100%), 1fr)` da tres columnas en
   escritorio ancho, dos en uno angosto y una en el teléfono, sin @media; el
   `min()` evita que la tarjeta desborde en pantallas de menos de 280 px. */
.permisos-llave-grupos { border: 0; padding: 0; margin: var(--e4) 0 0; min-width: 0; }
.permisos-llave-grupos > legend { padding: 0; font-weight: 600; color: var(--texto-suave); }
.permisos-herramientas {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e2) var(--e4);
  margin: var(--e2) 0 var(--e3);
}
.permisos-presets { display: flex; flex-wrap: wrap; align-items: center; gap: var(--e2); }
.permisos-buscar { flex: 1 1 240px; max-width: 360px; }
.permisos-rejilla {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr));
  gap: var(--e3);
  align-items: start;
}
.permisos-grupo {
  min-width: 0;
  padding: var(--e2) var(--e3) var(--e3);
  border: 1px solid var(--borde);
  border-radius: var(--r-md);
  background: var(--superficie);
}
/* «Todo el módulo» siempre en su renglón, debajo del título: al lado, unas
   tarjetas lo ponían a la derecha y otras abajo según el largo del nombre. */
.permisos-grupo-cabeza {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  padding-bottom: var(--e1);
  border-bottom: 1px solid var(--borde);
}
.permisos-grupo-cabeza h3 { margin: var(--e1) 0; font-size: var(--t-base); }
.permisos-grupo-cabeza label.casilla { margin: 0; font-size: var(--t-sm); color: var(--texto-suave); }
.permisos-lista { list-style: none; margin: var(--e1) 0 0; padding: 0; }
/* Una columna: la casilla arriba a la izquierda y el texto que envuelve debajo
   de sí mismo, no debajo del cuadrito. */
.permisos-lista label.casilla { display: flex; align-items: flex-start; margin: var(--e1) 0; }
.permisos-lista label.casilla input { flex: none; margin-top: 2px; }
/* El código va siempre en su propio renglón, debajo de la etiqueta: al lado
   quedaba unas veces en línea y otras partido, y la columna no se escaneaba. */
.permisos-texto { display: flex; flex-direction: column; min-width: 0; }
.permisos-clave {
  font-size: var(--t-xs);
  color: var(--texto-tenue);
  background: none;
  padding: 0;
  overflow-wrap: anywhere;
}
/* Lote 3 · semáforo de Hacienda. En la barra es solo un punto dentro del
   distintivo de ambiente, y solo cuando Hacienda está lenta o caída: el texto
   va en el aria-label del distintivo y en el menú (el color nunca va solo). */
.semaforo-punto { display: inline-block; width: 0.55rem; height: 0.55rem; border-radius: 50%; flex: none; }
.semaforo-punto.lento { background: var(--alerta); }
.semaforo-punto.caido { background: var(--destructivo); }
.semaforo-hacienda { margin: var(--e1) 0 var(--e3); }
.semaforo-hacienda .chip { margin-right: var(--e1); }

/* Lote 3 · «¿Qué dice esta clave?» debajo de la clave de 50 dígitos. */
.clave-decodificada { margin-top: var(--e1); font-size: var(--t-md); }
.clave-decodificada > summary { cursor: pointer; color: var(--texto-tenue); min-height: 44px; display: flex; align-items: center; }
.clave-decodificada .definiciones { margin-top: var(--e1); }
.clave-resumen { margin-top: var(--e1); }

/* Lote 3 · «Cliente nuevo» dentro de la factura y la cotización. */
.alta-rapida { margin-top: var(--e2); border: 1px solid var(--borde); border-radius: var(--r-md); padding: 0 var(--e3); }
.alta-rapida > summary { cursor: pointer; min-height: 44px; display: flex; align-items: center; font-weight: 600; }
.alta-rapida[open] { padding-bottom: var(--e3); }
.alta-rapida .boton { margin-top: var(--e2); }

/* 1380 · histórico del sistema anterior: barras de consumo (SVG del servidor)
   apiladas, abajo lo del sistema anterior y arriba lo de Kiwi. */
.historico-grafico { overflow-x: auto; padding-bottom: var(--e2); }
.historico-barras { display: block; overflow: visible; }
.historico-barras-base { stroke: var(--borde); stroke-width: 1; }
.historico-barras-historico { fill: var(--borde-fuerte); }
.historico-barras-kiwi { fill: var(--acento); }
.historico-barras-zona { fill: transparent; }
.historico-barras g:hover .historico-barras-zona { fill: var(--apagado); fill-opacity: 0.6; }
.historico-barras-etiqueta { font-size: 10px; fill: var(--texto-suave, currentColor); }
.leyenda-historico { display: inline-flex; align-items: center; gap: var(--e1); margin-left: var(--e3); }
.leyenda-historico .muestra { display: inline-block; width: 10px; height: 10px; border-radius: 2px; }
.leyenda-historico .muestra.historico { background: var(--borde-fuerte); }
.leyenda-historico .muestra.kiwi { background: var(--acento); }
.historico-matriz td, .historico-matriz th { white-space: nowrap; }

/* ===========================================================================
   1450 · LA FACTURA INTERNA SE VE DISTINTA EN TODA EMPRESA
   ---------------------------------------------------------------------------
   Pedido de la facturadora de Grudelsa: «colocarlo en color rojo». La entrada
   del menú, la banda del título y el botón principal llevan el rojo de
   `--interna`; la insignia «No va a Hacienda» dice con palabras lo mismo que el
   color (WCAG 1.4.1: el color nunca es la única señal). El PDF no lo lleva.
   ======================================================================== */
.lateral-fila.marca-interna .lateral-enlace { box-shadow: inset 3px 0 0 var(--interna-lateral); }
.lateral-fila.marca-interna .lateral-enlace .icono { color: var(--interna-lateral); }
.lateral-fila.marca-interna .lateral-enlace .texto { color: var(--interna-lateral); font-weight: 600; }

.banda-interna {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e2) var(--e3);
  margin: 0 0 var(--e4);
  padding: var(--e2) var(--e3);
  background: var(--interna-suave);
  border: 1px solid var(--interna-borde);
  border-left: 6px solid var(--interna);
  border-radius: var(--r-lg);
}
.banda-interna h1 { margin: 0; color: var(--interna); }
.insignia-interna {
  display: inline-flex;
  align-items: center;
  gap: var(--e1);
  padding: 2px var(--e2);
  border-radius: var(--r-pastilla);
  background: var(--interna);
  color: var(--superficie);
  font-size: var(--t-sm);
  font-weight: 600;
  white-space: nowrap;
}
.boton.principal.boton-interna,
a.boton.principal.boton-interna {
  background: var(--interna);
  border-color: var(--interna);
  color: var(--superficie);
}
.boton.principal.boton-interna:hover,
.boton.principal.boton-interna:focus-visible,
a.boton.principal.boton-interna:hover {
  background: var(--interna-hundido);
  border-color: var(--interna-hundido);
}
.opciones .casilla.no-disponible { opacity: 0.75; }

/* ---------------------------------------------------------------------------
   Generador de reportes (08-oct-2026). Un flujo en una sola página: los pasos
   son una lista ordenada de verdad (fuente → columnas → filtros → agrupar), así
   que llevan su número. Las casillas se agrupan por tema en columnas que se
   acomodan solas; en el teléfono todo baja a una columna (movil.css).
   --------------------------------------------------------------------------- */
.generador-fuentes {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--e3);
  margin: 0;
  padding: 0;
  list-style: none;
}
.generador-fuente {
  display: flex;
  flex-direction: column;
  gap: var(--e1);
  min-height: 44px;
  padding: var(--e4);
  border: 1px solid var(--borde);
  border-radius: var(--r-md);
  background: var(--superficie);
  color: var(--texto);
  text-decoration: none;
}
.generador-fuente:hover { border-color: var(--acento); background: var(--acento-suave); }
.generador-fuente strong { font-size: var(--t-lg, 1.1rem); }

.generador-pasos {
  counter-reset: paso;
  margin: 0 0 var(--e4);
  padding: 0;
  list-style: none;
}
.generador-paso {
  counter-increment: paso;
  position: relative;
  margin: 0 0 var(--e3);
  padding: var(--e4) var(--e4) var(--e4) calc(var(--e4) + 2.25rem);
  border: 1px solid var(--borde);
  border-radius: var(--r-md);
  background: var(--superficie);
}
.generador-paso::before {
  content: counter(paso);
  position: absolute;
  top: var(--e4);
  left: var(--e4);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  background: var(--acento-suave);
  color: var(--acento-hundido);
  font-weight: 700;
}
.generador-paso > h2 { margin: 0 0 var(--e2); font-size: var(--t-lg, 1.1rem); }
.generador-paso .filtros { margin-bottom: 0; }

.generador-columnas {
  display: grid;
  /* «En este orden» con lugar para el nombre, el botón «Ajustar» y las
     flechas en un renglón (1.71.0). */
  grid-template-columns: minmax(0, 3fr) minmax(360px, 2fr);
  gap: var(--e4);
  align-items: start;
}
.generador-catalogo {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: var(--e3);
  align-content: start;
}
.generador-catalogo > input[type="search"] { grid-column: 1 / -1; }
.generador-tema {
  margin: 0;
  padding: var(--e2) var(--e3);
  border: 1px solid var(--borde);
  border-radius: var(--r-md);
}
.generador-tema legend { padding: 0 var(--e1); font-weight: 600; color: var(--texto-suave); }
.generador-casilla { display: flex; min-height: 44px; font-weight: 400; }
.generador-casilla[hidden],
.generador-tema[hidden] { display: none; }

.generador-orden {
  padding: var(--e3);
  border: 1px dashed var(--borde-fuerte);
  border-radius: var(--r-md);
  background: var(--fondo);
}
.generador-orden h3 { margin: 0 0 var(--e1); }
.generador-orden-lista { margin: var(--e2) 0 0; padding: 0; list-style: none; }
.generador-orden-lista li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e1) var(--e2);
  min-height: 44px;
  margin-bottom: var(--e1);
  padding: var(--e1) var(--e2);
  border: 1px solid var(--borde);
  border-radius: var(--r-md);
  background: var(--superficie);
  cursor: grab;
}
.generador-orden-lista li:focus-visible { outline: 2px solid var(--acento); outline-offset: 2px; }
.generador-orden-lista li.arrastrando { opacity: 0.5; }
.generador-orden-lista li.soltar-aqui { border-color: var(--acento); box-shadow: 0 -3px 0 var(--acento) inset; }
.generador-orden-nombre { flex: 1 1 8rem; overflow-wrap: anywhere; }
.generador-orden-total select { min-height: 44px; max-width: 100%; }
.generador-orden-botones { display: inline-flex; gap: 2px; }
.generador-orden-botones .boton-icono[disabled] { opacity: 0.35; cursor: default; }

/* 1.71.0 · el panel «Ajustar» de cada columna, COMPACTO (revisión de
   Impeccable e Intent del PR #393). El renglón es una rejilla: nombre, botón
   «Ajustar» y flechas en la primera fila; el cuerpo del panel abierto baja a la
   segunda y ocupa todo el ancho. El `<details>` va con `display: contents` para
   que su `summary` y su cuerpo sean celdas de la rejilla: así el botón NO se
   mueve al abrir (Intent N-P3-2). Ancho y alineación son pastillas; el formato,
   un selector con el ejemplo en cada opción; la alineación, en «Más opciones».
   Meta: abierto, 280 px o menos. */
.generador-orden-lista li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
}
.generador-orden-nombre { grid-column: 1; grid-row: 1; }
.generador-orden-resumen { display: block; font-size: var(--t-sm); }
.generador-orden-resumen:empty { display: none; }
.generador-ajustes { display: contents; }
.generador-ajustes > summary { grid-column: 2; grid-row: 1; }
.generador-orden-botones { grid-column: 3; grid-row: 1; }
.generador-ajustes-cuerpo { grid-column: 1 / -1; grid-row: 2; cursor: default; min-width: 0; }
/* Chrome 131+ envuelve el cuerpo de un <details> en `::details-content`: esa
   caja es la celda de la rejilla y es la que va a la segunda fila. */
.generador-ajustes::details-content { grid-column: 1 / -1; grid-row: 2; min-width: 0; }
/* Sin esto el selector de formato (su opción más larga) ensancha la rejilla del
   renglón y en 375 px la página se corre de lado. */
.generador-ajustes-cuerpo > *, .generador-ajuste-formato select { min-width: 0; }
.generador-ajustes > summary {
  display: inline-flex;
  align-items: center;
  gap: var(--e1);
  min-height: 36px;
  min-width: 44px;
  padding: 0 var(--e2);
  border: 1px solid var(--borde);
  border-radius: var(--r-md);
  cursor: pointer;
  list-style: none;
  font-size: var(--t-md);
}
.generador-ajustes > summary::-webkit-details-marker { display: none; }
.generador-ajustes[open] > summary { background: var(--acento-suave); border-color: var(--acento-borde); }
.generador-ajustes-cuerpo {
  display: grid;
  gap: var(--e2) var(--e3);
  margin-top: var(--e1);
  padding: var(--e2) 0 var(--e1);
  border-top: 1px solid var(--borde);
}
.generador-segmentado { display: flex; flex-wrap: wrap; gap: var(--e1); margin: 0; padding: 0; border: 0; min-width: 0; }
.generador-segmentado legend { padding: 0; margin-bottom: var(--e1); font-size: var(--t-sm); font-weight: 600; color: var(--texto-suave); }
.generador-segmentado label { position: relative; margin: 0; }
/* El radio queda para el teclado y el lector; lo que se ve es la pastilla. */
.generador-segmentado input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.generador-segmentado label span {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  padding: 0 var(--e2);
  border: 1px solid var(--borde-fuerte);
  border-radius: 999px;
  background: var(--superficie);
  font-size: var(--t-sm);
  font-weight: 400;
}
.generador-segmentado input:checked + span { background: var(--acento-suave); border-color: var(--acento); color: var(--acento-fuerte); font-weight: 600; }
.generador-segmentado input:focus-visible + span { outline: 2px solid var(--acento); outline-offset: 2px; }
.generador-ajuste-formato { display: flex; flex-direction: column; gap: var(--e1); }
.generador-ajuste-formato label { font-size: var(--t-sm); font-weight: 600; color: var(--texto-suave); }
.generador-ajuste-formato select { min-height: 36px; width: 100%; max-width: 100%; }
.generador-total-pie .casilla-en-linea { min-height: 32px; font-weight: 400; }
/* En la computadora las flechas miden 36 px: el renglón deja lugar al nombre.
   En el teléfono vuelven a 44 (movil.css). */
.generador-orden-botones .boton-icono { width: 36px; height: 36px; }
.generador-orden-lista li { gap: 0 var(--e1); }
/* Resumiendo, el total lo calcula cada fila (paso 4): la casilla no aplica. */
.generador.agrupando [data-total-pie] { display: none; }
.generador-pista { margin: 0; font-size: var(--t-sm); }
.generador-mas-opciones > summary { cursor: pointer; font-size: var(--t-sm); color: var(--texto-suave); min-height: 32px; display: flex; align-items: center; }
.generador-mas-opciones[open] > summary { margin-bottom: var(--e1); }
.generador-ajustes-cuerpo [data-aplicar-formato] { justify-self: start; text-align: start; white-space: normal; max-width: 100%; }
.generador-contador { font-weight: 400; font-size: var(--t-sm); margin-left: var(--e2); }
.generador-orden-vacio { margin-top: var(--e2); }
.generador-resultado h2 { margin: 0; }
#titulo-orden:focus { outline: 2px solid var(--acento); outline-offset: 2px; }

/* Paso 4: los selectores directo en el paso, sin otra caja dentro de la
   tarjeta (Impeccable), y lo que se calcula en cada fila junto a «Resumir». */
.generador-rejilla { display: flex; flex-wrap: wrap; gap: var(--e2) var(--e3); align-items: flex-end; }
.generador-rejilla > div { display: flex; flex-direction: column; min-width: 12rem; }
.generador-calculos { margin-top: var(--e3); padding-top: var(--e3); border-top: 1px solid var(--borde); }
.generador-calculos[hidden] { display: none; }
.generador-calculos-titulo { margin: 0 0 var(--e2); }
.generador-calculos-lista { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: var(--e2) var(--e4); }
.generador-calculo { display: flex; flex-direction: column; gap: var(--e1); }
.generador-calculo[hidden] { display: none; }
.generador-calculo select { min-height: 44px; }

/* Anchos de la vista previa: la palabra del panel es un MÍNIMO (DESIGN_PATTERNS
   §11: lo que no cabe se desplaza, no se aplasta). Los mismos tres valen en el
   Excel (11, 16 y 30 caracteres) y en el PDF (pesos 0.7, 1 y 1.8). */
.generador-tabla th.col-angosta, .generador-tabla td.col-angosta { min-width: 6rem; }
.generador-tabla th.col-normal, .generador-tabla td.col-normal { min-width: 9rem; }
.generador-tabla th.col-ancha, .generador-tabla td.col-ancha { min-width: 16rem; }
.generador-tabla th.centro, .generador-tabla td.centro { text-align: center; }
/* El total general queda a la vista al bajar por la tabla, como el encabezado. */
/* El total general queda a la vista al bajar, con fondo y una raya arriba:
   sin ella la fila que pasa por debajo se veía cortada (Impeccable). */
.generador-tabla tfoot td { position: sticky; bottom: 0; z-index: 4; background: var(--superficie); box-shadow: inset 0 1px 0 var(--borde-fuerte); }
.generador-tabla .generador-total strong { white-space: nowrap; }
.generador-tabla tbody tr[hidden] { display: none !important; }
/* En la computadora el total va al pie de la tabla; arriba solo en el teléfono. */
.generador-total-arriba { display: none; }
.generador-volver { margin: var(--e3) 0 0; }
.generador-ver-mas { margin: var(--e3) 0 0; text-align: center; }

.generador-filtros { display: grid; gap: var(--e2); }
.generador-filtro {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--e2) var(--e3);
  margin: 0;
  padding: var(--e2) 0;
  border: 0;
  border-bottom: 1px solid var(--borde);
}
.generador-filtro > div { display: flex; flex-direction: column; min-width: 9rem; }
.generador-filtro > div[hidden] { display: none; }
.generador-filtro select[multiple] { min-width: 12rem; }
.generador-filtro-quitar { min-width: 0 !important; }

.generador-acciones {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e2);
  align-items: center;
  margin: var(--e3) 0;
}
.generador-acciones form { margin: 0; }

.generador-tabla td.numero,
.generador-tabla th.der { white-space: nowrap; font-variant-numeric: tabular-nums; }
.generador-tabla tr.generador-subtotal td { background: var(--apagado); font-weight: 600; }
.generador-tabla tfoot td { border-top: 2px solid var(--borde-fuerte); background: var(--fondo); }
.reportes-guardados { margin-top: var(--e5); }

/* 2020 · «Mis reportes» y «Compartidos conmigo» (`parciales/reportes-guardados.ejs`).
   En la pantalla inicial del generador van ARRIBA de las fuentes: la primera
   tarjeta no necesita el aire de arriba. La estrella de un favorito va antes del
   nombre, en el ámbar de las estrellas del menú; un reporte que la persona no
   puede abrir se pinta apagado (el motivo va en texto debajo del nombre). */
.encabezado-pagina + .reportes-guardados,
.encabezado-pagina + .aviso + .reportes-guardados { margin-top: 0; }
.reportes-guardados + .bloque { margin-top: var(--e5); }
/* La estrella va DENTRO del nombre (que se recorta a dos renglones): es texto
   generado y sin nombre accesible; el «(favorito)» lo lee `.solo-lectores`. */
.nombre-2-lineas.es-favorito::before { content: '★' / ''; color: var(--alerta); margin-right: 0.3em; }
.tabla tr.fila-apagada td { color: var(--texto-tenue); }
.tabla tr.fila-apagada [data-motivo-apagado] { color: var(--texto-suave); }

/* La estrella junto al nombre de un reporte guardado abierto. */
.titulo-con-estrella { display: flex; align-items: center; gap: var(--e2); flex-wrap: wrap; }
.titulo-con-estrella h1 { margin: 0; }
.titulo-con-estrella form { margin: 0; }
.reporte-estrella-boton.marcada { color: var(--alerta); }

/* Compartir con personas y elegir destinatarios: una lista de casillas en dos
   o tres columnas, cada una de 44 px de alto, con el correo en gris. */
.compartir-personas { margin: 0; padding: var(--e3) var(--e4); border: 1px solid var(--borde); border-radius: var(--r-md); min-width: 0; }
.compartir-personas[hidden] { display: none; }
.compartir-personas legend { padding: 0 var(--e1); font-weight: 600; }
.compartir-lista {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 0 var(--e3);
  margin: var(--e2) 0;
  padding: 0;
  list-style: none;
}
.compartir-lista li[hidden] { display: none; }
.compartir-persona { min-height: 44px; align-items: center; }
.compartir-persona .sutil { display: block; font-weight: 400; overflow-wrap: anywhere; }
.compartir-personas > .casilla-en-linea { min-height: 44px; }
.compartir-lista .linea-chica { display: block; margin: -6px 0 var(--e1) 1.75rem; }

/* «Enviar por correo» (`panel/generador-correo.ejs`): cada grupo del formulario
   en su recuadro, y las opciones (ahora/programado, período, formatos) como
   pastillas con su radio o casilla adentro, de 44 px. */
.correo-grupo { margin: 0 0 var(--e4); padding: var(--e3) var(--e4); border: 1px solid var(--borde); border-radius: var(--r-md); min-width: 0; }
.correo-grupo legend { padding: 0 var(--e1); font-weight: 600; }
.correo-grupo .rejilla { margin-top: var(--e3); }
.correo-grupo [hidden] { display: none; }
.opciones-pastilla { display: flex; flex-wrap: wrap; gap: var(--e2); }
.opcion-pastilla {
  display: inline-flex;
  align-items: center;
  gap: var(--e2);
  min-height: 44px;
  margin: 0;
  padding: 0 var(--e3);
  font-weight: 600;
  border: 1px solid var(--boton-borde);
  border-radius: var(--r-pastilla);
  background: var(--superficie);
  cursor: pointer;
}
.opcion-pastilla:has(input:checked) { border-color: var(--acento-fuerte); background: var(--acento-suave); }
.opcion-pastilla:focus-within { outline: 2px solid var(--acento-fuerte); outline-offset: 2px; }

/* Rotación de inventario (08-oct-2026): los umbrales en una fila, la fila de
   cada grupo y su total. `.centro` es para la letra ABC. */
.filtros fieldset.umbrales { display: flex; flex-wrap: wrap; align-items: center; gap: 0.25rem 0.5rem; border: 1px solid var(--borde); border-radius: 6px; padding: 0.25rem 0.75rem 0.5rem; margin: 0; }
.filtros fieldset.umbrales input { width: 4.5em; min-height: 44px; }
.tabla .fila-grupo th { background: var(--apagado); font-weight: 600; }
.tabla .fila-total th,
.tabla .fila-total td { font-weight: 600; border-top: 2px solid var(--borde); }
.tabla th.izq { text-align: left; }
.tabla th.centro,
.tabla td.centro { text-align: center; }

/* 1710 · alcance de la toma física: listas de casillas que se desplazan solas
   (una ferretería tiene doscientos fabricantes), el buscador de artículos y la
   vista previa. */
.alcance-toma {
  border: 1px solid var(--borde);
  border-radius: var(--r-md);
  padding: var(--e3) var(--e4);
  margin: 0 0 var(--e4);
  min-width: 0;
}
.alcance-toma legend { font-weight: 600; padding: 0 var(--e1); }
.alcance-casillas {
  max-height: 12rem;
  overflow-y: auto;
  border: 1px solid var(--borde);
  border-radius: var(--r-md);
  padding: var(--e1) var(--e2);
  background: var(--superficie);
}
.alcance-casillas label {
  display: flex;
  align-items: center;
  gap: var(--e2);
  min-height: 44px;
  font-weight: normal;
}
.alcance-casillas label[hidden] { display: none; }
.alcance-filtro { margin-bottom: var(--e1); width: 100%; }
.alcance-buscador { margin-bottom: var(--e2); }
.alcance-buscador input { width: 100%; }
.alcance-resultados {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e1);
  margin-top: var(--e1);
  font-size: var(--t-sm);
  color: var(--texto-suave);
}
.alcance-resultados .boton { min-height: 44px; text-align: left; }
.alcance-previa { margin: var(--e3) 0; }
.alcance-lista { margin: var(--e1) 0 var(--e2); padding-left: var(--e4); font-size: var(--t-md); }

/* 1720 · «Mis conteos»: la captura del contador, pensada para el celular.
   Un renglón por artículo, la ubicación arriba (es a donde se camina) y la
   casilla grande, con el botón al lado, para digitar con el pulgar. */
.lista-tarjetas { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.75rem; }
.ubicacion-conteo { font-size: 1.15rem; margin: 0.25rem 0; }
.avance-conteo { font-size: 1.05rem; }
.lineas-conteo { list-style: none; margin: 0; padding: 0; }
.linea-conteo { border-bottom: 1px solid var(--borde, #d9dee4); padding: 0.75rem 0; }
.linea-conteo.recontar { border-left: 4px solid var(--ambar, #b7791f); padding-left: 0.5rem; }
.linea-conteo .ubicacion { font-weight: 600; margin: 0 0 0.15rem; }
.linea-conteo .articulo { margin: 0 0 0.4rem; }
.linea-conteo .fila-captura { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 0.5rem; }
.linea-conteo .fila-captura label { flex-basis: 100%; }
.linea-conteo .fila-captura input { flex: 1 1 8rem; min-height: 44px; font-size: 1.1rem; }
.linea-conteo .fila-captura .boton { min-height: 44px; }
.linea-conteo .estado-linea { margin: 0.25rem 0 0; min-height: 1.2em; font-size: 0.9rem; }

/* Existencias por ubicación (08-oct-2026) · «Por artículo | Por ubicación» y
   el título de cada estante. Los enlaces envuelven si no caben: nunca se
   recortan, y miden lo que mide un botón (44 px en el teléfono, ver movil.css). */
.selector-vista {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin: 0.75rem 0;
}
.grupo-ubicacion-titulo {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25rem 0.75rem;
}
.grupo-ubicacion .tabla tfoot th,
.grupo-ubicacion .tabla tfoot td { background: var(--apagado); }
/* «Calidad del catálogo» (09-oct-2026). El número de cada tarjeta es el enlace
   a la lista de Artículos con el mismo filtro: se lee como cifra, no como un
   enlace azul subrayado, y mide 44 px de alto para el dedo. */
.tarjetas-calidad .numero {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  margin-top: 0;
  color: inherit;
  text-decoration: none;
}
.tarjetas-calidad .numero:hover,
.tarjetas-calidad .numero:focus-visible { text-decoration: underline; }
.tarjetas-calidad .enlace-mas {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}
.barra-lote-calidad { align-items: center; margin-bottom: var(--e3); }
.calidad-genericos {
  margin-top: var(--e5);
  padding-top: var(--e3);
  border-top: 1px solid var(--borde);
}
.calidad-genericos > summary {
  display: flex;
  align-items: center;
  min-height: 44px;
  font-weight: 600;
  cursor: pointer;
}
.calidad-genericos textarea { width: 100%; max-width: 32rem; }

/* ------------------------------------------------- usuarios: correo enviado
   13-oct-2026 · después de invitar, reenviar o corregir, el correo al que salió
   el enlace se ve EN GRANDE (`empresa/usuarios.ejs`). Es el último momento en
   que quien invita puede notar un correo mal escrito sin que nadie se quede
   esperando un enlace que nunca llega. `overflow-wrap` porque un correo largo
   en un teléfono de 375 px no tiene dónde partirse. */
.correo-enviado {
  margin: var(--e3) 0;
  padding: var(--e3) var(--e4);
  border: 1px solid var(--acento-borde);
  border-radius: var(--r-md);
  background: var(--acento-suave);
}
.correo-enviado-rotulo { margin: 0; color: var(--texto-suave); }
.correo-enviado-correo {
  margin: var(--e1) 0 var(--e2);
  font-size: var(--t-xl);
  font-weight: 600;
  color: var(--texto);
  overflow-wrap: anywhere;
}
.corregir-correo { margin-top: var(--e1); }
.corregir-correo > summary { cursor: pointer; color: var(--acento-fuerte); min-height: 32px; }
.corregir-correo[open] { display: grid; gap: var(--e1); max-width: 320px; }

/* ---------------------------------------------------------------------------
   1.66.3 · El cuadro «Envío de documento» (public/js/cuadro-envio.js)
   ---------------------------------------------------------------------------
   Como el del sistema anterior de Grudelsa: los correos son ETIQUETAS dentro de
   un recuadro que se lee como un solo campo, cada una con su ×, y el campo para
   escribir otro va adentro, debajo. El recuadro toma el foco del campo
   (`:focus-within`) para que se vea dónde se escribe. En el teléfono el cuadro
   ocupa el ancho y las etiquetas parten la dirección larga en vez de desbordar. */
/* El encabezado y los botones quedan fijos; lo que se desplaza es el cuerpo,
   para que «Enviar» no se vaya de la pantalla con muchas etiquetas. */
.cuadro-envio-modal[open] { display: flex; flex-direction: column; overflow: hidden; }
.cuadro-envio-modal > .cuadro-envio,
.cuadro-envio-modal > .modal-cuerpo { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
.cuadro-envio > .modal-cuerpo { flex: 1 1 auto; min-height: 0; }
.cuadro-envio-datos {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--e2) var(--e4);
  margin: 0 0 var(--e4);
}
.cuadro-envio-datos dt { font-size: var(--t-sm); color: var(--texto-tenue); font-weight: 600; }
.cuadro-envio-datos dd { margin: 0; overflow-wrap: anywhere; }
.cuadro-envio label { display: block; margin: 0 0 var(--e1); font-weight: 600; }

.campo-chips {
  display: flex;
  flex-direction: column;
  gap: var(--e2);
  padding: var(--e2);
  background: var(--superficie);
  border: 1px solid var(--borde-fuerte);
  border-radius: var(--r-md);
  cursor: text;
  transition: border-color var(--mov-rapido) var(--curva), box-shadow var(--mov-rapido) var(--curva);
}
.campo-chips:focus-within { border-color: var(--acento); box-shadow: 0 0 0 3px var(--acento-borde); }
.campo-chips-lista {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e2);
  margin: 0;
  padding: 0;
  list-style: none;
}
.campo-chips-lista:empty { display: none; }
/* El campo de adentro no lleva su propio borde: el recuadro ES el campo. */
.campo-chips .campo-chips-entrada,
.campo-chips .campo-chips-entrada:focus,
.campo-chips .campo-chips-entrada:hover {
  padding: var(--e1) var(--e1);
  border: 0;
  box-shadow: none;
  background: transparent;
}

.chip-correo {
  display: inline-flex;
  align-items: center;
  gap: var(--e1);
  max-width: 100%;
  padding: 2px 2px 2px var(--e3);
  color: var(--info);
  background: var(--info-suave);
  border: 1px solid var(--info-borde);
  border-radius: var(--r-pastilla);
  font-size: var(--t-md);
  line-height: 1.35;
}
.chip-correo.invalido {
  color: var(--destructivo-hundido);
  background: var(--destructivo-suave);
  border-color: var(--destructivo-borde);
}
.chip-correo-texto { display: flex; flex-direction: column; min-width: 0; padding: 2px 0; }
.chip-correo-direccion { font-weight: 600; overflow-wrap: anywhere; }
.chip-correo-etiqueta { font-size: var(--t-xs); color: var(--texto-suave); overflow-wrap: anywhere; }
.chip-correo.invalido .chip-correo-etiqueta { color: var(--destructivo-hundido); }
.chip-correo-quitar {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  color: inherit;
  background: none;
  border: 0;
  border-radius: var(--r-pastilla);
  cursor: pointer;
}
.chip-correo-quitar:hover { background: var(--info-borde); }
.chip-correo.invalido .chip-correo-quitar:hover { background: var(--destructivo-borde); }
.chip-correo-quitar:focus-visible { outline-offset: -2px; border-radius: var(--r-pastilla); }

.cuadro-envio-sugerencias { margin: var(--e4) 0 0; }
.cuadro-envio-subtitulo { margin: 0 0 var(--e2); font-size: var(--t-sm); font-weight: 600; color: var(--texto-suave); }
.cuadro-envio-sugerencias ul { display: flex; flex-wrap: wrap; gap: var(--e2); margin: 0; padding: 0; list-style: none; }
/* La sugerencia es GRIS y punteada: todavía no va. Al tocarla pasa arriba, azul. */
.chip-sugerido {
  display: inline-flex;
  align-items: center;
  gap: var(--e2);
  min-height: 36px;
  max-width: 100%;
  padding: var(--e1) var(--e3);
  font: inherit;
  font-size: var(--t-md);
  text-align: left;
  color: var(--texto-suave);
  background: var(--apagado);
  border: 1px dashed var(--boton-borde);
  border-radius: var(--r-lg);
  cursor: pointer;
}
.chip-sugerido > .icono { flex: none; }
.chip-sugerido-texto { display: flex; flex-wrap: wrap; gap: 0 var(--e1); min-width: 0; }
.chip-sugerido:hover { color: var(--texto); border-color: var(--boton-borde-hover); }
.chip-sugerido-correo { color: var(--texto-tenue); overflow-wrap: anywhere; }
.cuadro-envio-enlaces { display: flex; flex-wrap: wrap; align-items: center; gap: var(--e1) var(--e3); margin: var(--e4) 0 0; }
.cuadro-envio-enlaces a { display: inline-flex; align-items: center; gap: var(--e1); min-height: 44px; }

@media (max-width: 760px) {
  /* 44 px de área táctil para la × y la sugerencia, como el resto de botones. */
  .chip-correo-quitar { width: 44px; height: 44px; }
  .chip-sugerido { min-height: 44px; }
}

/* 1.66.7 · Ventas general: la fila de cuadre del pie (Subtotal + IVA + otros
   cargos = Total). Verde si cuadra, roja con la diferencia si no. */
tr[data-cuadre] td { font-weight: 600; font-size: var(--t-md); }
tr[data-cuadre].cuadra td { color: var(--acento-fuerte); }
tr[data-cuadre].descuadra td { color: var(--destructivo); background: var(--destructivo-suave); }

/* ===========================================================================
   14-oct-2026 · ASISTENTE DEL BUZÓN DE CORREO (Integraciones)
   ---------------------------------------------------------------------------
   «Como conectar Gmail»: tarjetas por proveedor, una guía corta y solo los
   campos que ese proveedor necesita. Sin JavaScript, `:has()` muestra lo del
   proveedor marcado; con JavaScript (`.con-js`) manda el atributo `hidden` y
   estas reglas se apagan para no pelear con él. Un navegador sin `:has()` y sin
   JavaScript ve TODO: más largo, pero nada queda escondido sin salida.
   Los colores de marca son tonos con nombre de ROL (`--marca-*`), usados solo
   en el ícono decorativo: el nombre del proveedor siempre va en texto al lado.
   ======================================================================== */
:root {
  --marca-gmail: #c5221f;
  --marca-outlook: #0a64c9;
  --marca-ms-rojo: #f25022;
  --marca-ms-verde: #7fba00;
  --marca-ms-azul: #00a4ef;
  --marca-ms-amarillo: #ffb900;
  --marca-yahoo: #6001d2;
  --marca-icloud: #3693f3;
}

.buzon-titulo { margin: var(--e5) 0 var(--e2); }
.buzon-asistente fieldset.buzon-paso { border: 0; padding: 0; margin: var(--e4) 0 0; min-width: 0; }
.buzon-asistente fieldset.buzon-paso > legend {
  padding: 0;
  margin-bottom: var(--e2);
  font-family: var(--fuente-titulo);
  font-weight: 600;
  color: var(--texto);
}

/* -- tarjetas de proveedor -------------------------------------------------
   Radios de verdad (teclado, lector de pantalla y sin JavaScript), con el
   punto a la vista: sin `:has()` es la única marca de cuál está elegido.     */
.buzon-proveedores {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: var(--e2);
}
label.buzon-proveedor {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--e3);
  min-height: 64px;
  margin: 0;
  padding: var(--e3);
  font-weight: 400;
  color: var(--texto);
  background: var(--superficie);
  border: 1px solid var(--boton-borde);
  border-radius: var(--r-lg);
  cursor: pointer;
}
label.buzon-proveedor:hover { border-color: var(--boton-borde-hover); background: var(--apagado); }
label.buzon-proveedor input[type="radio"] {
  position: absolute;
  top: var(--e2);
  right: var(--e2);
  width: 16px;
  height: 16px;
  margin: 0;
  accent-color: var(--acento-fuerte);
}
label.buzon-proveedor:has(input:checked) {
  border: 2px solid var(--acento-fuerte);
  padding: calc(var(--e3) - 1px);
  background: var(--acento-suave);
}
label.buzon-proveedor:has(input:focus-visible) { outline: 3px solid var(--acento); outline-offset: 2px; }
.buzon-proveedor-texto { display: flex; flex-direction: column; min-width: 0; padding-right: var(--e4); }
.buzon-proveedor-nombre { font-weight: 600; }
.buzon-proveedor-detalle { font-size: var(--t-sm); color: var(--texto-tenue); }

.buzon-logo { display: inline-flex; flex: none; width: 32px; height: 32px; color: var(--texto-suave); }
.buzon-logo svg { width: 100%; height: 100%; }
.buzon-logo .lc-relleno { fill: var(--superficie); }
.buzon-logo .lc-trazo { stroke: var(--superficie); }
.buzon-logo-gmail { color: var(--marca-gmail); }
.buzon-logo-outlook { color: var(--marca-outlook); }
.buzon-logo-yahoo { color: var(--marca-yahoo); }
.buzon-logo-icloud { color: var(--marca-icloud); }
.buzon-logo .ms-rojo { fill: var(--marca-ms-rojo); }
.buzon-logo .ms-verde { fill: var(--marca-ms-verde); }
.buzon-logo .ms-azul { fill: var(--marca-ms-azul); }
.buzon-logo .ms-amarillo { fill: var(--marca-ms-amarillo); }

/* -- guía corta y campos --------------------------------------------------- */
.buzon-guia {
  margin: 0 0 var(--e3);
  padding: var(--e3) var(--e4);
  background: var(--info-suave);
  border: 1px solid var(--info-borde);
  border-radius: var(--r-lg);
}
.buzon-guia > p { margin: 0 0 var(--e2); }
.buzon-guia > p:last-child { margin-bottom: 0; }
.buzon-pasos { margin: 0; padding-left: var(--e5); }
.buzon-pasos li { margin: 0 0 var(--e1); line-height: 1.5; }
.buzon-pasos a { font-weight: 600; overflow-wrap: anywhere; }
.buzon-m365-forma { display: flex; flex-direction: column; margin-bottom: var(--e2); }
.buzon-m365-forma label.casilla { align-items: flex-start; margin: 0; padding: var(--e2) 0; min-height: 44px; }
.buzon-m365-forma label.casilla input { margin-top: 2px; flex: none; }
.buzon-asistente .rejilla > div > p.sutil { margin: var(--e1) 0 0; font-size: var(--t-md); }
.buzon-asistente .buzon-emitidos { margin-top: var(--e3); }
.buzon-asistente .buzon-emitidos > p.sutil { margin-top: 0; }
.buzon-plegable { margin-top: var(--e4); }
.buzon-resultado { margin: var(--e4) 0 0; }
.buzon-botones { margin-top: var(--e4); }

/* -- el resumen del buzón guardado ----------------------------------------- */
.buzon-resumen {
  display: flex;
  align-items: flex-start;
  gap: var(--e3);
  margin: var(--e4) 0 var(--e3);
  padding: var(--e4);
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--r-lg);
}
.buzon-resumen-texto { min-width: 0; }
.buzon-resumen-texto p { margin: 0; }
.buzon-resumen-titulo { display: flex; flex-wrap: wrap; align-items: center; gap: var(--e1) var(--e2); }
.buzon-resumen-titulo .mono { overflow-wrap: anywhere; }
.buzon-resumen-texto .sutil { margin-top: var(--e1); }
.buzon-acciones { margin-bottom: var(--e2); }
.buzon-acciones .boton { min-height: 44px; }

/* -- sin JavaScript: el proveedor marcado decide qué se ve ------------------
   Se ESCONDE lo que no es del proveedor marcado (y no «se muestra lo que sí»):
   así cada bloque conserva su `display` de siempre (flex, grid) al verse.     */
@supports selector(:has(a)) {
  .buzon-asistente:not(.con-js):not(:has(input[name="proveedor"]:checked)) [data-para],
  .buzon-asistente:not(.con-js):not(:has(input[name="proveedor"]:checked)) .buzon-paso-cuenta,
  .buzon-asistente:not(.con-js):has(#buzon-prov-gmail:checked) [data-para]:not([data-para~="gmail"]),
  .buzon-asistente:not(.con-js):has(#buzon-prov-outlook:checked) [data-para]:not([data-para~="outlook"]),
  .buzon-asistente:not(.con-js):has(#buzon-prov-m365:checked) [data-para]:not([data-para~="m365"]),
  .buzon-asistente:not(.con-js):has(#buzon-prov-yahoo:checked) [data-para]:not([data-para~="yahoo"]),
  .buzon-asistente:not(.con-js):has(#buzon-prov-icloud:checked) [data-para]:not([data-para~="icloud"]),
  .buzon-asistente:not(.con-js):has(#buzon-prov-otro:checked) [data-para]:not([data-para~="otro"]),
  /* Microsoft 365 con aplicación: los tres datos de Entra ID en lugar de la
     contraseña; con cualquier otra cosa, al revés. */
  .buzon-asistente:not(.con-js):not(:has(#buzon-prov-m365:checked)) [data-solo-oauth],
  .buzon-asistente:not(.con-js):not(:has(#buzon-auth-oauth:checked)) [data-solo-oauth],
  .buzon-asistente:not(.con-js):has(#buzon-prov-m365:checked):has(#buzon-auth-oauth:checked) [data-solo-clave] {
    display: none;
  }
}

@media (max-width: 480px) {
  .buzon-proveedores { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  label.buzon-proveedor { gap: var(--e2); padding: var(--e2); min-height: 56px; }
  label.buzon-proveedor:has(input:checked) { padding: calc(var(--e2) - 1px); }
  .buzon-logo { width: 28px; height: 28px; }
  .buzon-resumen { padding: var(--e3); }
  .buzon-acciones .boton { flex: 1 1 calc(50% - var(--e1)); justify-content: center; }
}

/* ==========================================================================
   Migración 2010 · registro por autoservicio, cuenta en prueba y contraseña
   temporal
   ========================================================================== */

/* El aviso discreto del panel con los días de prueba. Mismo tono que el aviso
   de versión opcional; al terminar la prueba toma el de la obligatoria. */
.banda-prueba { justify-content: center; }
.banda-prueba a { color: inherit; font-weight: 600; }

/* La empresa que terminó la prueba sin activarse, en la lista de Kiwi. Va con
   barra y con el chip «Prueba terminada»: el color no es el único indicador. */
.tabla tbody tr.prueba-vencida,
.tabla tbody tr.prueba-vencida:hover { background: var(--alerta-suave); }
.tabla tbody tr.prueba-vencida > td:first-child { box-shadow: inset 3px 0 0 var(--alerta); }

/* «¿Cómo recibe el acceso?» del alta de empresa. */
fieldset.opciones-acceso { border: 0; padding: 0; margin: var(--e4) 0 0; min-width: 0; }
fieldset.opciones-acceso > legend { font-weight: 600; margin-bottom: var(--e2); padding: 0; }
fieldset.opciones-acceso label.casilla { align-items: flex-start; margin-bottom: var(--e2); }
fieldset.opciones-acceso .campo-con-boton { align-items: flex-end; margin-top: var(--e2); }
fieldset.opciones-acceso .campo-con-boton > div { flex: 1 1 14rem; min-width: 0; }

/* La tarjeta del registro: más ancha que la de entrar porque lleva el padrón
   y las tres opciones de uso, pero de una columna para que en 375 px nada se
   desplace de lado. */
.tarjeta-acceso.tarjeta-registro { max-width: 560px; }
.tarjeta-registro fieldset { border: 0; padding: 0; margin: var(--e3) 0 0; min-width: 0; }
.tarjeta-registro fieldset > legend { font-weight: 600; padding: 0; margin-bottom: var(--e2); }
.tarjeta-registro label.casilla { align-items: flex-start; margin-bottom: var(--e2); }
.tarjeta-registro .razon-social { margin: var(--e2) 0 0; padding: var(--e2) var(--e3); background: var(--apagado); border-radius: var(--r-md); }
/* El campo trampa: fuera de la vista y del lector de pantalla, pero en el DOM
   para que un robot que llena todo lo llene. `display: none` lo saltan algunos. */
.campo-trampa { position: absolute; left: -10000px; width: 1px; height: 1px; overflow: hidden; }
.registro-turnstile { margin-top: var(--e3); min-height: 65px; }
