﻿/* ===================================================================
     SISTEMA VISUAL DEL CLIENTE

     Inspirado en el panel de administración de Ágora: claro, sobrio, poco
     contraste y esquinas casi rectas. El usuario reconoce el terreno.

     Lo que cambiamos a propósito respecto a Ágora:
       - El acento es el ROJO de ScanStudio, no su azul: es otro producto.
       - Hay tema OSCURO de verdad, no solo claro.
       - Tipografía INTER, empotrada en el propio .exe (lib/InterVariable
         .woff2): moderna, idéntica en todos los PC, y sin internet ni CDN
         (el fichero viaja dentro del ejecutable, no se descarga de fuera).

     TEMA: claro por defecto. Si el usuario lo cambia, su elección manda y se
     recuerda en el navegador. El atributo 'data-tema' del <html> es quien
     decide; el CSS solo obedece.
     =================================================================== */
  /* Fuente Inter (variable, un solo fichero para todos los pesos 100-900),
     empotrada en el .exe y servida como /lib/InterVariable.woff2. */
  @font-face {
    font-family:"Inter";
    src:url("/lib/InterVariable.woff2") format("woff2");
    font-weight:100 900;
    font-style:normal;
    font-display:swap;
  }

  :root {
    /* Marca */
    --rojo:#D4232A; --rojo-oscuro:#B01C22; --rojo-tenue:#FDF0F0;

    /* Superficies: blanco el lienzo, gris muy claro el fondo */
    --fondo:#F4F5F7; --papel:#FFFFFF; --papel2:#FAFBFC; --papel3:#F0F1F3;

    /* Tinta: tres pesos, del titular al dato secundario */
    --tinta:#1F2328; --tinta2:#5B6570; --tinta3:#8A929C;

    /* Líneas: una fina para separar, otra marcada para delimitar */
    --linea:#E4E6E9; --linea2:#D3D7DC;

    /* Estado */
    --verde:#1A7F45; --verde-tenue:#EAF6EF;
    --ambar:#9A6400; --ambar-tenue:#FDF4E3;
    --rojo-estado:#C42B2B;
    --azul:#2563EB; --azul-tenue:#EAF1FE;
    --morado:#7C3AED;
    /* Tokens de TIPO DE DOCUMENTO (23/8/2026): el maestro G_TipoDocumental
       guarda el NOMBRE del token (azul, turquesa, naranja, gris) y estas
       variables deciden la tinta exacta en cada tema. Sobrios a propósito:
       distinguen, no gritan. El naranja es el complementario del azul —
       máxima diferencia entre compra y venta de un vistazo. */
    --turquesa:#0D7E83; --turquesa-tenue:#E7F4F5;
    --naranja:#B45A1B; --naranja-tenue:#FBEFE4;

    /* Sombras: apenas insinuadas. Un panel de gestión no flota. */
    --sombra:0 1px 2px rgba(16,24,40,.06);
    --sombra-menu:0 8px 24px rgba(16,24,40,.12);

    --r:6px; --r-s:4px; --r-g:8px;
    --lateral:238px;

    /* --- ASPECTO DE UN CONTROL NEUTRO, EN UN SOLO SITIO (22/8/2026) -----
       Todo lo que sea "un botón que se puede pulsar y no es la acción
       principal" usa ESTOS DOS tokens, nunca un color suelto. Sirve para
       '.boton' y también para los controles con forma propia (los del
       visor, el de tema, el de idioma, el de volver, la tijera del
       recorte), que NO llevan la clase '.boton' porque tienen su propia
       geometría.

       Nace de un fallo real: al dar contraste a '.boton' —que estaba en
       blanco sobre blanco, 1,45:1 contra el 3:1 que pide la norma— los
       cinco controles hechos a mano se quedaron atrás y la pantalla
       apareció a medias, con unos botones grises y otros blancos. Peor
       que antes de tocar nada. Cambiar el aspecto de los controles tiene
       que ser tocar UNA línea, no encontrar los seis sitios (REGLA UN
       SOLO SITIO). Al declararlos aquí, el tema oscuro los hereda solo. */
    --control-fondo:var(--papel3);
    --control-borde:var(--tinta3);

    --fuente:"Inter",system-ui,"Segoe UI",-apple-system,"Helvetica Neue",Arial,sans-serif;
    --mono:"Cascadia Mono",Consolas,ui-monospace,monospace;
  }

  /* El tema oscuro solo se aplica cuando el usuario lo elige. Por defecto,
     claro — aunque Windows esté en oscuro. */
  :root[data-tema="oscuro"] {
    --rojo:#F2555C; --rojo-oscuro:#D93A42; --rojo-tenue:#2A1416;
    --fondo:#14161A; --papel:#1B1E23; --papel2:#20242A; --papel3:#272C33;
    --tinta:#E9ECEF; --tinta2:#A4ADB8; --tinta3:#79828D;
    --linea:#2C3138; --linea2:#3A414A;
    --verde:#4ADE80; --verde-tenue:#132A1D;
    --ambar:#F0B84B; --ambar-tenue:#2C2312;
    --rojo-estado:#F2555C;
    --azul:#7CA5FF; --azul-tenue:#132038;
    --morado:#A78BFA;
    --turquesa:#5ED4DA; --turquesa-tenue:#0F2A2C;
    --naranja:#F5A467; --naranja-tenue:#2E1D10;
    --sombra:0 1px 2px rgba(0,0,0,.4);
    --sombra-menu:0 10px 28px rgba(0,0,0,.5);
  }

  * { box-sizing:border-box; }
  html, body { height:100%; }
  body {
    margin:0; font-family:var(--fuente); font-size:14px;
    background:var(--fondo); color:var(--tinta);
    display:grid; grid-template-columns:var(--lateral) 1fr; grid-template-rows:100vh;
    -webkit-font-smoothing:antialiased;
  }

  /* ---------- Barra lateral ---------- */
  .lateral { background:var(--papel); border-right:1px solid var(--linea);
             display:flex; flex-direction:column; min-height:0; }

  /* Franja BLANCA fija, NUNCA por tema (17/8/2026, pedido explícito): el
     logo trae texto negro horneado en la imagen — en tema oscuro el panel
     se oscurece, y sin este fondo propio quedaría ilegible. */
  .marca-caja { display:flex; align-items:center; justify-content:center; padding:14px 16px;
                border-bottom:1px solid var(--linea); background:#fff; }
  .marca-logo { display:block; width:100%; max-width:220px; height:auto; }

  nav { padding:10px 8px; flex:1 1 auto; overflow-y:auto; }
  .nav-item { display:flex; align-items:center; gap:10px; width:100%; text-align:left;
              border:none; background:none; color:var(--tinta2); font:inherit; font-size:13.5px;
              font-weight:500; padding:8px 10px; border-radius:var(--r); cursor:pointer;
              margin-bottom:1px; }
  .nav-item:hover { background:var(--papel3); color:var(--tinta); }
  /* El activo se marca con una barra a la izquierda, como los paneles de
     gestión de toda la vida: se ve de un vistazo sin gritar. */
  .nav-item.activo { background:var(--rojo-tenue); color:var(--rojo); font-weight:600;
                     box-shadow:inset 2px 0 0 var(--rojo); }
  .nav-item svg { width:17px; height:17px; flex:0 0 auto; opacity:.85; }
  /* OJO: 'display:flex' gana al 'hidden' del navegador; sin esto, un botón
     marcado como oculto se seguiría viendo. */
  /* EL ATRIBUTO 'hidden' SIEMPRE OCULTA, pase lo que pase.
     Trampa conocida: una clase con 'display:grid/flex/inline-flex' (como
     .estado-lateral o .pastilla) PISA el 'display:none' que trae 'hidden', y el
     elemento se ve aunque esté marcado como oculto. Con '!important' aquí, poner
     'hidden' basta para ocultar cualquier cosa, sin tener que acordarse de
     añadir una regla por cada componente nuevo. Ya mordió varias veces. */
  [hidden] { display:none !important; }
  .nav-item[hidden] { display:none; }
  .nav-item .pronto { margin-left:auto; font-size:11px; font-weight:600; letter-spacing:.04em;
                      color:var(--tinta3); background:var(--papel3); border:1px solid var(--linea);
                      padding:1px 6px; border-radius:20px; text-transform:uppercase; }

  /* Ficha del equipo: cada dato aparece UNA sola vez en toda la web. */
  .ficha-lateral { margin-top:auto; padding:11px 14px 9px; border-top:1px solid var(--linea);
                   display:grid; gap:5px; }
  .ficha-linea { display:flex; justify-content:space-between; align-items:baseline; gap:10px;
                 font-size:11.5px; color:var(--tinta3); }
  .ficha-linea b { font-weight:600; color:var(--tinta2); font-size:11.5px; text-align:right;
                   overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:118px; }
  .ficha-linea b.mono { font-family:var(--mono); font-size:11px; }
  /* El Id, en UNA sola línea: etiqueta a la izquierda; a la derecha, la mitad
     final del Id y el botón para copiarlo entero. */
  .ficha-id { align-items:center; }
  .ficha-id-valor { display:flex; align-items:center; gap:6px; min-width:0; }
  .ficha-id-valor b { max-width:none; overflow:visible; text-overflow:clip; }
  .ficha-copiar { flex:0 0 auto; display:grid; place-items:center; width:24px; height:24px;
                  padding:0; border:1px solid transparent; border-radius:var(--r);
                  background:none; color:var(--tinta3); cursor:pointer;
                  transition:color .12s ease, background .12s ease, border-color .12s ease; }
  .ficha-copiar svg { width:13px; height:13px; }
  .ficha-copiar:hover { color:var(--tinta); background:var(--papel3); }
  .ficha-copiar.copiado { color:var(--verde); border-color:var(--verde); }

  /* Estado de conexiones. El servidor decide; aquí solo se pinta (Regla 4). */
  .estado-lateral { padding:10px 14px; border-top:1px solid var(--linea); display:grid; gap:7px; }
  .estado-linea { display:grid; grid-template-columns:7px 1fr auto; align-items:center; gap:9px;
                  font-size:12px; color:var(--tinta2); }
  .estado-linea b { font-weight:600; color:var(--tinta); font-size:11.5px; text-align:right;
                    overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:108px; }
  .punto { width:7px; height:7px; border-radius:50%; background:var(--tinta3); }
  .punto.ok { background:var(--verde); }
  .punto.mal { background:var(--rojo-estado); }
  .punto.aviso { background:var(--ambar); }
  .punto.nada { background:var(--tinta3); opacity:.35; }

  /* La fecha y hora del servidor, completa: puede ocupar dos líneas en un panel
     estrecho, así que se deja envolver en vez de recortarse. */
  .pie-lateral { padding:10px 14px; border-top:1px solid var(--linea); font-size:11.5px;
                 color:var(--tinta3); line-height:1.45; word-break:break-word; }

  /* ---------- Zona principal ---------- */
  .principal { display:flex; flex-direction:column; min-width:0; min-height:0; }
  .barra-sup { display:flex; align-items:center; gap:12px; padding:0 22px; height:54px; flex:0 0 auto;
               border-bottom:1px solid var(--linea); background:var(--papel); }
  /* 'min-width:0' en los DOS lados, y es lo que evita el defecto nº1 de la
     REGLA WEB (desplazamiento horizontal de la página entera). Un hijo de
     flex NO se encoge por debajo de su contenido salvo que se le diga: sin
     esto, el título empujaba y el bloque de la derecha se salía 83 px en un
     móvil de 390 — medido, no supuesto. El título cede primero (con puntos
     suspensivos) porque el nombre de la pantalla ya se ve en el propio
     contenido; los controles de la derecha no tienen dónde repetirse. */
  .titulo-pagina { font-size:16px; font-weight:600; letter-spacing:-.01em;
                    min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .barra-sup .derecha { margin-left:auto; display:flex; align-items:center; gap:8px;
                        min-width:0; flex:0 1 auto; }

  .pastilla { display:inline-flex; align-items:center; gap:6px; padding:3px 10px 3px 8px;
              border-radius:20px; font-size:12px; font-weight:600; border:1px solid transparent; }
  .pastilla::before { content:""; width:6px; height:6px; border-radius:50%; background:currentColor; }
  /* El mismo punto, pero suelto: para cuando algo que NO es una pastilla
     tiene que ir a juego con ella (las tarjetas de filtro de Gestión
     Documental, 25/8/2026). El color lo pone quien lo usa, siempre desde
     el catálogo de tipos — nunca escrito a mano. */
  .punto-tipo { display:inline-block; width:6px; height:6px; border-radius:50%;
                margin-right:6px; vertical-align:middle; }
  .on  { color:var(--verde); background:var(--verde-tenue); border-color:var(--linea2);
         border-color:color-mix(in srgb, var(--verde) 25%, transparent); }
  .off { color:var(--ambar); background:var(--ambar-tenue); border-color:var(--linea2);
         border-color:color-mix(in srgb, var(--ambar) 25%, transparent); }
  .dev { color:var(--rojo); background:var(--rojo-tenue); border-color:var(--linea2);
         border-color:color-mix(in srgb, var(--rojo) 25%, transparent); }
  /* Alias semánticos de '.pastilla' para estados genéricos (Scan y
     cualquier pantalla futura): ok=verde, aviso=ámbar, mal=rojo,
     info=neutro. Mismas variables que .on/.off/.dev — un solo sistema de
     color (REGLA COMPONENTES DE INTERFAZ), solo cambian los nombres para
     que se lean con sentido fuera del contexto "activo/inactivo".         */
  .p-ok  { color:var(--verde); background:var(--verde-tenue); border-color:var(--linea2);
           border-color:color-mix(in srgb, var(--verde) 25%, transparent); }
  .p-aviso { color:var(--ambar); background:var(--ambar-tenue); border-color:var(--linea2);
             border-color:color-mix(in srgb, var(--ambar) 25%, transparent); }
  .p-mal { color:var(--rojo); background:var(--rojo-tenue); border-color:var(--linea2);
           border-color:color-mix(in srgb, var(--rojo) 25%, transparent); }
  .p-info { color:var(--tinta2); background:var(--papel2); border-color:var(--linea2); }
  .p-azul { color:var(--azul); background:var(--azul-tenue); border-color:var(--linea2);
            border-color:color-mix(in srgb, var(--azul) 25%, transparent); }
  /* Pastillas de TIPO DE DOCUMENTO: una clase por token del maestro
     (G_TipoDocumental.Color) — la web traduce el token a 'p-<token>' en la
     puerta única ss.pastillaTipo, nunca a mano por pantalla. Ninguno es
     "bien" ni "mal": solo distinguen tipos entre sí. 'gris' es .p-info. */
  .p-turquesa { color:var(--turquesa); background:var(--turquesa-tenue); border-color:var(--linea2);
                border-color:color-mix(in srgb, var(--turquesa) 25%, transparent); }
  .p-naranja { color:var(--naranja); background:var(--naranja-tenue); border-color:var(--linea2);
               border-color:color-mix(in srgb, var(--naranja) 25%, transparent); }
  /* Parpadeo para los dos CIF a contrastar (leído vs. Ágora, Scan/Resolver):
     verde+parpadea si coinciden, rojo+parpadea si no — para que se note sin
     tener que leer y comparar los dígitos a mano. */
  /* SIN excepción de 'prefers-reduced-motion' (18/8/2026, pedido explícito):
     en Windows con "Mostrar animaciones" desactivado, Chrome reporta
     reduced-motion y apagaba el parpadeo sin que nadie lo notara — el aviso
     de "tipo obligatorio" tiene que verse siempre, pase lo que pase con esa
     preferencia del sistema. */
  .pastilla-parpadea { animation:pastilla-parpadeo 1.4s ease-in-out infinite; }
  @keyframes pastilla-parpadeo { 0%,100% { opacity:1; } 50% { opacity:.35; } }

  /* FICHAS DE SELECCIÓN MÚLTIPLE (26/8/2026, nacen con los destinatarios de
     Gestión Documental). Cuando hay que marcar VARIOS de una lista corta y
     conocida — los departamentos que tramitan un documento, y mañana
     cualquier otra cosa parecida.

     Por qué así y no un '<select multiple>', que era lo obvio:
      · En móvil, un select múltiple nativo es de lo peor que hay — exige
        pulsación mantenida o Ctrl, gestos que con el dedo no existen
        (REGLA WEB, punto 1). Aquí cada opción es UN toque.
      · Todo se ve a la vez: no hay estado escondido detrás de un
        desplegable, así que se lee de un vistazo qué está marcado y qué no.
        Es el patrón "filter chips" de Material y los "tokens" de SAP Fiori,
        recomendado justamente para listas cortas (menos de ~15 opciones);
        por encima de eso lo correcto sería un buscador (Baymard/NN-g), y
        cuando llegue ese caso se añade aquí, no en la pantalla.

     Es un <button aria-pressed>, no un checkbox, para que el área pulsable
     sea la ficha entera y no un cuadradito de 13 px. 40 px de alto mínimo
     en táctil (REGLA WEB, punto 3).                                        */
  .fichas { display:flex; flex-wrap:wrap; gap:6px; align-items:center; }
  .ficha-sel { display:inline-flex; align-items:center; gap:6px;
               min-height:28px; padding:4px 12px; border-radius:20px;
               font-size:12px; font-weight:600; cursor:pointer;
               color:var(--tinta2); background:var(--papel2);
               border:1px solid var(--linea2);
               transition:background .12s, border-color .12s, color .12s; }
  .ficha-sel:hover:not(:disabled) { border-color:var(--azul); }
  .ficha-sel::before { content:""; width:6px; height:6px; border-radius:50%;
                       background:currentColor; opacity:.35; }
  .ficha-sel[aria-pressed="true"] { color:var(--azul); background:var(--azul-tenue);
                                    border-color:color-mix(in srgb, var(--azul) 35%, transparent); }
  .ficha-sel[aria-pressed="true"]::before { opacity:1; }
  .ficha-sel:disabled { cursor:default; opacity:.55; }
  /* EL COLOR ESTÁNDAR DEL TIPO DE DOCUMENTO, TAMBIÉN AQUÍ (26/8/2026,
     pedido explícito: "faltan los colores por defecto estándar"). Cuando
     una ficha es de un TIPO documental (Conceptos la marca con 'p-azul',
     'p-turquesa'…, el mismo token que pinta 'ss.pastillaTipo' en el resto
     del aplicativo — REGLA UN SOLO SITIO, un color por tipo en todo el
     producto), su color de MARCADA es el suyo, no el azul genérico de
     arriba. Sin marcar sigue neutra, igual que una ficha de departamento
     — "marcado" significa lo mismo en toda la pantalla. Cada regla suma
     una clase más que el genérico de arriba, así que gana sin depender
     del orden de las hojas de estilo. */
  .ficha-sel[aria-pressed="true"].p-azul { color:var(--azul); background:var(--azul-tenue);
    border-color:color-mix(in srgb, var(--azul) 35%, transparent); }
  .ficha-sel[aria-pressed="true"].p-turquesa { color:var(--turquesa); background:var(--turquesa-tenue);
    border-color:color-mix(in srgb, var(--turquesa) 35%, transparent); }
  .ficha-sel[aria-pressed="true"].p-naranja { color:var(--naranja); background:var(--naranja-tenue);
    border-color:color-mix(in srgb, var(--naranja) 35%, transparent); }
  .ficha-sel[aria-pressed="true"].p-info { color:var(--tinta2); background:var(--papel2); border-color:var(--linea2); }
  /* Cuando NO hay nada que marcar (el concepto no propone departamentos, o
     la empresa no tiene ninguno): se dice, no se deja el hueco en blanco. */
  .fichas-vacio { font-size:12px; color:var(--tinta2); opacity:.7; }

  /* Sin relleno: el área de trabajo va A HUESO en todo el producto — cada
     pantalla ocupa el hueco entero, pegada a los cuatro bordes (pedido
     explícito, generalizado desde Resolver de Scan). */
  .contenido { flex:1 1 auto; overflow-y:auto; padding:0; }

  /* ---------- Piezas de contenido ---------- */
  .rejilla { display:grid; grid-template-columns:repeat(auto-fill,minmax(260px,1fr)); gap:14px; max-width:1100px; }
  .tarjeta { background:var(--papel); border:1px solid var(--linea); border-radius:var(--r-g);
             padding:16px 18px; box-shadow:var(--sombra); position:relative; overflow:hidden; }
  /* Brillo sutil que recorre el borde superior: un detalle vivo, sin gritar.
     Usa las mismas variables del tema, se adapta solo a claro/oscuro. */
  .tarjeta::after {
    content:""; position:absolute; top:0; left:0; right:0; height:2px;
    background-image:linear-gradient(90deg, transparent, var(--rojo), transparent);
    background-size:50% 100%; background-repeat:no-repeat; background-position:-50% 0;
    animation:ss-brillo 5.5s ease-in-out infinite; opacity:.5; pointer-events:none;
  }
  @keyframes ss-brillo {
    0%, 12%   { background-position:-50% 0; }
    88%, 100% { background-position:150% 0; }
  }
  @media (prefers-reduced-motion: reduce) { .tarjeta::after { animation:none; } }
  .tarjeta h2 { margin:0 0 12px; font-size:11.5px; font-weight:700; letter-spacing:.05em;
                text-transform:uppercase; color:var(--tinta3); }
  .fila { display:flex; justify-content:space-between; gap:12px; padding:8px 0;
          border-bottom:1px solid var(--linea); font-size:13.5px; }
  .fila:last-child { border-bottom:none; }
  .etiqueta { color:var(--tinta2); }
  .valor { font-weight:600; text-align:right; word-break:break-all; }
  .valor.mono { font-family:var(--mono); font-size:12.5px; font-weight:500; }

  .lienzo { border:1px dashed var(--linea2); border-radius:var(--r-g); background:var(--papel);
            padding:40px 22px; text-align:center; color:var(--tinta3); font-size:13.5px; }
  .lienzo-alto { display:flex; align-items:center; justify-content:center; min-height:calc(100vh - 118px); }
  .lienzo-centro { display:flex; flex-direction:column; align-items:center; justify-content:center;
                   gap:8px; min-height:calc(100vh - 200px); }
  .lienzo-titulo { font-size:16px; font-weight:600; color:var(--tinta); }
  .lienzo-texto { color:var(--tinta3); font-size:13px; max-width:46ch; }

  /* --- PORTADA (pantalla de Inicio) --------------------------------------
     Estado del equipo, centrado y sobrio. Sin conexión NO alarma: es un estado
     normal mientras la base arranca, no un fallo. */
  .portada { display:flex; align-items:center; justify-content:center;
             min-height:calc(100vh - 118px); padding:24px; }
  .portada-caja { display:flex; flex-direction:column; align-items:center;
                  text-align:center; gap:12px; max-width:440px; }
  .portada-icono { width:64px; height:64px; display:grid; place-items:center;
                   border-radius:50%; }
  .portada-icono svg { width:38px; height:38px; }
  .portada-titulo { font-size:19px; font-weight:600; color:var(--tinta); }
  .portada-sub { font-size:13.5px; color:var(--tinta3); line-height:1.55; }

  /* Esperando: tono neutro con un latido suave. Nada de rojo de alarma. */
  .portada-caja.esperando .portada-icono {
    background:var(--papel3); color:var(--tinta2);
    animation:latido-portada 2s ease-in-out infinite;
  }
  /* Listo: verde sereno. */
  .portada-caja.conectado .portada-icono { background:var(--verde-tenue); color:var(--verde); }

  @keyframes latido-portada {
    0%, 100% { transform:scale(1);    opacity:.85; }
    50%      { transform:scale(1.06); opacity:1; }
  }
  /* Si el usuario pide menos movimiento, se respeta: nada de animación. */
  @media (prefers-reduced-motion:reduce) {
    .portada-caja.esperando .portada-icono { animation:none; }
  }

  /* --- CAJÓN DE SASTRE: sub-pestañas -------------------------------------
     Banco de pruebas de desarrollo. Sus pestañas van arriba, como en Ágora:
     una fila de botones planos con la activa subrayada en rojo. */
  .taller-tabs { display:flex; gap:4px; border-bottom:1px solid var(--linea);
                 margin-bottom:18px; overflow-x:auto; }
  .taller-tab { flex:0 0 auto; background:none; border:none; cursor:pointer;
                padding:10px 16px; font:inherit; font-size:13.5px; color:var(--tinta2);
                border-bottom:2px solid transparent; margin-bottom:-1px;
                white-space:nowrap; transition:color .12s ease, border-color .12s ease; }
  .taller-tab:hover { color:var(--tinta); }
  .taller-tab.activa { color:var(--rojo); border-bottom-color:var(--rojo); font-weight:600; }
  /* Cada herramienta se monta en su hueco y convive con las demás; solo se ve
     la activa. Así cambiar de pestaña no vuelve a montarla. */
  .taller-panel { display:none; }
  .taller-panel.activo { display:block; }

  /* --- Herramienta "Prompts": lista a la izquierda, texto completo a la
     derecha — igual reparto que cualquier maestro/detalle del producto
     (Regla componentes de interfaz: mismo patrón, no uno nuevo). */
  .prompts { display:flex; gap:18px; align-items:flex-start; }
  .prompts-lista { flex:0 0 260px; display:flex; flex-direction:column; gap:6px;
                    max-height:70vh; overflow-y:auto; }
  .prompts-item { display:flex; flex-direction:column; align-items:flex-start; gap:2px;
                   text-align:left; background:var(--panel2); border:1px solid var(--linea);
                   border-radius:var(--r-g); padding:9px 12px; cursor:pointer; font:inherit; }
  .prompts-item:hover { border-color:var(--rojo); }
  .prompts-item.activo { border-color:var(--rojo); background:var(--rojo-tenue); }
  .prompts-item-nombre { font-size:13.5px; font-weight:600; }
  .prompts-item-codigo { font-size:11.5px; color:var(--tinta2); font-family:monospace; }
  .prompts-vacio { color:var(--tinta2); font-size:13px; padding:8px; }
  .prompts-detalle { flex:1 1 auto; min-width:0; }
  .prompts-cab { display:flex; justify-content:space-between; align-items:center;
                  gap:12px; margin-bottom:10px; flex-wrap:wrap; }
  .prompts-cab-info h3 { margin:0 0 2px; font-size:15px; }
  .prompts-meta { font-size:12px; color:var(--tinta2); }
  .prompts-cab-acciones { display:flex; gap:8px; }
  .prompts-texto { white-space:pre-wrap; word-break:break-word; font-family:monospace;
                    font-size:12.5px; line-height:1.55; background:var(--panel2);
                    border:1px solid var(--linea); border-radius:var(--r-g); padding:16px;
                    max-height:70vh; overflow-y:auto; margin:0; }
  .prompts-textarea { width:100%; min-height:70vh; box-sizing:border-box; resize:vertical;
                       font-family:monospace; font-size:12.5px; line-height:1.55;
                       background:var(--panel2); color:var(--tinta); border:1px solid var(--linea);
                       border-radius:var(--r-g); padding:16px; }
  @media (max-width:767px) {
    .prompts { flex-direction:column; }
    .prompts-lista { flex:none; width:100%; max-height:220px; }
  }

  /* --- MENÚ DE ACCESOS: tarjetas clicables dentro de un módulo (p. ej.
     Administración → Perfiles de Usuario / Usuarios). Mismo lenguaje que las
     tarjetas de estado (borde fino, esquinas suaves, sombra apenas
     insinuada), pero pensadas para navegar, no para mostrar datos.

     REGLA APROVECHAMIENTO DEL ÁREA DE TRABAJO: cuando el contenido no llena
     el ancho disponible (pocos accesos), el bloque se CENTRA y las tarjetas
     CRECEN para ocupar el hueco — nunca se quedan arracimadas arriba a la
     izquierda con media pantalla vacía al lado. 'auto-fit' (no 'auto-fill')
     es la clave: las columnas vacías se colapsan y su espacio se reparte
     entre las tarjetas reales, así 2 tarjetas ocupan el ancho de 2, no se
     quedan estrechas dejando columnas fantasma sin usar. Con 'margin:auto'
     el bloque entero se centra dentro de un ancho máximo cómodo de leer.
     Aplica a cualquier pantalla-menú de accesos, no solo a Administración. --- */
  .menu-grupo-titulo { font-size:11.5px; font-weight:700; letter-spacing:.05em;
                       text-transform:uppercase; color:var(--tinta3); margin:0 0 12px; }
  .menu-rejilla { display:grid; grid-template-columns:repeat(auto-fit,minmax(260px,1fr));
                  gap:16px; max-width:1300px; margin:0 auto; }
  /* Centrado (pedido explícito 17/8/2026): icono arriba, texto debajo, todo
     centrado — antes iba en fila (icono izquierda, texto izquierda). El
     contador ya no puede empujarse con 'margin-left:auto' (no hay fila que
     empujar), así que pasa a esquina superior derecha, absoluto. */
  .menu-item { position:relative; display:flex; flex-direction:column; align-items:center;
               gap:10px; padding:24px 20px;
               background:var(--papel); border:1px solid var(--linea); border-radius:var(--r-g);
               box-shadow:var(--sombra); cursor:pointer; text-align:center; font:inherit;
               color:inherit; transition:border-color .12s ease, background .12s ease; }
  .menu-item:hover, .menu-item:focus-visible { border-color:var(--rojo); background:var(--rojo-tenue); }
  /* Variante MENOS ALTA (18/8/2026, pedido explícito: "menos alto para
     aprovechar pantalla") — hoy solo la usa el menú de Scan, pero es un
     modificador general (REGLA COMPONENTES): reduce el padding y el icono,
     nunca cambia el resto del patrón .menu-item. */
  .menu-rejilla-compacta .menu-item { padding:14px 16px; gap:8px; flex-direction:row; text-align:left; }
  .menu-rejilla-compacta .menu-item-icono { width:36px; height:36px; }
  .menu-rejilla-compacta .menu-item-icono svg { width:18px; height:18px; }
  .menu-rejilla-compacta .menu-item-contador { top:8px; right:8px; }
  /* Variante MINI (20/8/2026, pedido explícito: "existe en Archivo, pero
     necesito los botones menor alto" — una barra de FILTRO pegada encima
     de una tabla, no un menú de navegación: sin icono, una línea, el
     contador junto al nombre en vez de credencial flotante en la esquina.
     Mismo patrón .menu-item/.menu-item-nombre/.menu-item-sub que el resto
     del producto — REGLA COMPONENTES: solo cambia tamaño, nunca el
     mecanismo (resaltado por 'style.cssText' inline al elegirla, igual que
     ya hace 'archivo.js'). */
  .menu-rejilla-mini { gap:8px; margin:0; max-width:none; }
  .menu-rejilla-mini .menu-item { flex-direction:row; align-items:baseline; gap:6px;
                                   padding:6px 14px; box-shadow:none; }
  .menu-rejilla-mini .menu-item-nombre { font-size:13px; }
  .menu-rejilla-mini .menu-item-sub { font-size:13px; font-weight:700; margin-top:0; color:inherit; }
  .menu-item-icono { width:46px; height:46px; border-radius:var(--r); background:var(--papel3);
                     color:var(--tinta2); display:grid; place-items:center; flex:0 0 auto;
                     transition:background .12s ease, color .12s ease; }
  .menu-item:hover .menu-item-icono, .menu-item:focus-visible .menu-item-icono { background:var(--rojo); color:#fff; }
  .menu-item-icono svg { width:22px; height:22px; }
  .menu-item-nombre { font-size:15px; font-weight:700; }
  .menu-item-sub { font-size:12.5px; color:var(--tinta3); margin-top:2px; }
  /* Contador de la tarjeta (cuántos documentos esperan dentro): esquina
     superior derecha, absoluto (el diseño centrado ya no tiene una fila que
     lo empuje con auto-margin). '.alerta' (rojo) para lo que espera una
     decisión humana; sin ella, informativo neutro. Componente GENERAL de
     .menu-item (REGLA COMPONENTES DE INTERFAZ): cualquier menú de tarjetas
     futuro lo reutiliza tal cual. */
  .menu-item-contador { position:absolute; top:12px; right:12px; flex:none; min-width:28px; height:28px;
                        display:inline-flex; align-items:center; justify-content:center;
                        padding:0 8px; border-radius:14px; font-size:13px; font-weight:700;
                        font-variant-numeric:tabular-nums;
                        background:var(--fondo2, var(--rojo-tenue)); color:var(--tinta3);
                        border:1px solid var(--linea); }
  .menu-item-contador.alerta { background:var(--rojo); color:#fff; border-color:var(--rojo); }

  /* --- PUERTA DE SESIÓN: velo a pantalla completa, por ENCIMA de todo
     (barra lateral incluida) — mientras esté visible, no se puede navegar a
     ningún sitio. Solo existe si el módulo Ágora está activo y no hay
     sesión; el resto del tiempo 'hidden' y no ocupa nada. --- */
  .sesion-velo { position:fixed; inset:0; z-index:2000; background:var(--fondo);
                display:flex; align-items:center; justify-content:center; overflow-y:auto; }

  /* --- INICIO DE SESIÓN: tarjeta centrada, sobria, mismo lenguaje del
     producto (marca roja, sin adornos). Solo aparece cuando el módulo Ágora
     está activo y no hay sesión — el resto del tiempo no existe. --- */
  .login-envoltorio { display:flex; align-items:center; justify-content:center;
                      min-height:min(60vh, 520px); padding:24px; }
  .login-caja { width:100%; max-width:360px; }
  .login-icono { width:44px; height:44px; border-radius:50%; background:var(--rojo); color:#fff;
                display:grid; place-items:center; margin:0 auto 14px; }
  .login-icono svg { width:22px; height:22px; }
  .login-titulo { font-size:17px; font-weight:700; text-align:center; margin-bottom:3px; }
  .login-sub { font-size:12.5px; color:var(--tinta3); text-align:center; margin-bottom:22px; }
  .login-campos { display:grid; gap:14px; }
  .login-recordar { display:flex; align-items:center; gap:8px; font-size:12.5px; color:var(--tinta2); }
  .login-recordar input { width:15px; height:15px; accent-color:var(--rojo); cursor:pointer; }
  .login-error { font-size:12.5px; color:var(--rojo-estado); background:var(--rojo-tenue);
                border:1px solid var(--rojo); border-radius:var(--r-s); padding:8px 10px; }
  .login-entrar { width:100%; margin-top:2px; }
  .login-reloj { margin-top:16px; text-align:center; font-family:var(--mono); font-size:12px; color:var(--tinta3); }
  .sesion-usuario { display:flex; align-items:center; gap:8px; font-size:12.5px; color:var(--tinta2); margin-right:4px;
                     min-width:0; flex:0 1 auto; }
  .sesion-usuario svg { flex:0 0 auto; }
  .sesion-usuario #sesionNombre { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  /* Botón de salir: en escritorio texto (y el icono estorbaría), en móvil solo
     icono. La variante la deciden los @media, aquí solo el punto de partida. */
  .boton-salir { display:inline-flex; align-items:center; gap:7px; flex:0 0 auto; }
  .boton-salir .boton-salir-icono { display:none; width:17px; height:17px; }

  /* Cabecera de una sub-pantalla alcanzada desde un menú de accesos: flecha
     de volver + migas de ubicación, mismo patrón en cualquier módulo que la
     use — ES LA BARRA GLOBAL DE "DÓNDE ESTOY" (pedido explícito, 14/8/2026:
     más ajustada de alto y que destaque más). Compacta, tipografía con
     más peso, y FIJA arriba al desplazar (sticky respecto al scroll de
     '.contenido'): la ubicación nunca se pierde de vista. */
  /* Doble clase a propósito: '.panel-titulo' (definida más abajo) fija
     14px/600 y con un solo nombre esta regla perdía por orden — la barra
     de migas debe ganar SIEMPRE su propia letra. */
  .subpantalla-cab, .panel-titulo.subpantalla-cab {
                     display:flex; align-items:center; gap:10px;
                     font-size:13.5px; font-weight:650; letter-spacing:-.01em; }
  .subpantalla-volver { display:inline-flex; align-items:center; justify-content:center;
                        width:26px; height:26px; border:1px solid var(--control-borde); border-radius:var(--r-s);
                        background:var(--control-fondo); color:var(--tinta2); cursor:pointer; flex:0 0 auto; }
  .subpantalla-volver:hover { border-color:var(--rojo); color:var(--rojo); }
  .subpantalla-volver svg { width:14px; height:14px; }
  /* La cabecera de panel que lleva las migas: más ajustada que una cabecera
     normal y pegada arriba al hacer scroll. ':has()' está dentro de la
     línea base de navegadores del producto (Chrome/Edge 110+, Safari 16+). */
  .panel-cabecera:has(.subpantalla-cab) { position:sticky; top:0; z-index:5;
                                          padding:7px 14px; background:var(--papel);
                                          box-shadow:0 1px 0 var(--linea), 0 4px 10px -8px rgba(16,24,40,.25); }
  /* Navegador de documentos del Resolver: anterior / "3 de 27" / siguiente,
     pegado a las migas — mismos botones que la flecha de volver. */
  .resolver-nav { display:inline-flex; align-items:center; gap:6px; margin-left:6px; }
  .resolver-nav-pos { font-size:12px; color:var(--tinta3); font-variant-numeric:tabular-nums;
                      min-width:48px; text-align:center; }
  .subpantalla-volver:disabled { opacity:.35; cursor:default; }
  .subpantalla-volver:disabled:hover { border-color:var(--linea2); color:var(--tinta2); }

  /* --- GESTIÓN DE FICHEROS ------------------------------------------------ */
  .doc-barra { display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-bottom:14px; }
  .doc-etq { font-size:13px; color:var(--tinta2); font-weight:600; }
  .doc select { font:inherit; font-size:13.5px; color:var(--tinta); background:var(--papel);
                border:1px solid var(--linea2); border-radius:var(--r); padding:7px 10px; min-width:140px; }
  /* Zona de arrastrar y soltar (solo ratón). Se ilumina al pasar un fichero. */
  /* Alto contenido, ancho aprovechado (18/8/2026, pedido explícito:
     "optimizar alto, y aprovechar ancho") — icono y texto en fila, no
     apilados, para que la caja no sea más alta de lo necesario. En móvil
     (ancho < 480px, REGLA WEB) vuelve a apilarse: en fila no cabría. */
  .doc-soltar { border:1.5px dashed var(--linea2); border-radius:var(--r-g); padding:14px 20px;
                display:flex; flex-wrap:wrap; align-items:center; justify-content:center; gap:14px;
                text-align:center; color:var(--tinta3); font-size:13.5px;
                transition:border-color .15s ease, background .15s ease, color .15s ease; }
  .doc-soltar svg { flex:none; width:30px; height:30px; }
  .doc-soltar .acciones-fila { margin-top:0 !important; }
  @media (max-width:480px) { .doc-soltar { flex-direction:column; padding:22px 16px; } }
  .doc-soltar.encima { border-color:var(--rojo); background:var(--rojo-tenue); color:var(--tinta); }
  /* Bloqueada mientras no haya tipo elegido (18/8/2026, pedido explícito):
     el arrastre en sí sigue posible (no se puede desactivar 'drop' del
     navegador con CSS), pero 'subirGlobalFicheros' rechaza igual — esto
     solo lo deja CLARO a la vista antes de intentarlo. */
  .doc-soltar-bloqueado { opacity:.55; }
  /* Barra de progreso AZUL mientras el fichero viaja. */
  /* Cola de subida: una fila por fichero. */
  .doc-cola { margin-top:14px; display:flex; flex-direction:column; gap:10px; }
  .doc-cola:empty { display:none; }
  .doc-item { border:1px solid var(--linea); border-radius:var(--r); padding:9px 11px; background:var(--papel); }
  .doc-item-cab { display:flex; justify-content:space-between; align-items:baseline; gap:12px; margin-bottom:6px; }
  .doc-item-nombre { font-size:12.5px; color:var(--tinta2); overflow:hidden;
                     text-overflow:ellipsis; white-space:nowrap; }
  .doc-item-estado { font-size:12px; font-weight:600; color:#2f6fed; flex:0 0 auto; }
  .doc-item.hecho .doc-item-estado { color:var(--verde); }
  .doc-item.hecho .doc-barra-relleno { background:var(--verde); }
  .doc-item.error .doc-item-estado { color:var(--rojo); }
  .doc-item.error .doc-barra-relleno { background:var(--rojo); width:100% !important; }
  .doc-barra-progreso { height:8px; background:var(--papel3); border-radius:999px; overflow:hidden; }
  .doc-barra-relleno { height:100%; width:0; background:#2f6fed; border-radius:999px; transition:width .12s ease; }
  /* Sin porcentaje medible (redimensionando): barra que va y viene, para que se
     vea que se está trabajando aunque no se sepa cuánto falta. */
  .doc-barra-relleno.indeterminada { width:40% !important; transition:none;
    animation:doc-indet 1.1s ease-in-out infinite; }
  @keyframes doc-indet { 0% { margin-left:-40%; } 100% { margin-left:100%; } }
  .doc-titulo { font-size:13.5px; font-weight:600; color:var(--tinta); margin-bottom:10px; }

  /* Filas de la lista: pinchables para ver el documento. */
  #docLista .doc-fila { cursor:pointer; }
  #docLista .doc-fila:hover { background:var(--papel2); }

  /* Visor de documentos: grande, con cabecera y cuerpo que se adapta al tipo. */
  /* El visor ocupa casi toda la pantalla: cuanto más grande, mejor se ve el
     documento. La cabecera es lo más fina posible para no robar altura. */
  /* ALTO FIJO al 90% de la pantalla, SIEMPRE (pedido explícito 17/8/2026:
     "no sale a toda la pantalla por defecto") — sin esto, un <dialog> nativo
     se dimensiona a su CONTENIDO: con la imagen en poco zoom se ve pequeño,
     y al ampliar zoom el propio diálogo (no un hueco interior) crecía más
     allá de la ventana — eso es lo que se llevaba la barra de herramientas
     fuera de la vista ("al hacer zoom se pierden los botones"). Con
     'display:flex' en columna y alto fijo, la cabecera y la barra quedan
     SIEMPRE visibles y solo el documento se desplaza por dentro. */
  /* AL MÁXIMO (pedido explícito 17/8/2026: "mejor lo máximo que puedas") —
     prácticamente toda la pantalla, no solo el 90%. */
  dialog.visor { padding:0; border:1px solid var(--linea2); border-radius:var(--r-g);
                 width:99vw; max-width:99vw; height:97vh; max-height:97vh;
                 background:var(--papel); color:var(--tinta);
                 box-shadow:var(--sombra-menu); }
  /* 'display' SOLO cuando está abierto — igual que '.dialogo-pantalla-
     completa' (mismo bug si no: el <dialog> recién creado y aún cerrado se
     queda visible en flujo normal, pisando el 'display:none' de fábrica). */
  dialog.visor[open] { display:flex; flex-direction:column; }
  dialog.visor::backdrop { background:rgba(16,24,40,.6); }
  .visor-cab { flex:0 0 auto; display:flex; align-items:center; justify-content:space-between; gap:12px;
               padding:5px 6px 5px 14px; border-bottom:1px solid var(--linea); }
  .visor-nombre { flex:1 1 auto; min-width:0; font-size:13px; font-weight:600; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .visor-acciones { display:flex; align-items:center; gap:8px; flex:0 0 auto; }
  /* Botones compactos dentro del visor (no engordan la cabecera). */
  .visor-cab .boton { min-height:0; height:28px; padding:0 12px; font-size:12.5px; }
  /* Cerrar: una X a cada lado, cómoda para el pulgar (>=40px pulsables). */
  .visor-x { flex:0 0 auto; width:40px; height:32px; display:inline-flex; align-items:center;
             justify-content:center; border:1px solid var(--linea2); border-radius:var(--r-s);
             background:var(--papel); color:var(--tinta2); font-size:22px; line-height:1;
             cursor:pointer; }
  .visor-x:hover { background:var(--fondo); color:var(--tinta); }
  /* 'flex:1;min-height:0' (no 'max-height:calc(...)'): el hueco es EXACTO,
     lo que sobra del alto fijo del diálogo tras la cabecera — así el
     '.visor-img-raiz'/'visor-pdf' de dentro, con 'height:100%', tiene un
     100% real contra el que medirse (un padre con solo 'max-height' no
     sirve: un 'height:100%' hijo se resuelve como 'auto' y el contenido
     puede crecer sin límite, exactamente el fallo de la barra perdida). */
  .visor-cuerpo { flex:1 1 auto; min-height:0; padding:14px; display:flex; align-items:center; justify-content:center;
                  overflow:auto; background:var(--fondo); }
  /* El PDF llena el cuerpo sin centrarse (tiene su propia zona de scroll). */
  .visor-cuerpo:has(.visor-pdf) { align-items:stretch; padding:0; overflow:hidden; }
  /* La imagen con zoom también lleva su propia barra arriba: mismo
     tratamiento — el cuerpo deja de centrar y pasa a apilar en columna. */
  .visor-cuerpo:has(.visor-img-marco) { flex-direction:column; align-items:stretch; padding:0; overflow:hidden; }
  /* Envuelve barra + marco (mismo papel que '.visor-pdf' en el PDF): pantalla
     completa se pide sobre ESTE elemento para que la barra no se quede fuera. */
  .visor-img-raiz { display:flex; flex-direction:column; width:100%; height:100%; background:var(--fondo); position:relative; }
  .visor-img-raiz:fullscreen { height:100vh; width:100vw; }
  /* Barra de zoom de la imagen: mismos botones/porcentaje que el PDF, pero
     en flujo normal (no absoluta) — la imagen no tiene páginas que la
     empujen a ocupar todo el hueco por debajo. */
  .visor-img-barra { display:flex; align-items:center; justify-content:center; gap:8px;
                     padding:8px 12px; background:var(--papel); border-bottom:1px solid var(--linea); flex:none; }
  /* SIN 'align-items:center'/'justify-content:center': cuando la imagen
     desborda (zoom), un contenedor flex centrado reparte el desbordamiento
     a los DOS lados de cada eje, pero el navegador no puede desplazar
     hacia el lado negativo — la parte de arriba (o la izquierda) queda
     inaccesible aunque exista (bug real, detectado con zoom aplicado). El
     PDF nunca ha tenido este problema porque no centra sus páginas; la
     imagen ahora tampoco — empieza arriba a la izquierda, como el PDF. */
  .visor-img-marco { flex:1; min-height:0; overflow:auto; position:relative; }

  /* --- CUANDO EL DOCUMENTO NO SE PUEDE MOSTRAR (22/8/2026) -------------
     Sustituye a la imagen rota del navegador (icono de página partida +
     el nombre del fichero en crudo sobre un hueco vacío), que parecía un
     fallo del programa. Aquí: icono tenue centrado y dos líneas sobrias.
     Gris neutro, sin rojo: que el fichero no esté no es un ERROR del que
     mira — es un estado, y se cuenta con calma. */
  .visor-vacio { position:absolute; inset:0; display:flex; flex-direction:column;
                 align-items:center; justify-content:center; gap:10px; padding:24px;
                 text-align:center; color:var(--tinta3); }
  .visor-vacio svg { width:44px; height:44px; opacity:.55; }
  .visor-vacio-titulo { font-size:14px; font-weight:600; color:var(--tinta2); }
  .visor-vacio-pie { font-size:12.5px; max-width:32ch; line-height:1.45; }
  /* Recuadro de "localizar campo" (20/8/2026, pedido explícito: botón
     derecho en la tabla -> caja roja sobre la imagen). Posición y tamaño
     los fija JS en píxeles (ver 'marco._verCaja' en visor.js) — aquí solo
     el aspecto: borde rojo bien visible, sin robar clics a la imagen de
     debajo, y un parpadeo breve para que se note dónde ha saltado la vista. */
  .visor-img-marca-campo { position:absolute; border:3px solid var(--rojo); border-radius:4px;
                            box-shadow:0 0 0 1px rgba(255,255,255,.6), 0 0 10px rgba(0,0,0,.35);
                            pointer-events:none; z-index:4; }
  .visor-img-marca-campo.visor-img-marca-parpadea { animation:marca-campo-parpadeo 0.5s ease-out 3; }
  @keyframes marca-campo-parpadeo { 0%,100% { opacity:1; } 50% { opacity:.25; } }
  /* Indicador de "doble clic localiza en la imagen" (20/8/2026, pedido
     explícito: "una marquita en el campo que no ocupe lugar"). Un punto
     pequeño en la esquina, con 'position:absolute' — CERO influencia en el
     alto/ancho de la celda o de la fila; nunca descoloca la tabla. */
  /* Genérico (no solo 'td'/'div'): el TOTAL de cabecera es un '<span>'
     (20/8/2026, fallo real visto en pantalla — el punto no salía ahí
     porque el selector solo cubría celda/bloque). 'position:relative' en
     un span inline no le cambia el flujo — el '::after' se ancla igual. */
  .col-con-caja { position:relative; cursor:pointer; }
  .col-con-caja::after {
    content:""; position:absolute; top:2px; right:2px;
    width:6px; height:6px; border-radius:50%;
    background:var(--rojo); opacity:.45; pointer-events:none;
  }
  .col-con-caja:hover::after { opacity:.9; }
  /* Arrastrar para desplazar (clic mantenido + mover), compartido por la
     imagen y el PDF (su '.visor-pdf-cont' también lleva esta clase). */
  .visor-arrastrable { cursor:grab; touch-action:none; }
  /* Botón flotante de recorte (solo táctil): tijeras sobre el documento,
     44px de zona pulsable (REGLA WEB). Encendido = rojo del producto. */
  .recorte-fab { position:absolute; right:14px; bottom:14px; z-index:6;
                 width:44px; height:44px; border-radius:50%; display:grid; place-items:center;
                 border:1px solid var(--control-borde); background:var(--control-fondo); color:var(--tinta);
                 box-shadow:var(--sombra); cursor:pointer; }
  .recorte-fab svg { width:20px; height:20px; }
  .recorte-fab.activo { background:var(--rojo); border-color:var(--rojo); color:#fff; }
  /* Modo recorte táctil ENCENDIDO: el visor entero deja de responder a los
     gestos táctiles del navegador (scroll/zoom) para que el arrastre del
     dedo dibuje el rectángulo sin que iOS/Android lo cancelen a mitad
     (pointercancel por pan nativo — fallo real en tablet, 14/8/2026). */
  .recorte-tactil-activo, .recorte-tactil-activo * { touch-action:none; }
  .visor-arrastrable.arrastrando { cursor:grabbing; }
  /* Tamaño BASE, pensado para ir dentro de OTRO contenedor con altura ya
     fijada (p. ej. el Resolver de Scan, 1/3 del ancho). Dentro del diálogo
     ('.visor-cuerpo') se agranda a casi toda la ventana — reglas debajo. */
  /* 'display:block + margin:auto': centrada cuando SOBRA sitio (pantalla
     completa, imagen estrecha) y sin romper el desplazamiento cuando la
     imagen es MÁS GRANDE que el hueco (margin:auto colapsa a 0 y el
     overflow del marco sigue mandando) — al contrario que el centrado con
     flex del contenedor, que hacía inaccesible la parte alta (ya mordió). */
  .visor-img { display:block; margin:auto; max-width:100%; max-height:100%; object-fit:contain; border-radius:var(--r-s); transform-origin:center center; }
  .visor-video { width:100%; height:100%; border:none; border-radius:var(--r-s); background:#000; }
  .visor-cuerpo .visor-video { height:100%; }
  /* PDF: barra propia arriba + zona de scroll con el componente oficial (pdf.js)
     dentro. La zona va en absoluto porque el componente posiciona sus páginas
     respecto a ella. */
  /* La zona de páginas (.visor-pdf-cont) DEBE ir en posición absoluta: es
     requisito del componente oficial. La barra va encima (también absoluta) y
     puede ocupar varias líneas si el buscador no cabe; el JS ajusta el 'top' del
     contenedor a la altura real de la barra (ResizeObserver), así nunca la pisa. */
  .visor-pdf { position:relative; width:100%; height:100%; background:var(--fondo); }
  .visor-cuerpo .visor-pdf { height:100%; }
  .visor-pdf:fullscreen { height:100vh; width:100vw; }

  /* Reserva cuando la API de pantalla completa del navegador falla o no
     existe (política del equipo, navegador antiguo...): mismo efecto visual
     conseguido a pulso con CSS, para que el botón nunca se quede callado. */
  .visor-pantalla-simulada { position:fixed !important; inset:0 !important; width:100vw !important; height:100vh !important; z-index:5000; }

  /* Rectángulo de selección al recortar una zona (Ctrl + arrastrar). */
  .visor-recorte-marco { position:fixed; border:2px dashed var(--rojo); background:rgba(212,35,42,.12); z-index:9999; pointer-events:none; }
  .caract-recorte-vista { max-width:100%; max-height:200px; border-radius:var(--r-s); border:1px solid var(--linea); display:block; margin-bottom:8px; }
  .caract-item-mini { width:44px; height:44px; object-fit:cover; border-radius:var(--r-s); border:1px solid var(--linea); flex:none; }
  .visor-pdf-barra { position:absolute; top:0; left:0; right:0; z-index:2;
                     display:flex; align-items:center; gap:8px 12px; flex-wrap:wrap;
                     padding:8px 12px; background:var(--papel); border-bottom:1px solid var(--linea); }
  .visor-pdf-cont { position:absolute; top:47px; left:0; right:0; bottom:0; overflow:auto; }
  .visor-pdf-grupo { display:flex; align-items:center; gap:4px; }
  .visor-pdf-grupo:last-child { flex:1 1 140px; }   /* el buscador ocupa lo que sobre */
  .visor-pdf-boton { display:inline-flex; align-items:center; justify-content:center; width:30px; height:30px;
                     border:1px solid var(--control-borde); border-radius:var(--r-s); background:var(--control-fondo);
                     color:var(--tinta); font-size:16px; line-height:1; cursor:pointer; }
  .visor-pdf-boton svg { width:16px; height:16px; flex:0 0 auto; }
  .visor-pdf-boton:hover { background:var(--fondo2, var(--fondo)); }
  .visor-pdf-pag { width:44px; height:30px; text-align:center; border:1px solid var(--linea2);
                   border-radius:var(--r-s); background:var(--papel); color:var(--tinta); font-size:13px; }
  .visor-pdf-total, .visor-pdf-zoom { font-size:13px; color:var(--tinta3); min-width:34px; }
  .visor-pdf-zoom { text-align:center; }
  .visor-pdf-buscar { height:30px; width:100%; min-width:110px; padding:0 10px; border:1px solid var(--linea2);
                      border-radius:var(--r-s); background:var(--papel); color:var(--tinta); font-size:13px; }
  .visor-texto { width:100%; margin:0; padding:14px; font-family:var(--mono); font-size:12.5px;
                 line-height:1.5; color:var(--tinta); background:var(--papel); border-radius:var(--r-s);
                 white-space:pre-wrap; word-break:break-word; }
  .visor-aviso { text-align:center; color:var(--tinta3); font-size:13.5px; padding:34px 18px; display:grid; gap:14px; justify-items:center; }

  /* CONTENEDOR con barra de utilidades, para hoja y Word (26/8/2026,
     REGLA VISOR: las mismas opciones que imagen y PDF). Mismo esqueleto que
     '.visor-pdf': barra flotante arriba y el documento debajo, desplazándose
     dentro. 'position:relative' porque la barra va absoluta sobre él, y
     'min-*:0' por lo de siempre con flex (ver '.visor-hoja'). */
  .visor-doc { position:relative; display:flex; flex-direction:column;
               width:100%; height:100%; min-height:0; min-width:0; }

  /* LA BARRA EMPUJA, NO TAPA (26/8/2026, fallo real visto en un Android: la
     barra flotaba SOBRE la carta y escondía las primeras líneas y el
     membrete; al estrecharse se partía en dos filas y tapaba aún más).
     En el PDF y la imagen flota a propósito —ahí sobra sitio y se gana
     lienzo—, pero un DOCUMENTO se lee desde la primera línea: tapar el
     principio es tapar justo lo que se ha venido a ver. Como '.visor-doc'
     ya es una columna flex, basta con devolverla al flujo. */
  .visor-doc .visor-pdf-barra { position:static; flex:0 0 auto; }
  .visor-doc-cont { flex:1 1 auto; min-height:0; min-width:0; overflow:auto; display:flex; }

  /* EL ÚLTIMO GRUPO NO SE ESTIRA AQUÍ (26/8/2026, medido en móvil: la barra
     ocupaba 105px en TRES filas). '.visor-pdf-grupo:last-child' lleva
     'flex:1 1 140px' para que el BUSCADOR del PDF se coma lo que sobre —
     correcto allí, pero en esta barra el último grupo son dos botones y se
     estiraba a 351px, empujando el resto a filas nuevas. Sin buscador que
     alimentar, cada grupo ocupa lo suyo y todo cabe en una línea. */
  .visor-doc .visor-pdf-grupo:last-child { flex:0 0 auto; }

  /* HOJA DE CÁLCULO y WORD en el visor (26/8/2026). La rejilla NO lleva
     estilo propio: es 'table.tabla' del sistema, con su orden por columna,
     sus anchos y su ficha de móvil. Aquí solo se resuelve el envoltorio —
     pestañas de hoja arriba, contenido debajo, y que el desplazamiento
     ocurra DENTRO y no en la página (REGLA WEB). */
  /* 'min-width:0' Y 'min-height:0' — LOS DOS, y por el mismo motivo (fallo
     real medido el 26/8/2026 en un Android: la hoja salía de 1774px dentro
     de una ventana de 375, con media rejilla inalcanzable). Un elemento flex
     nace con 'min-width:auto', que significa "no encojas por debajo de tu
     contenido": con una tabla ancha dentro, la caja crece en vez de
     desplazarse, y el desplazamiento no aparece nunca. Ponerlo a 0 es lo que
     autoriza a encoger, y solo entonces el 'overflow:auto' del panel hace su
     trabajo. Es la trampa clásica de flex, y se paga en el móvil. */
  .visor-hoja { display:flex; flex-direction:column; height:100%;
                min-height:0; min-width:0; max-width:100%; gap:10px; padding:12px; }
  .visor-hoja-pestanas { display:flex; flex-wrap:wrap; gap:6px; flex:0 0 auto; }
  .visor-hoja-panel { flex:1 1 auto; min-height:0; min-width:0; max-width:100%; overflow:auto; }
  /* La caja de la tabla nunca empuja: se queda en el ancho del panel y es
     ella quien desplaza (REGLA WEB — lo ancho se mueve dentro de su caja). */
  .visor-hoja .tabla-caja { max-width:100%; overflow-x:auto; -webkit-overflow-scrolling:touch; }
  .visor-hoja-tope { display:flex; flex-wrap:wrap; align-items:center; justify-content:center; gap:12px;
                     padding:12px; font-size:12.5px; color:var(--tinta3);
                     border-top:1px solid var(--linea2); }

  /* LA REJILLA DE UNA HOJA (26/8/2026). Cabecera PEGADA arriba: al bajar por
     una hoja larga se sigue sabiendo qué columna se está mirando, que es la
     diferencia entre poder leerla y no. */
  /* '1em' Y NO PÍXELES: es lo que hace que el zoom de la barra funcione de
     verdad (26/8/2026, fallo real medido — la etiqueta subía a 120% pero la
     letra se quedaba en 13,5px). Las reglas de '.tabla' fijan el tamaño en
     px, así que la celda NO heredaba el porcentaje que el zoom pone en
     '.visor-hoja'. Con 'em' hereda, y un botón que dice 120% enseña 120%. */
  /* La TABLA también, no solo sus celdas (26/8/2026, medido: '.visor-hoja'
     subía bien a 16,2px pero la tabla se quedaba en 13,5 — 'table.tabla'
     fija su propio tamaño, y un '1em' en la celda es 1em DE LA TABLA, no de
     la raíz. Sin esta línea el zoom movía la etiqueta y nada más). */
  .visor-hoja .hoja-rejilla,
  .visor-hoja .hoja-rejilla th,
  .visor-hoja .hoja-rejilla td { font-size:inherit; }
  /* La base por 'var(--zoom-doc)', que pone la barra. Con 'calc' sobre un
     tamaño FIJO el resultado es exacto y predecible; un porcentaje suelto se
     calcularía sobre el padre y el 100% ya no sería el tamaño de partida. */
  .visor-hoja { font-size:calc(13.5px * var(--zoom-doc, 1)); }

  .hoja-rejilla { width:max-content; min-width:100%; }
  .hoja-rejilla thead th { position:sticky; top:0; z-index:1; background:var(--fondo2); }
  /* Celdas en UNA línea: en una hoja, una celda larga que parta en cinco
     renglones descoloca toda la rejilla. Se corta con puntos suspensivos y
     el valor entero queda en el 'title'. */
  .hoja-rejilla td { max-width:280px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }

  /* Word: ancho de LECTURA y tipografía del producto — el .docx original no
     manda en el estilo a propósito (un documento con letra de 8 puntos o
     colores raros se sigue leyendo bien aquí). */
  /* 'width:100%' y 'min-width:0' porque esto vive dentro de un flex
     ('.visor-cuerpo'): sin ellos el bloque se ENCOGE A SU CONTENIDO en vez
     de ocupar el ancho disponible — medido el 26/8/2026: 366px de columna
     de lectura dentro de una ventana de 1223, con el resto en blanco. Con
     esto llega hasta 80ch y se centra, que es lo que se buscaba. */
  .visor-word { width:100%; min-width:0; max-width:80ch; margin:0 auto;
                padding:24px 20px; color:var(--tinta);
                font-size:calc(14px * var(--zoom-doc, 1)); line-height:1.65;
                overflow-wrap:break-word; }
  .visor-word h1, .visor-word h2, .visor-word h3,
  .visor-word h4, .visor-word h5, .visor-word h6 { margin:1.4em 0 .5em; line-height:1.3; }
  .visor-word h1 { font-size:1.5em; } .visor-word h2 { font-size:1.3em; } .visor-word h3 { font-size:1.15em; }
  .visor-word p { margin:0 0 .9em; }
  .visor-word ul, .visor-word ol { margin:0 0 .9em; padding-left:1.6em; }
  .visor-word li { margin:.25em 0; }
  .visor-word blockquote { margin:0 0 .9em; padding:.2em 0 .2em 1em;
                           border-left:3px solid var(--linea2); color:var(--tinta2); }
  .visor-word a { color:var(--marca); }
  /* Las imágenes del documento: nunca más anchas que la columna de lectura
     (una captura de 1920px reventaría el ancho y sacaría desplazamiento
     lateral en el móvil). 'height:auto' conserva la proporción. */
  .visor-word img { display:block; max-width:100%; height:auto; margin:1em auto;
                    border-radius:var(--r-s); border:1px solid var(--linea2); }
  .visor-word pre { white-space:pre-wrap; font-family:var(--mono); font-size:12.5px; }
  /* Una tabla dentro del Word se desplaza en su caja, nunca de lado la página. */
  .visor-word table { display:block; overflow-x:auto; max-width:100%; border-collapse:collapse; margin:0 0 1em; }
  .visor-word th, .visor-word td { border:1px solid var(--linea2); padding:6px 9px; text-align:left; vertical-align:top; }
  .visor-word th { background:var(--fondo2); font-weight:600; }
  @media (max-width: 767px) {
    /* LETRA MÁS GRANDE PARA LEER EN EL MÓVIL (26/8/2026, pedido explícito:
       "en móvil no se ven las letras igual que en Windows"). Medido: eran
       14px en los dos sitios — el tamaño no cambiaba, es que 14px en un
       teléfono, con el móvil a un palmo de la cara y sin poder acercarlo,
       se lee peor que los mismos 14px en un monitor a medio metro.
       16px es lo que recomiendan Nielsen Norman y Baymard para texto de
       lectura en móvil, y además es el umbral por debajo del cual Safari
       de iPhone hace zoom solo. Aplica SOLO al documento Word (texto para
       leer seguido); las tablas y el resto de la interfaz no se tocan. */
    .visor-word { padding:16px 14px; font-size:calc(16px * var(--zoom-doc, 1)); line-height:1.7; }
    .visor-word h1 { font-size:1.45em; } .visor-word h2 { font-size:1.25em; }
    .visor-hoja { padding:8px; }

    /* UNA HOJA SIGUE SIENDO UNA REJILLA EN EL MÓVIL — se desactiva aquí el
       modo FICHA que 'table.tabla' aplica por defecto abajo (fallo real
       medido en un Android: once etiquetas por fila, casi todas sin valor).
       Ver el porqué largo en 'tablaDeHoja' (visor.js). Esto NO afecta a
       ninguna otra tabla: solo a la del visor de hojas. */
    table.tabla.hoja-rejilla { display:table; width:max-content; min-width:100%; }
    table.tabla.hoja-rejilla thead { display:table-header-group; }
    table.tabla.hoja-rejilla tbody { display:table-row-group; }
    table.tabla.hoja-rejilla tr { display:table-row; border:0; margin:0; padding:0; background:none; }
    table.tabla.hoja-rejilla td,
    table.tabla.hoja-rejilla th { display:table-cell; width:auto; max-width:180px; }
    /* Fuera la etiqueta de ficha: la cabecera vuelve a estar arriba. */
    table.tabla.hoja-rejilla td::before { display:none; }
  }

  /* CENTRO DE SALIDA — pantalla completa + zoom (centroSalida.js, 17/8/2026):
     mismo patrón que el visor de documentos (arrastrar con puntero, barra
     flotante con el porcentaje), reutilizable en CUALQUIER pantalla que
     monte esta utilidad, no solo Informes. */
  .cs-zoom-lienzo { transform-origin:0 0; transition:transform .08s ease-out; touch-action:none; }
  .cs-zoom-barra { display:none; position:fixed; left:50%; bottom:18px; transform:translateX(-50%); z-index:5100;
                   align-items:center; gap:6px; padding:6px 10px; border-radius:999px;
                   background:var(--papel); border:1px solid var(--linea2); box-shadow:var(--sombra); }
  .cs-zoom-valor { font-size:13px; color:var(--tinta3); min-width:42px; text-align:center; }
  :fullscreen.cs-zoom-lienzo, :fullscreen .cs-zoom-lienzo { background:var(--fondo); }

  @media (max-width:767px) {
    /* 'dvh' en vez de 'vh' (REGLA WEB): la barra del navegador móvil
       aparece/desaparece, y 'vh' dejaba la parte de abajo fuera de pantalla. */
    dialog.visor { width:98vw; max-width:98vw; height:94dvh; max-height:94dvh; }
  }

  @media (max-width:767px) {
    /* Los botones no se aprietan: la fila se desplaza dentro de su caja
       (nunca la página) y cada pestaña sigue siendo cómoda con el dedo. */
    .taller-tab { padding:11px 14px; }
  }

  .apagado { opacity:.4; }

  /* Aviso de servidor caído: rojo pleno, arriba del todo, imposible de no ver. */
  .aviso-caido { display:flex; align-items:center; gap:13px; padding:11px 22px;
                 background:var(--rojo); color:#fff; }
  .aviso-caido[hidden] { display:none; }
  .aviso-caido b { display:block; font-size:13.5px; font-weight:600; }
  .aviso-caido span { font-size:12.5px; opacity:.92; }
  .giro { width:14px; height:14px; flex:none; border:2px solid rgba(255,255,255,.35);
          border-top-color:#fff; border-radius:50%; animation:girar 1s linear infinite; }
  @keyframes girar { to { transform:rotate(360deg); } }

  /* ---------- Selectores de la barra superior (idioma y tema) ---------- */
  .selector-idioma { position:relative; }
  .selector-idioma[hidden] { display:none; }
  .bandera { display:inline-flex; width:19px; height:13px; border-radius:2px; overflow:hidden;
             flex:0 0 auto; box-shadow:inset 0 0 0 1px rgba(0,0,0,.14); }
  .bandera svg { width:100%; height:100%; display:block; }

  .selector-boton, .boton-tema {
    display:flex; align-items:center; gap:7px; font:inherit; font-size:12.5px; font-weight:500;
    color:var(--tinta2); background:var(--control-fondo); border:1px solid var(--control-borde);
    border-radius:var(--r); padding:5px 9px; cursor:pointer; line-height:1.4;
  }
  .selector-boton:hover, .boton-tema:hover { border-color:var(--tinta3); color:var(--tinta); background:var(--papel2); }
  .boton-tema { padding:5px 8px; }
  .boton-tema svg { width:15px; height:15px; }

  /* BARRA DE ACTIVIDAD: 6 px arriba del todo, SIEMPRE presente (línea tenue) y
     con un "ojo" que la recorre de un lado a otro, SIN PARAR, mientras el
     servidor trabaja — escáner tipo KITT (El Coche Fantástico): un brillo
     con degradado (no un bloque sólido) que rebota de extremo a extremo,
     nunca salta ni se reinicia de golpe (pedido explícito, 12/8/2026: "no se
     ve viva"). Es la señal grande de "espera", visible en cualquier pantalla. */
  .latido-barra { position:fixed; top:0; left:0; right:0; height:6px; z-index:1000;
                  background:var(--linea); overflow:hidden; pointer-events:none; }
  .latido-barra::before { content:""; position:absolute; inset:0 auto 0 -30%; width:30%;
                          background:linear-gradient(90deg, transparent, var(--rojo) 45%, var(--rojo) 55%, transparent);
                          filter:drop-shadow(0 0 5px var(--rojo)); transform:translateX(0); }
  .latido-barra.activo::before { animation:latido-barra 1.3s cubic-bezier(.45,0,.55,1) infinite alternate; }
  @keyframes latido-barra { 0% { transform:translateX(0); } 100% { transform:translateX(333%); } }
  @media (prefers-reduced-motion:reduce) {
    .latido-barra.activo::before { animation:none; transform:none; inset:0; width:100%; opacity:.5; filter:none; }
  }

  /* LATIDO de actividad: un corazón que descansa TENUE cuando no esperamos nada
     y late cuando hay una petición en vuelo. Es información, no un botón. */
  .latido { display:inline-flex; align-items:center; justify-content:center; }

  /* LA CAMPANA de mensajes y avisos (cabecera). El contador solo existe
     con no leídos; el panel es un desplegable anclado a la derecha. */
  .campana { position:relative; }
  .campana svg { width:20px; height:20px; }
  .campana-contador { position:absolute; top:-4px; right:-4px; min-width:16px;
                      height:16px; padding:0 4px; border-radius:999px;
                      background:var(--rojo, #c62828); color:#fff;
                      font-size:10px; font-weight:700; line-height:16px;
                      text-align:center; }
  .campana-panel { position:fixed; top:52px; right:12px; width:min(360px, calc(100vw - 24px));
                   max-height:min(480px, 70dvh); display:flex; flex-direction:column;
                   background:var(--papel); border:1px solid var(--linea);
                   border-radius:10px; box-shadow:0 8px 28px rgba(0,0,0,.18);
                   z-index:1200; overflow:hidden; }
  .campana-cabecera { display:flex; align-items:center; justify-content:space-between;
                      gap:10px; padding:10px 14px; border-bottom:1px solid var(--linea);
                      font-weight:600; }
  .campana-lista { overflow-y:auto; }
  .campana-item { padding:10px 14px; border-bottom:1px solid var(--linea);
                  font-size:13px; }
  .campana-item.noleido { background:var(--ambar-suave, #fff8e6); }
  .campana-item .de { font-weight:600; }
  .campana-item .cuando { color:var(--tinta3); font-size:11px; margin-left:6px; }
  .campana-item .texto { margin-top:2px; white-space:pre-wrap; word-break:break-word; }
  .campana-vacio { padding:22px 14px; text-align:center; color:var(--tinta3); }
  .latido svg { width:24px; height:24px; color:var(--tinta3); opacity:.35;
                transition:color .2s ease, opacity .2s ease;
                animation:latido-reposo 3.4s ease-in-out infinite; }
  /* En reposo: latido lento y muy suave, casi imperceptible. */
  @keyframes latido-reposo { 0%,100% { transform:scale(1); } 50% { transform:scale(1.08); } }
  /* Activo (hay algo en vuelo): rojo del producto, vivo y más rápido. */
  .latido.activo svg { color:var(--rojo); opacity:1; animation:latido-vivo 0.9s ease-in-out infinite; }
  @keyframes latido-vivo { 0%,100% { transform:scale(1); } 25% { transform:scale(1.22); } 50% { transform:scale(1); } }
  /* Si el usuario pide menos movimiento, se respeta. */
  @media (prefers-reduced-motion:reduce) { .latido svg { animation:none; } }
  .selector-punta { width:0; height:0; border-left:3.5px solid transparent; border-right:3.5px solid transparent;
                    border-top:4.5px solid currentColor; opacity:.55; }

  .selector-opciones { position:absolute; right:0; top:calc(100% + 5px); margin:0; padding:4px;
                       list-style:none; min-width:172px; background:var(--papel);
                       border:1px solid var(--linea2); border-radius:var(--r-g);
                       box-shadow:var(--sombra-menu); z-index:50; }
  .selector-opciones[hidden] { display:none; }
  .selector-opcion { display:flex; align-items:center; gap:9px; padding:7px 9px; border-radius:var(--r-s);
                     font-size:13px; cursor:pointer; }
  .selector-opcion:hover { background:var(--papel3); }
  .selector-opcion.activa { color:var(--rojo); font-weight:600; background:var(--rojo-tenue); }

  /* Indicador de la última llamada a la IA (SOLO PGM, pedido explícito
     19/8/2026) — mismo patrón de botón + desplegable que el selector de
     idioma (REGLA COMPONENTES: reutiliza '.selector-boton'/'.selector-opciones',
     solo añade lo propio del panel). */
  .ia-pgm { position:relative; }
  .ia-pgm[hidden] { display:none; }
  /* Más ancho y más alto (pedido explícito, 19/8/2026: "se ve muy mal, más
     grande hacia abajo") — el nombre real del modelo ("openai/gpt-5.6-luna-
     pro") es largo, así que la tabla necesita sitio de verdad. */
  /* Anclado a la DERECHA del botón (20/8/2026, pedido explícito: la
     columna izquierda se salía de la pantalla — el botón vive pegado al
     borde derecho de la barra, así que el panel tiene que crecer hacia
     la izquierda, nunca hacia fuera). */
  .ia-pgm-panel { width:640px; max-width:calc(100vw - 32px); padding:10px; right:0; left:auto; }
  .ia-pgm-panel-titulo { font-size:11.5px; font-weight:700; letter-spacing:.04em; text-transform:uppercase;
                          color:var(--tinta3); margin:2px 4px 8px;
                          display:flex; justify-content:space-between; align-items:baseline; gap:12px; }
  /* Totales de las 24 h (TL tokens · TL $), a la derecha del título. */
  .ia-pgm-totales { font-weight:600; text-transform:none; letter-spacing:0; color:var(--tinta2); }
  /* Interruptor genérico "casilla + texto" (REGLA COMPONENTES DE INTERFAZ:
     un solo patrón, reutilizado por el panel PGM y por cualquier otro sitio
     que necesite un checkbox con su etiqueta — p. ej. "aceptar descuadre"
     en Resolver). Alto de zona pulsable holgado (REGLA WEB: nada pulsable
     por debajo de 40 px en táctil). */
  .interruptor-linea { display:flex; align-items:center; gap:8px; margin:0 4px 10px;
                        min-height:40px; font-size:12.5px; color:var(--tinta2); cursor:pointer; }
  .interruptor-linea input { width:16px; height:16px; cursor:pointer; accent-color:var(--rojo); }
  /* '.campo-casilla' (checkbox "Activo/Activa" de los 5 mantenimientos de
     Organización) es EL MISMO patrón que '.interruptor-linea' de aquí
     arriba — casilla + su etiqueta — así que lleva el mismo aspecto en vez
     de un tercer estilo suelto (REGLA COMPONENTES DE INTERFAZ). Corregido
     26/8/2026: no tenía NINGUNA regla propia, así que salía con el
     checkbox azul de fábrica del navegador, descuadrado del resto del
     sistema visual (rojo en todas partes: tabla, "recordarme", ahora aquí). */
  .campo-casilla { display:flex; align-items:center; gap:8px; min-height:40px; }
  .campo-casilla input[type="checkbox"] { width:16px; height:16px; cursor:pointer; accent-color:var(--rojo); }
  .campo-casilla label { cursor:pointer; }
  .ia-pgm-panel .tabla-caja { max-height:420px; overflow-y:auto; overflow-x:hidden; }
  /* Columnas fijas: sin esto el nombre del modelo partía la fila a lo bruto
     (el fallo del pantallazo). La celda larga se trunca con "…" y el título
     (tooltip) lleva el nombre completo. */
  .ia-pgm-panel table { table-layout:fixed; width:100%; }
  .ia-pgm-panel table td, .ia-pgm-panel table th { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .ia-pgm-panel table .col-hora { width:64px; }
  .ia-pgm-panel table .col-tokens { width:84px; }
  .ia-pgm-panel table .col-pantalla { width:120px; }
  .ia-pgm-panel table .col-coste { width:100px; }
  .ia-pgm-panel table .col-consultas { width:104px; }
  .ia-pgm-vacio { padding:10px 4px; font-size:12.5px; }
  /* Pestañas "Por documento" / "Por llamada" (20/8/2026, pedido explícito:
     ver el coste de PROCESAR UN DOCUMENTO, no llamada a llamada). Mismo
     lenguaje visual que un botón pequeño, sin inventar un componente nuevo. */
  .ia-pgm-tabs { display:flex; gap:6px; margin:0 4px 8px; }
  .ia-pgm-tab { border:1px solid var(--linea2); background:var(--papel); color:var(--tinta2);
                 border-radius:var(--r-s); padding:4px 10px; font-size:12px; cursor:pointer; }
  .ia-pgm-tab.activo { border-color:var(--rojo); color:var(--rojo); font-weight:600; }

  /* ---------- Chat de pruebas con la IA (solo en el PC de programación) ---------- */
  .chat { display:flex; flex-direction:column; height:calc(100vh - 180px); max-width:1000px; }
  .chat-barra { display:flex; align-items:center; gap:11px; flex-wrap:wrap; padding:9px 13px;
                background:var(--papel); border:1px solid var(--linea); border-radius:var(--r-g) var(--r-g) 0 0;
                border-bottom:none; font-size:12.5px; }
  .chat-barra label { color:var(--tinta2); }
  .chat-barra select { background:var(--papel); color:var(--tinta); border:1px solid var(--linea2);
                       border-radius:var(--r-s); padding:4px 8px; font:inherit; font-size:12.5px; }
  .chat-check { display:inline-flex; align-items:center; gap:6px; }
  .chat-gasto { margin-left:auto; font-family:var(--mono); font-size:11.5px; color:var(--tinta3); }
  .chat-limpiar { background:var(--papel); border:1px solid var(--linea2); color:var(--tinta2);
                  border-radius:var(--r-s); padding:4px 10px; cursor:pointer; font:inherit; font-size:12px; }
  .chat-limpiar:hover { border-color:var(--rojo); color:var(--rojo); }

  .chat-hilo { flex:1 1 auto; overflow-y:auto; padding:16px; background:var(--papel2);
               border:1px solid var(--linea); display:flex; flex-direction:column; gap:12px; }
  .chat-hilo.soltando { outline:2px dashed var(--rojo); outline-offset:-8px; }
  .chat-vacio { margin:auto; text-align:center; color:var(--tinta3); font-size:13.5px; line-height:1.6; max-width:420px; }

  .msg { max-width:78%; padding:10px 13px; border-radius:var(--r-g); font-size:13.5px; line-height:1.55;
         white-space:pre-wrap; word-break:break-word; }
  .msg.yo { align-self:flex-end; background:var(--rojo); color:#fff; border-bottom-right-radius:2px; }
  .msg.ia { align-self:flex-start; background:var(--papel); border:1px solid var(--linea);
            border-bottom-left-radius:2px; }
  .msg.error { align-self:flex-start; background:var(--rojo-tenue); border:1px solid var(--rojo); color:var(--rojo); }
  .msg pre { margin:8px 0 0; padding:10px; background:var(--papel3); border:1px solid var(--linea);
             border-radius:var(--r-s); overflow-x:auto; font-family:var(--mono); font-size:11.5px; white-space:pre; }
  .msg-pie { margin-top:6px; font-family:var(--mono); font-size:11px; opacity:.65; }
  .msg-adj { display:inline-block; margin:0 6px 4px 0; padding:2px 8px; border-radius:20px;
             background:rgba(255,255,255,.2); font-size:11.5px; }
  .pensando { align-self:flex-start; color:var(--tinta3); font-size:12.5px; font-style:italic; }

  .chat-adjuntos { display:flex; flex-wrap:wrap; gap:6px; padding:0 13px; background:var(--papel);
                   border-left:1px solid var(--linea); border-right:1px solid var(--linea); }
  .chat-adjuntos:not(:empty) { padding:9px 13px; }
  .adj { display:inline-flex; align-items:center; gap:7px; padding:3px 9px; border-radius:20px;
         background:var(--papel2); border:1px solid var(--linea); font-size:11.5px; }
  .adj button { background:none; border:none; color:var(--tinta3); cursor:pointer; font-size:13px; padding:0; }
  .adj button:hover { color:var(--rojo); }

  .chat-pie { display:flex; align-items:flex-end; gap:8px; padding:11px 13px; background:var(--papel);
              border:1px solid var(--linea); border-radius:0 0 var(--r-g) var(--r-g); }
  .chat-clip { background:var(--papel); border:1px solid var(--linea2); border-radius:var(--r);
               cursor:pointer; padding:7px 10px; font-size:14px; }
  .chat-pie textarea { flex:1 1 auto; resize:none; max-height:160px; background:var(--papel);
                       color:var(--tinta); border:1px solid var(--linea2); border-radius:var(--r);
                       padding:9px 11px; font:inherit; font-size:13.5px; }
  .chat-pie textarea:focus { outline:none; border-color:var(--rojo);
                             box-shadow:0 0 0 3px rgba(212,35,42,.16); }
  .chat-enviar { background:var(--rojo); color:#fff; border:1px solid var(--rojo); border-radius:var(--r);
                 padding:9px 18px; font:inherit; font-weight:600; font-size:13.5px; cursor:pointer; }
  .chat-enviar:hover { background:var(--rojo-oscuro); border-color:var(--rojo-oscuro); }

  .agoraprb { display:flex; flex-direction:column; gap:10px; height:100%; }
  .agoraprb-barra { display:flex; gap:8px; flex-wrap:wrap; }
  .agoraprb-barra select { background:var(--papel); color:var(--tinta); border:1px solid var(--linea2);
                           border-radius:var(--r-s); padding:9px 8px; font:inherit; font-size:13px; }
  .agoraprb-barra #agoraCatalogo { flex:0 1 260px; }
  .agoraprb-desc { font-size:12.5px; color:var(--tinta2); background:var(--papel2);
                   border:1px solid var(--linea); border-radius:var(--r-s); padding:8px 11px; }
  .agoraprb-campos { display:flex; flex-direction:column; gap:6px; }
  .agoraprb-campos-titulo { font-size:11.5px; font-weight:600; color:var(--tinta3); text-transform:uppercase;
                            letter-spacing:.03em; }
  .agoraprb-campos-fila { display:flex; flex-wrap:wrap; gap:8px; }
  .agoraprb-campo { display:flex; flex-direction:column; gap:3px; font-size:11px; color:var(--tinta2);
                    min-width:140px; flex:1 1 140px; }
  .agoraprb-campo input { background:var(--papel); color:var(--tinta); border:1px solid var(--linea2);
                          border-radius:var(--r-s); padding:6px 8px; font:inherit; font-family:var(--mono);
                          font-size:12px; }
  .agoraprb-campo input:focus { outline:none; border-color:var(--rojo);
                                box-shadow:0 0 0 3px rgba(212,35,42,.16); }
  .agoraprb-barra input { flex:1 1 auto; background:var(--papel); color:var(--tinta);
                          border:1px solid var(--linea2); border-radius:var(--r-s); padding:9px 11px;
                          font:inherit; font-family:var(--mono); font-size:13px; }
  .agoraprb-barra input:focus { outline:none; border-color:var(--rojo);
                                box-shadow:0 0 0 3px rgba(212,35,42,.16); }
  .agoraprb-cuerpo { background:var(--papel); color:var(--tinta); border:1px solid var(--linea2);
                     border-radius:var(--r-s); padding:9px 11px; font:inherit; font-family:var(--mono);
                     font-size:12.5px; resize:vertical; }
  .agoraprb-historico { display:flex; flex-wrap:wrap; gap:6px; }
  .agoraprb-chip { background:var(--papel2); border:1px solid var(--linea); color:var(--tinta2);
                   border-radius:20px; padding:4px 10px; font-family:var(--mono); font-size:11.5px;
                   cursor:pointer; }
  .agoraprb-chip:hover { border-color:var(--rojo); color:var(--rojo); }
  .agoraprb-resultado { flex:1 1 auto; overflow-y:auto; background:var(--papel2);
                        border:1px solid var(--linea); border-radius:var(--r-g); padding:14px; }
  .agoraprb-cab { font-family:var(--mono); font-size:12px; margin-bottom:8px; color:var(--tinta2); }
  .agoraprb-cab.agoraprb-ok { color:#1a7d3a; }
  .agoraprb-cab.agoraprb-fallo { color:var(--rojo); }
  .agoraprb-json { margin:0; padding:11px; background:var(--papel3); border:1px solid var(--linea);
                   border-radius:var(--r-s); overflow-x:auto; font-family:var(--mono); font-size:11.5px;
                   white-space:pre; }
  .agoraprb-error { color:var(--rojo); font-size:13px; }

  .agoraprb-vistas { display:flex; gap:4px; margin-bottom:8px; }
  .agoraprb-vista-boton { background:var(--papel); border:1px solid var(--linea2); color:var(--tinta2);
                          border-radius:var(--r-s); padding:5px 12px; cursor:pointer; font:inherit; font-size:12px; }
  .agoraprb-vista-boton:hover { border-color:var(--rojo); color:var(--rojo); }
  .agoraprb-vista-boton.activa { background:var(--rojo); border-color:var(--rojo); color:#fff; }

  .agoraprb-tabla-elegir { display:block; margin-bottom:8px; background:var(--papel); color:var(--tinta);
                           border:1px solid var(--linea2); border-radius:var(--r-s); padding:6px 8px;
                           font:inherit; font-size:12.5px; max-width:100%; }
  .agoraprb-tabla td { font-family:var(--mono); font-size:12px; white-space:nowrap; }

  .agoraprb-arbol { font-family:var(--mono); font-size:12.5px; line-height:1.7; }
  .arbol-nodo { margin-left:14px; }
  .arbol-cabecera { cursor:pointer; user-select:none; }
  .arbol-cabecera:hover { color:var(--rojo); }
  .arbol-cabecera::before { content:"▾ "; color:var(--tinta3); }
  .arbol-nodo.cerrado > .arbol-cabecera::before { content:"▸ "; }
  .arbol-nodo.cerrado > .arbol-hijos { display:none; }
  .arbol-hoja { margin-left:14px; }
  .arbol-clave { color:var(--rojo-oscuro); font-weight:600; }
  .arbol-valor { color:var(--tinta2); }
  .arbol-resumen { color:var(--tinta3); }

  .agorabd { display:flex; flex-direction:column; gap:10px; height:100%; }
  .agorabd-barra { display:flex; gap:8px; align-items:flex-start; }
  .agorabd-sql { flex:1 1 auto; background:var(--papel); color:var(--tinta); border:1px solid var(--linea2);
                border-radius:var(--r-s); padding:9px 11px; font:inherit; font-family:var(--mono);
                font-size:12.5px; resize:vertical; }
  .agorabd-sql:focus { outline:none; border-color:var(--rojo); box-shadow:0 0 0 3px rgba(212,35,42,.16); }
  .agorabd-historico { display:flex; flex-wrap:wrap; gap:6px; }
  .agorabd-resultado { flex:1 1 auto; overflow-y:auto; background:var(--papel2);
                       border:1px solid var(--linea); border-radius:var(--r-g); padding:14px; }
  .agorabd-error { color:var(--rojo); font-size:13px; }
  .agorabd-tabla { background:var(--papel); }
  .agorabd-tabla td { font-family:var(--mono); font-size:12px; white-space:nowrap; }
  .agorabd-cab { display:flex; align-items:center; justify-content:space-between; gap:10px; margin-bottom:8px; }
  .agorabd-cab .agoraprb-cab { margin:0; }
  .agorabd-copiar { flex:0 0 auto; background:var(--papel); border:1px solid var(--linea2); color:var(--tinta2);
                    border-radius:var(--r-s); padding:5px 12px; cursor:pointer; font:inherit; font-size:12px; }
  .agorabd-copiar:hover { border-color:var(--rojo); color:var(--rojo); }
  .agorabd-copiar-error { border-color:var(--rojo); color:var(--rojo); background:var(--rojo-tenue); }
  .agorabd-seleccionable tbody tr.agorabd-fila { cursor:pointer; user-select:none; }
  .chat-enviar:disabled { opacity:.45; cursor:default; }

  /* ===================================================================
     PANTALLA DE GESTIÓN: barra de herramientas + tabla + edición

     La ORGANIZACIÓN es la de Ágora, que el usuario ya conoce: acciones
     arriba, casillas de selección, contador de registros al pie, y la
     edición con su pie de botones.

     LO NUESTRO, para que no sea un calco:
       1. Las acciones de fila NO aparecen al pasar el ratón: están siempre.
          En Ágora se ocultan, y en una tablet no hay forma de llegar a ellas.
       2. La columna por la que se ordena se subraya en rojo. Se ve de un
          vistazo por dónde va la lista.
       3. Los títulos de sección llevan una marca roja corta delante.
       4. Filas más altas (44 px) y esquinas algo más suaves (6 px frente a
          sus 3): más aire, se lee mejor y se toca mejor.
       5. Sin cebra: solo líneas. Menos ruido.
     =================================================================== */
  /* SIN tope de ancho — REGLA ÁREA DE TRABAJO COMPLETA: todo panel ocupa el
     máximo disponible, en cualquier tamaño de pantalla. '.panel-menu' queda
     como alias sin efecto (compatibilidad con html ya escrito). */
  /* Sin marco (borde, radio, sombra): con el contenido a hueso, el panel ES
     la pantalla — un marco alrededor solo devolvería el hueco que se acaba
     de quitar. */
  .panel { background:var(--papel); overflow:hidden; }

  /* Panel de INTEGRACIÓN: sección destacada dentro de un formulario para los
     parámetros de una base externa (hoy Ágora; mañana un ExitERP tendrá la
     suya con el mismo patrón — REGLA COMPONENTES DE INTERFAZ). Se distingue
     del resto del formulario con un acento lateral y cabecera teñida, porque
     va a ir creciendo con más opciones y tiene que leerse como un bloque
     propio, no como más campos sueltos. */
  .panel-integracion { margin-top:4px; border:1px solid var(--linea2); border-left:3px solid var(--rojo);
                        border-radius:var(--r-g); background:var(--papel); overflow:hidden; }
  .panel-integracion-cab { display:flex; align-items:center; gap:9px; padding:11px 16px;
                            background:var(--rojo-tenue); border-bottom:1px solid var(--linea2); }
  .panel-integracion-cab svg { width:18px; height:18px; color:var(--rojo); flex:0 0 auto; }
  .panel-integracion-titulo { font-size:13.5px; font-weight:600; }
  .panel-integracion-cuerpo { padding:16px; display:grid; gap:14px; }

  .panel-cabecera { display:flex; align-items:center; gap:10px; flex-wrap:wrap;
                    padding:12px 14px; border-bottom:1px solid var(--linea); background:var(--papel2); }
  /* Marca roja corta antes del título: la seña de la casa. */
  .panel-titulo { display:flex; align-items:center; gap:9px; font-size:14px; font-weight:600; }
  .panel-titulo::before { content:""; width:3px; height:15px; border-radius:2px; background:var(--rojo); }
  .panel-acciones { margin-left:auto; display:flex; align-items:center; gap:6px; }

  /* Desplegable "Mostrar: los últimos N días" (Escanear/Listos/Enviados,
     REGLA COMPONENTES: una sola pieza reutilizada) — compacto, a la
     izquierda de la cabecera del panel. */
  .filtro-dias { display:flex; align-items:center; gap:8px; }
  .filtro-dias label { font-size:12.5px; font-weight:600; color:var(--tinta3);
                        text-transform:uppercase; letter-spacing:.03em; }
  .filtro-dias select { min-height:34px; padding:5px 30px 5px 11px; font:inherit; font-size:13px;
                         font-weight:600; border:1px solid var(--linea2); border-radius:var(--r);
                         background:var(--papel) url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="%23888" stroke-width="2"><path d="m6 9 6 6 6-6"/></svg>') no-repeat right 8px center/16px;
                         color:var(--tinta); cursor:pointer; appearance:none; -webkit-appearance:none; }
  .filtro-dias select:focus { outline:none; border-color:var(--rojo); box-shadow:0 0 0 3px rgba(212,35,42,.14); }

  /* --- BOTÓN NEUTRO: QUE SE VEA QUE ES UN BOTÓN (22/8/2026) -----------
     Antes el botón era BLANCO sobre fondo BLANCO, y su único indicio era
     un borde muy tenue. Medido en el navegador:
       · relleno del botón contra el fondo ....... 1,00 : 1  (el MISMO color)
       · borde contra el fondo ................... 1,45 : 1
     La norma de accesibilidad (WCAG 2.1, 1.4.11 "contraste de lo que no
     es texto") pide 3:1 para el contorno de un control. O sea que no era
     una impresión: la zona pulsable no se distinguía, y en los botones
     que solo llevan icono —los del visor, los de la cabecera— el contorno
     es LO ÚNICO que dice que ahí hay algo que tocar.

     Se arregla con dos cambios, los dos en gris NEUTRO del propio sistema
     (nada de color nuevo, y funciona igual en tema claro y oscuro):
       · relleno '--papel3' en vez de blanco: la FORMA del botón ya se ve
         sola, sin necesidad de un contorno grueso;
       · borde '--tinta3', que da 3,15:1 y cumple la norma.
     El botón principal (rojo) no se toca: ese ya destacaba de sobra. */
  .boton { display:inline-flex; align-items:center; justify-content:center; gap:7px;
           min-height:36px; padding:7px 13px; font:inherit; font-size:13px; font-weight:600;
           border:1px solid var(--control-borde); border-radius:var(--r); background:var(--control-fondo);
           color:var(--tinta2); cursor:pointer; white-space:nowrap; }
  .boton:hover { border-color:var(--tinta2); color:var(--tinta); background:var(--linea); }
  .boton svg { width:15px; height:15px; }
  .boton-principal { background:var(--rojo); border-color:var(--rojo); color:#fff; }
  .boton-principal:hover { background:var(--rojo-oscuro); border-color:var(--rojo-oscuro); color:#fff; }
  .boton-icono { min-width:36px; padding:7px 9px; }
  /* ENLACE (26/8/2026): botón que se lee como un hipervínculo, para cuando
     un control queda BLOQUEADO y hace falta ofrecer la SALIDA en vez de un
     campo mudo — p. ej. "esta empresa no tiene conceptos, créalo aquí"
     (Revisar de Documental). Va al SISTEMA, no a esa pantalla, porque con
     ~200 pantallas por delante este patrón —"aquí no hay nada que elegir,
     ve a crearlo"— se va a repetir (REGLA COMPONENTES DE INTERFAZ). Reset
     de botón nativo + apariencia de texto con color de acento; min-height
     36px para el dedo (REGLA WEB), aunque no tenga caja visible. */
  .enlace { display:inline-flex; align-items:center; min-height:36px; padding:0;
            border:none; background:none; font:inherit; font-size:12.5px; font-weight:600;
            color:var(--rojo); cursor:pointer; text-align:left; }
  .enlace:hover { text-decoration:underline; }
  /* Botones de FILA en modo FANTASMA (23/8/2026, pedido explícito: "ocupan
     mucho espacio... menos espacio, más estético"). En una tabla el mismo
     botón se repite N veces por pantalla: con caja propia cada uno, la
     columna de acciones pesa visualmente más que los datos. Patrón de los
     sistemas de referencia para acciones de línea (SAP Fiori los pinta
     "transparent", IBM Carbon los llama "ghost"): el icono manda, sin
     borde propio. En táctil conservan el mínimo de 40px (REGLA WEB) — el
     fantasma es visual, no de tamaño.
     Con un FONDO NEUTRO tenue en reposo (matiz pedido el 23/8: "resaltar un
     poco más con algún color de fondo neutro"): fantasma puro no es lo
     mismo que invisible — el relleno suave dice "esto se pulsa" sin
     dibujar N cajas con borde. Va sobre la cebra: '--papel3' se distingue
     igual en la fila blanca y en la rayada, y en oscuro sube (no baja),
     así que se lee como una pastilla en los dos temas. */
  .acciones-fila .boton-icono { min-width:28px; min-height:28px; padding:4px 5px;
                                border-color:transparent; background:var(--papel3); }
  .acciones-fila .boton-icono:hover { border-color:var(--control-borde); background:var(--linea2); color:var(--tinta); }
  .acciones-fila .boton-icono svg { width:16px; height:16px; }
  /* Las variantes con color propio (PGM ámbar, Ágora roja) conservan su
     seña también en fantasma. */
  .acciones-fila .boton-icono-pgm { border-color:var(--ambar); background:var(--ambar-tenue); }
  /* UN BOTÓN QUE DICE SI YA SE HIZO (25/8/2026, pedido explícito: el de
     descargar en rojo claro mientras no se ha descargado, y en verde claro
     cuando sí). Va al SISTEMA y no a la pantalla porque es un patrón que
     vale para cualquier acción de fila con memoria —descargar, imprimir,
     enviar—: de un vistazo se ve qué queda por hacer en la lista, sin
     abrir nada.

     Intensidad SUBIDA el 25/8/2026 ("muy claro... más vivo"): el tinte de
     antes era el de una pastilla informativa y en una fila con cebra casi
     no se veía. Ahora el fondo lleva bastante color y el borde va a plena
     intensidad, así que la fila canta de un vistazo.
     Lo que NO se hace es rellenarlo macizo: ese acabado lo tiene
     '.boton-principal' para DESTRUIR, y si empatan el ojo deja de
     distinguir "esto borra" de "esto está pendiente". */
  .acciones-fila .boton-icono.estado-pendiente {
    color:var(--rojo); border-color:var(--rojo);
    background:color-mix(in srgb, var(--rojo) 26%, var(--rojo-tenue));
  }
  .acciones-fila .boton-icono.estado-pendiente:hover {
    color:#fff; background:var(--rojo); border-color:var(--rojo);
  }
  .acciones-fila .boton-icono.estado-hecho {
    color:var(--verde); border-color:var(--verde);
    background:color-mix(in srgb, var(--verde) 26%, var(--verde-tenue));
  }
  .acciones-fila .boton-icono.estado-hecho:hover {
    color:#fff; background:var(--verde); border-color:var(--verde);
  }
  /* DESTRUIR SE VE (25/8/2026). El fantasma de arriba apaga cualquier
     color de fondo, así que un '.boton-principal' dentro de una fila de
     acciones perdía su rojo y quedaba igual que "ver" o "restaurar" — y
     eso es justo lo que no puede pasar con el ÚNICO botón del producto
     que borra de verdad (hoy, Eliminar de la Papelera). Se declara aquí,
     en el sistema, para que cualquier acción irreversible futura lo
     herede sin inventarse un estilo suelto. */
  .acciones-fila .boton-icono.boton-principal {
    background:var(--rojo); border-color:var(--rojo); color:#fff;
  }
  .acciones-fila .boton-icono.boton-principal:hover {
    background:var(--rojo-oscuro); border-color:var(--rojo-oscuro); color:#fff;
  }
  @media (pointer:coarse) {
    .acciones-fila .boton-icono { min-width:40px; min-height:40px; }
    /* Con el dedo, cada ficha tiene que ser acertable: 40 px (REGLA WEB,
       punto 3). En ratón se quedan compactas, que es donde interesa la
       densidad. */
    .ficha-sel { min-height:40px; padding:8px 14px; }
  }
  .boton-icono-g { min-width:42px; padding:9px 11px; }
  .boton-icono-g svg { width:20px; height:20px; }
  /* Utilidad SOLO PGM (13/8/2026): mismo icono que otras utilidades de
     desarrollo (inspector de campos) — ámbar, para que se note a la legua
     que no es un botón del producto para el equipo del cliente. */
  .boton-icono-pgm { color:var(--ambar); background:var(--ambar-tenue); border-color:var(--ambar); border-style:dashed; }
  .boton-icono-pgm:hover { background:var(--ambar); color:#fff; }
  /* Botón "Abrir en Ágora" (Enviados): logo "á" en el rojo del producto,
     mismo sistema .boton-icono (REGLA COMPONENTES). */
  .boton-icono.boton-agora { color:var(--rojo); }
  .boton-icono.boton-agora:hover { background:var(--rojo); color:#fff; }
  /* "Reprocesar con IA", junto a Características: mismo tamaño que el de
     Características, pero en rojo para que se note que es la acción que
     vuelve a leer el documento con la IA (pedido explícito, 12/8/2026). */
  #scanResolverReprocesarIa { color:var(--rojo); border-color:var(--rojo-tenue); }
  #scanResolverReprocesarIa:hover { background:var(--rojo-tenue); border-color:var(--rojo); color:var(--rojo-oscuro); }
  /* Contador de características activas, sobre el propio icono — se ve de
     un vistazo sin tener que abrir el panel. */
  .caract-boton-contador { position:relative; }
  .caract-contador { position:absolute; top:-6px; right:-6px; min-width:16px; height:16px; padding:0 3px;
                      border-radius:999px; background:var(--rojo); color:#fff; font-size:10px; font-weight:700;
                      line-height:16px; text-align:center; }
  .boton:disabled { opacity:.45; cursor:default; }

  .buscador { position:relative; display:flex; align-items:center; }
  .buscador svg { position:absolute; left:9px; width:15px; height:15px; color:var(--tinta3); pointer-events:none; }
  .buscador input { min-height:36px; padding:7px 11px 7px 30px; font:inherit; font-size:13px;
                    border:1px solid var(--linea2); border-radius:var(--r);
                    background:var(--papel); color:var(--tinta); width:190px; }
  .buscador input:focus { outline:none; border-color:var(--rojo); box-shadow:0 0 0 3px rgba(212,35,42,.14); }

  /* ===================================================================
     ESPERA — REGLA: cualquier hueco de la web mientras el aplicativo
     espera una respuesta del servidor se destaca así (giro + texto),
     NUNCA un simple texto plano de "Cargando...". Es información activa:
     le dice al usuario que está funcionando, no colgado. Reutilizable en
     cualquier pantalla o diálogo, no solo en listas.
     =================================================================== */
  .espera { display:flex; flex-direction:column; align-items:center; justify-content:center;
            gap:11px; padding:38px 20px; color:var(--tinta3); font-size:13px; }
  .espera-giro { width:26px; height:26px; flex:none; border:3px solid var(--linea2);
                 border-top-color:var(--rojo); border-radius:50%;
                 animation:espera-girar .8s linear infinite; }
  @keyframes espera-girar { to { transform:rotate(360deg); } }
  @media (prefers-reduced-motion:reduce) { .espera-giro { animation:none; } }

  /* Lista compacta y buscable para elegir UNO entre muchos (cientos o miles):
     rejilla multi-columna en vez de tabla — una tabla de una sola columna
     "Nombre" desperdicia todo el ancho. Se desplaza dentro de su propia caja
     con tope de alto, la página nunca crece con la lista. */
  .lista-elegir { display:grid; grid-template-columns:repeat(auto-fill,minmax(210px,1fr));
                  gap:6px; max-height:56vh; overflow-y:auto; padding-right:4px; }
  .lista-elegir-item { display:flex; align-items:center; justify-content:space-between; gap:8px;
                       padding:9px 12px; border:1px solid var(--linea); border-radius:var(--r);
                       background:var(--papel); color:var(--tinta); cursor:pointer;
                       font:inherit; font-size:13px; text-align:left;
                       transition:border-color .12s ease, background .12s ease, color .12s ease; }
  .lista-elegir-item:hover, .lista-elegir-item:focus-visible {
    border-color:var(--rojo); background:var(--rojo-tenue); color:var(--rojo); }
  .lista-elegir-item svg { width:15px; height:15px; flex:0 0 auto; opacity:.55; }

  /* Marca la opción ACTUAL (la que ya tiene la línea) de forma SIEMPRE
     visible — no basta con enfocarla (':focus-visible' de Chrome no
     siempre pinta el anillo si el popover se abrió con el ratón; 21/8/2026,
     pedido explícito: "marca por defecto la base si la tienes"). */
  .lista-elegir-item.activo { border-color:var(--rojo); background:var(--rojo-tenue); color:var(--rojo); }
  .lista-elegir-item.activo::after {
    content:'✓'; font-weight:700; flex:0 0 auto; }

  /* Diálogo ancho: cuando el contenido necesita más sitio que el formulario
     estándar de 520px (p. ej. la rejilla de arriba). Mismo lenguaje, más aire. */
  dialog.dialogo.ancho { max-width:680px; }

  /* Tabla de permisos con casilla (checklist denso, cientos de filas): mismo
     table.tabla de siempre, con una columna de categoría (Tipo) para poder
     escanear el bloque de un vistazo, como en el editor de perfiles de
     Ágora — pero con nuestro estilo (sin cebra, acento rojo). */
  table.tabla input[type="checkbox"] { width:16px; height:16px; accent-color:var(--rojo); cursor:pointer; }
  table.tabla tbody tr.permiso-fila { cursor:pointer; }

  /* Cabecera de una pantalla de edición a pantalla completa dentro de un
     panel: volver + título a la izquierda, acciones a la derecha. */
  .panel-cabecera .subpantalla-cab { flex:1 1 auto; }
  .campos-linea { display:grid; grid-template-columns:1fr 2fr; gap:14px; padding:16px; }
  @media (max-width:900px) { .campos-linea { grid-template-columns:1fr; } }

  /* DOS CAMPOS EN LA MISMA LÍNEA, a partes iguales (23/8/2026). Se apila en
     móvil, donde partir el ancho dejaría los valores cortados — es la misma
     lección que ya nos costó una medición: en pantalla estrecha, la etiqueta
     encima y el campo entero. Vive en el sistema, no en una pantalla suelta
     (REGLA COMPONENTES DE INTERFAZ), para que cualquier formulario lo use. */
  .campos-par { display:grid; grid-template-columns:1fr 1fr; gap:14px; }
  @media (max-width:900px) { .campos-par { grid-template-columns:1fr; } }

  /* La tabla se desplaza DENTRO de su caja; la página nunca se mueve de lado. */
  .tabla-caja { overflow-x:auto; -webkit-overflow-scrolling:touch; }
  /* Listas de documentos de compra (pedido explícito, 14/8/2026): la caja
     tiene también su barra VERTICAL propia — la tabla se desplaza por
     dentro, con la cabecera de columnas siempre a la vista (sticky abajo),
     y la barra horizontal queda en la misma caja, no al fondo de la página. */
  #scanListaPanel .tabla-caja { max-height:calc(100dvh - 240px); min-height:160px; overflow:auto; }
  /* Cabecera de columnas fija arriba DENTRO de la caja con scroll. Solo
     actúa donde hay caja desplazable — en una tabla suelta no cambia nada. */
  .tabla-caja table.tabla thead th { position:sticky; top:0; z-index:2; }
  /* Las esquinas (check a la izquierda, acciones a la derecha) son sticky
     en LOS DOS ejes a la vez y van por encima del resto. */
  .tabla-caja table.tabla thead th.col-check,
  .tabla-caja table.tabla thead th.col-id,
  .tabla-caja table.tabla thead th.col-acciones { z-index:3; }
  /* 'tabular-nums': con Inter, todas las cifras miden lo mismo y las
     columnas de importes quedan alineadas dígito a dígito. */
  /* 'separate' (no 'collapse') A PROPÓSITO: 'position:sticky' sobre una
     celda NO funciona con border-collapse:collapse (limitación real de
     Chrome, vista en vivo 14/8/2026 — la columna de acciones fija no se
     pegaba). Mismo aspecto: los bordes son líneas inferiores por celda,
     con spacing 0 no se nota la diferencia. */
  /* 'table.tabla' (Scan/Buzón/Empresa) es EL MISMO patrón visual que
     'table.tabla' (Administración) — antes no tenía ni una sola regla
     propia y se veía como HTML sin estilo (fallo real detectado probando
     en vivo, 17/8/2026: "todo muy blanco, no se destaca nada"). Un solo
     bloque de reglas para las dos clases (REGLA COMPONENTES DE INTERFAZ). */
  table.tabla { width:100%; border-collapse:separate; border-spacing:0; font-size:13.5px; font-variant-numeric:tabular-nums; }
  table.tabla th { position:relative; text-align:left; font-weight:600; font-size:11.5px; letter-spacing:.04em;
                   text-transform:uppercase; color:var(--tinta3); padding:9px 12px;
                   border-bottom:1px solid var(--linea2); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
                   background:var(--papel); }
  table.tabla th.ordenable { cursor:pointer; user-select:none; }
  table.tabla th.ordenable:hover { color:var(--tinta); }
  /* Asa de redimensionar columna (pedido explícito, 14/8/2026): franja
     angosta pegada al borde derecho de la cabecera, cursor de arrastre
     horizontal.
     FALLO REAL corregido (21/8/2026, "toco el ancho... la columna se me
     cambia y los datos se quedan con la que no está"): antes 'right:-5px'
     con 'width:10px' hacía que la mitad de la franja cayera GEOMÉTRICAMENTE
     dentro de la CABECERA VECINA (5 px de los 10 pasaban de largo el borde).
     Cada 'th' es 'position:sticky' con el MISMO z-index — entre dos
     hermanos posicionados con igual z-index gana el que va DESPUÉS en el
     DOM, así que en esos 5 px la cabecera vecina (más tarde en el DOM)
     tapaba el asa aunque se viera encima: el clic ahí no redimensionaba,
     arrastraba la columna vecina para MOVERLA — comprobado con
     'elementFromPoint' en varios puntos del borde. Subir el z-index del
     asa no lo arregla: un descendiente nunca puede ganar a un HERMANO de
     su propio contenedor por mucho z-index que lleve él solo (así
     funciona el apilamiento en CSS). La solución de verdad es que el asa
     NUNCA invada la celda vecina: se queda entera dentro de SU propia
     cabecera ('right:0'), y la rayita visible se pinta pegada a su borde
     derecho (antes centrada) para que seguir viéndose exactamente igual. */
  table.tabla th .col-resize { position:absolute; top:0; right:0; bottom:0; width:10px;
                                cursor:col-resize; touch-action:none; z-index:1; }
  table.tabla th .col-resize::before { content:""; position:absolute; top:4px; bottom:4px; right:0; width:2px;
                                        border-radius:1px; background:var(--linea2); }
  table.tabla th .col-resize:hover::before,
  table.tabla th .col-resize.col-resize-activo::before { background:var(--rojo); }
  /* Con anchos fijados a mano (table-layout:fixed) una celda NO puede
     desbordar sobre la vecina: lo que no cabe se corta con puntos
     suspensivos (visto en vivo: la pastilla de Tipo pisaba el Total). */
  table.tabla.tabla-redimensionable td { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  /* Subrayado rojo en la columna por la que se ordena (Regla 9). */
  table.tabla th.orden { color:var(--rojo); box-shadow:inset 0 -2px 0 var(--rojo); }
  table.tabla th.orden::after { content:" ▲"; font-size:8px; }
  table.tabla th.orden.desc::after { content:" ▼"; }
  /* DENSIDAD (23/8/2026, pedido explícito: "las líneas súper anchas
     desperdiciando un montón de espacio... aprende de profesionales"): 6px
     de aire vertical y 10px de horizontal — con el botón de fila fantasma
     de 28px, la fila queda en ~34px, la franja "compact" de los sistemas
     de referencia (SAP Fiori compact y IBM Carbon compact trabajan en
     32px). En táctil los botones vuelven a 40px y la fila crece sola. */
  table.tabla td { padding:6px 10px; border-bottom:1px solid var(--linea); vertical-align:middle; }
  /* La celda de acciones casi sin aire vertical: su contenido (el botón)
     ya trae el suyo — sin esto, sumaría dos veces y mandaría en el alto. */
  table.tabla td.col-acciones { padding-top:2px; padding-bottom:2px; }
  table.tabla tbody tr:hover { background:var(--papel2); }
  table.tabla tbody tr.marcada { background:var(--rojo-tenue); }
  table.tabla tbody tr:last-child td { border-bottom:none; }
  /* CEBRA (23/8/2026, pedido explícito: "todo en blanco... destacar con
     algo de fondo neutro que no destaque mucho para diferenciar las
     líneas" — mejora GENERAL, todas las tablas). Va sobre el td (no el tr)
     para que las columnas FIJAS —que llevan fondo opaco propio para tapar
     lo que pasa por debajo al desplazar— sigan la raya de su fila. El
     hover y la fila marcada van DESPUÉS y con td, para ganar a la cebra;
     el hover sube a --papel3 porque --papel2 es ahora el tono de la raya. */
  table.tabla tbody tr:nth-child(even) td { background:var(--papel2); }
  table.tabla tbody tr:hover td { background:var(--papel3); }
  table.tabla tbody tr.marcada td { background:var(--rojo-tenue); }
  /* Tabla de líneas de Resolver: distingue de un vistazo lo que viene TAL
     CUAL del documento leído (columnas normales) de lo que ya está resuelto
     contra Ágora (fondo tenue + borde a los lados, a modo de corchete —
     pedido explícito). No es "acierto/fallo" (eso ya lo dice la pastilla
     verde/gris de dentro): es solo "esta columna es del lado Ágora". */
  .tabla-linea-scan th.col-agora, .tabla-linea-scan td.col-agora { background:var(--papel3); }
  .tabla-linea-scan th:nth-of-type(2), .tabla-linea-scan td:nth-of-type(2) { border-left:2px solid var(--verde); }
  .tabla-linea-scan th:nth-of-type(3), .tabla-linea-scan td:nth-of-type(3) { border-right:2px solid var(--verde); }
  .tabla-linea-scan tbody tr:hover td.col-agora { background:color-mix(in srgb, var(--papel3) 70%, var(--papel2)); }
  /* Panel de totales de Resolver (pedido explícito): mismo lenguaje visual
     que las tarjetas de cabecera — el descuento GLOBAL de cabecera arriba,
     el desglose por tipo de impuesto debajo, igual que el "Pie"/"Totales"
     del propio panel de Ágora. */
  .resolver-totales { display:flex; flex-direction:column; gap:10px; padding:14px 20px; }
  /* 'nowrap' (21/8/2026, pedido explícito "en la misma línea"): con las
     dos etiquetas acortadas y el ancho del campo ya fijo (ver
     '.campo-linea.campo-cabecera' más abajo) los dos caben de sobra sin
     partirse en dos filas. */
  .resolver-totales-dto .cab-linea { justify-content:flex-end; flex-wrap:nowrap; gap:5px 14px; }
  /* 'width:100%; table-layout:fixed' (20/8/2026, junto con quitar la barra
     horizontal de '.resolver-doc'): sin esto la tabla medía su ancho por
     el CONTENIDO y, en una columna estrecha, se salía por el lado — antes
     se veía gracias a la barra horizontal que también sobraba quitar; con
     la barra fuera, ese trozo se habría quedado invisible sin más. Con
     ancho fijo al 100 % del hueco, las columnas se reparten el espacio
     real y nunca se cortan. */
  .tabla-totales-scan { max-width:520px; width:100%; table-layout:fixed; margin-left:auto; }
  .tabla-totales-scan .tabla-totales-fila { font-weight:700; }
  .tabla-totales-scan .tabla-totales-fila td { border-top:2px solid var(--linea2); border-bottom:none; }
  /* FALLO REAL corregido (21/8/2026, "horrible" — pedido explícito con
     captura, DOS intentos): la pastilla verde/roja del TOTAL FINAL
     (verifica contra el total del documento) es la MISMA de "Verif.
     Ágora" en las líneas — bien ahí, porque toda esa columna se ve igual
     fila tras fila. Aquí no: es la ÚNICA celda de esta fila EN NEGRITA que
     lleva una pastilla, y el bulto de color (fondo, borde redondeado,
     punto) se nota igual de forzado aunque se iguale la tipografía —
     sigue pareciendo una etiqueta pegada encima de una línea de totales
     que por lo demás es texto plano. Aquí se quita TODO el "chrome" de
     pastilla (fondo, borde, relleno, el puntito de before) y se deja
     SOLO el color del texto — el mismo verde/rojo, pero leyéndose como
     parte de la fila, no como una insignia aparte. */
  .tabla-totales-scan .tabla-totales-fila .pastilla { font-size:inherit; font-weight:inherit;
    padding:0; background:none; border:none; display:inline; }
  .tabla-totales-scan .tabla-totales-fila .pastilla::before { display:none; }
  /* Nombre de fichero corto en una celda de tabla: se corta con puntos
     suspensivos, el nombre completo sale al pasar el ratón (title). Clic
     para descargar el original — REGLA COMPONENTES: un solo sitio, para
     cualquier tabla que necesite mostrar un nombre de fichero largo. */
  .col-truncar { display:inline-block; max-width:160px; overflow:hidden; text-overflow:ellipsis;
                 white-space:nowrap; vertical-align:bottom; color:var(--tinta2); text-decoration:none; }
  .col-truncar:hover { color:var(--rojo); text-decoration:underline; }
  .col-sel { width:38px; }
  .col-acciones { width:1%; white-space:nowrap; text-align:left; }
  /* Columna de acciones SIEMPRE visible (pedido explícito, 14/8/2026): con
     columnas redimensionables la tabla puede acabar más ancha que el hueco
     visible, y sin esto los botones quedaban fuera hasta desplazar del
     todo. Pegada al borde derecho de '.tabla-caja' al desplazar — fondo
     sólido para tapar el contenido que pasa por debajo, y se actualiza con
     el resto de la fila al pasar el ratón para que no se note la costura. */
  /* CAMBIO 23/8/2026 (pedido explícito: "los botones en toda la aplicación
     en las tablas no están fijados... colocarlos todos a la izquierda
     fijados para que puedan acceder sin usar la barra"): acciones pasa del
     borde derecho al IZQUIERDO. La columna la mueve al principio
     'anclarColumnasFijas' (app.js, puerta única); el 'left' exacto de cada
     fija lo pone esa misma función, acumulando anchos, porque dos sticky
     con el mismo left se montan una encima de otra. */
  table.tabla td.col-acciones,
  table.tabla th.col-acciones {
    position:sticky; left:0; z-index:1; background:var(--papel);
    box-shadow:6px 0 6px -6px rgba(16,24,40,.18);
  }
  table.tabla tbody tr:hover td.col-acciones { background:var(--papel3); }
  table.tabla tbody tr.marcada td.col-acciones { background:var(--rojo-tenue); }
  /* Y el CHECK de selección (Listos), igual pero al borde IZQUIERDO
     (pedido explícito, 14/8/2026): al desplazar la tabla hacia la derecha
     la primera columna se perdía de vista y no se podía marcar. */
  table.tabla td.col-check,
  table.tabla th.col-check {
    position:sticky; left:0; z-index:1; background:var(--papel);
    box-shadow:6px 0 6px -6px rgba(16,24,40,.18);
  }
  table.tabla tbody tr:hover td.col-check { background:var(--papel3); }
  table.tabla tbody tr.marcada td.col-check { background:var(--rojo-tenue); }
  /* Id y Tipo de documento: columnas angostas en las tablas de documentos
     de compra (mismo patrón que '.col-acciones', REGLA COMPONENTES). */
  .col-id, .col-tipo { width:1%; white-space:nowrap; text-align:center; }
  /* Nombre de fichero: con ancho ACOTADO (23/8/2026, pedido explícito: "el
     nombre de fichero ocupa todo a lo ancho, y no es tan relevante"). Sin
     él, el layout automático regala a esta columna todo el sobrante y los
     datos de verdad (emisor, número, total) quedan apretados. Sigue siendo
     redimensionable donde la tabla tenga clave de anchos. */
  .col-fichero { width:280px; }
  /* Id NUNCA se recorta con puntos suspensivos (18/8/2026, queja repetida:
     "llevamos con esto toda la vida... en todas las tablas") — pisa la
     regla general de arriba ('.tabla-redimensionable td { overflow:hidden;
     text-overflow:ellipsis }') SOLO para esta columna. Es el dato más corto
     de la fila y el que identifica el registro sin ambigüedad: perderlo
     por un ancho mal calculado es peor que dejarlo asomar un poco sobre la
     columna vecina. Sin 'max-width' fijo, tampoco: un número puede tener
     más o menos cifras según cuántos documentos lleve el equipo, y un tope
     en píxeles vuelve a cortarlo tarde o temprano. */
  table.tabla.tabla-redimensionable td.col-id,
  table.tabla.tabla-redimensionable th.col-id {
    overflow:visible; text-overflow:clip;
  }
  /* Id SIEMPRE visible (18/8/2026, pedido explícito: "campo id siempre
     visible es fundamental") — mismo patrón sticky-izquierda que '.col-check'
     un poco más arriba: pegada al borde al desplazar en horizontal. */
  table.tabla td.col-id,
  table.tabla th.col-id {
    position:sticky; left:0; z-index:1; background:var(--papel);
    box-shadow:6px 0 6px -6px rgba(16,24,40,.18);
  }
  table.tabla tbody tr:hover td.col-id { background:var(--papel3); }
  table.tabla tbody tr.marcada td.col-id { background:var(--rojo-tenue); }
  /* Columna con VARIAS LÍNEAS vivas dentro (24/8/2026): varias casillas o
     etiquetas por celda que necesitan envolver, no una sola línea de texto.
     Mismo patrón que '.col-id' arriba — pisa la regla general de
     '.tabla-redimensionable' SOLO para esta columna, así una tabla puede
     tener el botón "Columnas" y anchos de las DEMÁS columnas sin forzar
     'nowrap' en esta. */
  table.tabla.tabla-redimensionable td.col-multilinea,
  table.tabla.tabla-redimensionable th.col-multilinea {
    overflow:visible; text-overflow:clip; white-space:normal;
  }
  /* Mover columnas (18/8/2026, pedido explícito): cursor de agarre en el
     cuerpo de la cabecera (nunca sobre el asa de ancho, que ya tiene el
     suyo). Mientras se arrastra, la cabecera se atenúa — señal clara de
     "esto se está moviendo", sin mover visualmente el resto hasta soltar. */
  /* 'user-select:none' (18/8/2026, bug real: con ratón de verdad, un
     mousedown+arrastre sobre texto dispara SIEMPRE la selección nativa del
     navegador antes que nada — con el texto puesto en azul, el arrastre
     de columna se sentía roto aunque el código funcionara perfectamente,
     comprobado disparando los eventos de puntero a mano). Sin esto, no
     hay arrastre de columna que se note fiable con ratón real. */
  table.tabla th.col-movible { cursor:grab; user-select:none; }
  table.tabla th.col-arrastrando { opacity:.4; cursor:grabbing; }
  /* Mover una columna, al modo de las rejillas profesionales (23/8/2026,
     "implanta profesionalmente"): mientras se arrastra, un FANTASMA con el
     nombre de la columna sigue al puntero y una RAYA vertical marca el
     borde donde va a caer. Nada se reordena hasta soltar. Los dos van con
     'position:fixed' en coordenadas de ventana y 'pointer-events:none'
     para no robarle ni un evento al arrastre. */
  .col-fantasma { position:fixed; z-index:9999; pointer-events:none;
                  transform:translate(10px, 10px);
                  max-width:220px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
                  padding:5px 10px; border-radius:var(--r);
                  background:var(--papel); border:1px solid var(--linea2);
                  box-shadow:0 6px 18px rgba(16,24,40,.18);
                  font-size:11.5px; font-weight:600; letter-spacing:.04em;
                  text-transform:uppercase; color:var(--tinta2); }
  .col-marca-soltar { position:fixed; z-index:9998; pointer-events:none;
                      width:2px; margin-left:-1px; background:var(--rojo);
                      box-shadow:0 0 0 1px var(--rojo-tenue); }
  body.moviendo-columna { user-select:none; cursor:grabbing; }
  /* Botón "Columnas", DENTRO de la primera cabecera (23/8/2026: "no me
     ocupes espacio solo para el botón"). Ni un píxel de alto extra: se
     queda en el hueco que la cabecera ya ocupa, y como esa primera columna
     suele ser fija, no se va de la vista al desplazar en horizontal.
     Discreto en reposo, se marca al pasar por encima. */
  table.tabla th .tabla-columnas-btn {
    min-width:24px; min-height:24px; padding:0 2px; margin-right:6px;
    vertical-align:middle; border-color:transparent; background:transparent;
    color:var(--tinta3); border-radius:4px; }
  table.tabla th .tabla-columnas-btn svg { width:14px; height:14px; }
  table.tabla th .tabla-columnas-btn:hover { background:var(--linea2); color:var(--tinta); border-color:transparent; }
  /* Diálogo de columnas: una casilla por columna, en lista compacta. */
  .tabla-columnas-ayuda { margin:0 0 10px; font-size:12.5px; }
  .columnas-lista { display:grid; gap:2px; max-height:50vh; overflow:auto; }
  .columnas-item { display:flex; align-items:center; gap:9px; padding:6px 8px;
                   border-radius:var(--r); cursor:pointer; font-size:13.5px; }
  .columnas-item:hover { background:var(--papel2); }
  /* Columna oculta. El '!important' NO es pereza: en móvil la tabla pasa a
     ficha y ahí 'table.tabla td' declara 'display:flex', que ganaría al
     atributo 'hidden' y la columna escondida reaparecería. */
  table.tabla th[hidden], table.tabla td[hidden] { display:none !important; }
  /* Documento recortado (pedido explícito 17/8/2026): en la Bandeja de
     Entrada el nombre del fichero cede espacio a los campos clave del tipo
     (CIF, Proveedor) — se ve completo en el título (tooltip). */
  #bandejaTabla .col-documento { max-width:180px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .acciones-fila { display:inline-flex; gap:4px; }
  .num { text-align:right; font-variant-numeric:tabular-nums; white-space:nowrap; }

  .panel-pie { display:flex; align-items:center; gap:12px; padding:10px 14px;
               border-top:1px solid var(--linea); background:var(--papel2);
               font-size:12.5px; color:var(--tinta3); }

  .sin-datos { padding:44px 20px; text-align:center; color:var(--tinta3); font-size:13.5px; }
  /* TABLA VACÍA: se marca con la clase 'tabla-vacia' (la pone
     'prepararTabla') por si alguna pantalla quiere afinar su estado sin
     datos. NO se le toca el reparto de columnas: probado el 23/8/2026,
     forzar 'table-layout:fixed' para que la cabecera no desborde aprieta
     las columnas a partes iguales, mete el texto de una dentro de la
     vecina y recorta el botón de columnas — peor que la barra de
     desplazamiento que se quería quitar. Esa barra, además, no es un
     defecto del estado vacío: con 14 columnas la tabla también desborda
     llena (es lo que hacen las rejillas de referencia). */

  /* --- Diálogo de edición: mismo lenguaje visual (Regla 7) --- */
  dialog.dialogo { padding:0; border:1px solid var(--linea2); border-radius:10px;
                   background:var(--papel); color:var(--tinta); max-width:520px; width:calc(100vw - 28px);
                   box-shadow:0 18px 48px rgba(16,24,40,.24); }
  /* Variante ancha: diálogos con tabla (más columnas que un formulario). */
  dialog.dialogo.dialogo-ancho { max-width:860px; }
  /* Variante muy ancha: diálogos con mucho contenido en paralelo (p. ej.
     "Detalles del sistema" — cabecera + líneas + confianzas a la vez).
     REGLA COMPONENTES: un tercer escalón reutilizable, no un ancho suelto
     para una sola pantalla. */
  dialog.dialogo.dialogo-muy-ancho { max-width:1200px; }
  /* Variante A TODA LA PANTALLA (pedido explícito 17/8/2026): para diálogos
     de revisión con mucho contenido (cabecera + líneas + documento), donde
     el recuadro pequeño de siempre se queda corto. Alto fijo (mismo motivo
     que el visor): así '.dialogo-cuerpo' tiene un 100% real del que
     desplazarse por dentro, y la cabecera/pie quedan siempre a la vista. */
  dialog.dialogo.dialogo-pantalla-completa { width:100vw; height:100vh; max-width:100vw; max-height:100vh;
                                              border-radius:0; }
  /* 'display' SOLO cuando está abierto — de lo contrario se pisa el
     'display:none' de fábrica del <dialog> cerrado y se queda visible EN
     LA PÁGINA, en flujo normal, como un bloque más (bug real: se veía
     "Revisar documento" pegado bajo la tabla sin haber pulsado nada). */
  dialog.dialogo.dialogo-pantalla-completa[open] { display:flex; flex-direction:column; }
  dialog.dialogo.dialogo-pantalla-completa .dialogo-cuerpo { flex:1 1 auto; max-height:none; }
  .estructura-grupo { margin:14px 0 6px; font-size:12px; font-weight:700; color:var(--tinta3); text-transform:uppercase; letter-spacing:.04em; }
  .estructura-grupo:first-child { margin-top:0; }
  #scanEstructuraCuerpo table.tabla { font-size:12.5px; }
  #scanEstructuraCuerpo table.tabla td:first-child { font-family:monospace; font-size:11.5px; color:var(--tinta2); }
  dialog.dialogo::backdrop { background:rgba(16,24,40,.5); }
  .dialogo-cabecera { padding:15px 18px; border-bottom:1px solid var(--linea); }
  .dialogo-cabecera h2 { margin:0; font-size:15px; font-weight:600; display:flex; align-items:center; gap:9px; }
  .dialogo-cabecera h2::before { content:""; width:3px; height:16px; border-radius:2px; background:var(--rojo); }
  .dialogo-cuerpo { padding:18px; display:grid; gap:14px; max-height:min(60vh, 460px); overflow-y:auto; }
  /* Pie SIEMPRE visible aunque el formulario sea largo: en móvil es crítico. */
  .dialogo-pie { position:sticky; bottom:0; display:flex; justify-content:flex-end; gap:8px;
                 padding:13px 18px; border-top:1px solid var(--linea); background:var(--papel2); }
  .campo { display:grid; gap:5px; }
  .campo label { font-size:12px; font-weight:600; color:var(--tinta2); }
  /* REGLA: en cualquier formulario de mantenimiento, un campo OBLIGATORIO se
     marca con un asterisco rojo junto a la etiqueta — el estándar del
     sector, reconocible sin leer nada más. Nunca con texto ("obligatorio").
     Se pone la clase en el '.campo' (no en el input), así vale para
     cualquier tipo de control. */
  .campo.requerido label::after { content:" *"; color:var(--rojo); font-weight:700; }
  .campo input, .campo select, .campo textarea {
    min-height:38px; padding:8px 11px; font:inherit; font-size:13.5px;
    border:1px solid var(--linea2); border-radius:var(--r);
    background:var(--papel); color:var(--tinta); width:100%; }
  .campo input:focus, .campo select:focus, .campo textarea:focus {
    outline:none; border-color:var(--rojo); box-shadow:0 0 0 3px rgba(212,35,42,.14); }
  .campo .ayuda { font-size:11.5px; color:var(--tinta3); }
  /* Dato LEÍDO, no editable — mismo alto/tipografía que un input, sin borde
     de edición (REGLA COMPONENTES DE INTERFAZ: un solo patrón, reutilizado
     en cualquier pantalla que muestre cabecera ya leída, p.ej. Bandeja). */
  .campo-solo-lectura { min-height:38px; padding:8px 0; font-size:13.5px; color:var(--tinta); display:flex; align-items:center; }

  /* --- Móvil: cada fila pasa a ser una TARJETA ---------------------
     Una tabla de seis columnas en 390 px no se lee. En vez de encogerla o
     esconder columnas, cada fila se convierte en una ficha con sus datos
     etiquetados y sus acciones dentro. Nada se pierde. */
  @media (max-width:767px) {
    .panel-cabecera { padding:11px; }
    .panel-acciones { width:100%; margin-left:0; }
    .buscador { flex:1 1 auto; }
    .buscador input { width:100%; }

    table.tabla thead { display:none; }
    table.tabla, table.tabla tbody { display:block; width:100%; }
    /* Cada fila es una FICHA compacta en rejilla de DOS columnas: la mitad
       de alto que la lista apilada de antes (en móvil se veían fichas
       enormes, un campo por línea — pedido explícito: compacto y
       profesional en vertical y horizontal). El primer campo (el nombre o
       documento, el que identifica la ficha) y las acciones ocupan el
       ancho entero; el resto se reparte de dos en dos. */
    table.tabla tr { display:grid; grid-template-columns:repeat(2, minmax(0, 1fr));
                     column-gap:16px; row-gap:1px;
                     padding:8px 12px; position:relative; }

    /* --- CADA FICHA ES UNA TARJETA DE VERDAD (22/8/2026) --------------
       Antes solo llevaba una raya de separación abajo, de lado a lado:
       es el estilo de "lista con separador", que funciona cuando cada
       elemento son dos o tres campos. Con DOCE campos apilados deja de
       funcionar — probado en un móvil real: no se distinguía dónde
       acababa una línea y empezaba la siguiente, y el bloque flotaba
       sobre el blanco sin ningún límite, justo al lado de los paneles de
       totales que SÍ tienen su recuadro. Se le da el mismo tratamiento de
       tarjeta que ya usa el resto del producto (borde, esquina redonda y
       la sombra sutil de '--sombra'), con los colores NEUTROS del propio
       sistema para que siga bien en tema claro y oscuro sin tocar nada.

       El margen LATERAL no es decorativo: sin él la tarjeta ocupaba los
       375 px justos de la pantalla y sus bordes izquierdo y derecho caían
       en el filo, o sea que no se veían (medido: la fila iba de 0 a 375).
       Una tarjeta se reconoce porque flota sobre el fondo; pegada a los
       bordes vuelve a parecer una lista. */
    table.tabla tbody tr { background:var(--papel); border:1px solid var(--linea);
                     border-radius:var(--r); box-shadow:var(--sombra); margin:0 10px 10px; }
    table.tabla tbody tr:last-child { margin-bottom:0; }
    /* El hover de escritorio pinta la fila entera; sobre una tarjeta con
       fondo propio sobra y solo ensucia. */
    table.tabla tbody tr:hover { background:var(--papel); }

    /* LA BARRITA DE ARRIBA: el primer campo es la CABECERA de la tarjeta
       (el dato que la identifica). Ocupa el ancho entero, con un fondo
       neutro y una línea debajo que lo separa de los campos. Los márgenes
       negativos son EXACTAMENTE el 'padding' del <tr> (8px/12px) para que
       la barra llegue a los bordes de la tarjeta en vez de quedar flotando
       con un marco blanco alrededor. */
    table.tabla tbody td:first-child { background:var(--papel2);
                     border-bottom:1px solid var(--linea); border-radius:var(--r) var(--r) 0 0;
                     margin:-8px -12px 8px; padding:8px 12px; }
    table.tabla td { border:none; padding:2px 0; display:flex; gap:8px; align-items:baseline; min-width:0; }
    /* En modo FICHA la cebra no aplica: cada fila ya es una tarjeta con su
       borde, y rayar los campos DENTRO de la tarjeta la rompería. */
    table.tabla tbody tr:nth-child(even) td,
    table.tabla tbody tr:hover td { background:transparent; }
    table.tabla td::before { content:attr(data-col); flex:0 0 auto; max-width:45%; font-size:10.5px; font-weight:600;
                             color:var(--tinta3); text-transform:uppercase; letter-spacing:.03em;
                             overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
    table.tabla td:first-child { grid-column:1 / -1; }

    /* --- LA ETIQUETA VA ENCIMA DEL VALOR, NO AL LADO (22/8/2026) -------
       Corregido tras probar en un móvil real. Antes la etiqueta iba
       DELANTE y se comía hasta el 45 % de una celda de 168 px: al valor
       le quedaban ~85 px. Medido en esa pantalla: 'Artículo' pedía
       415 px y 'Producto mapeado' 240 — se cortaban siempre.

       No es una preferencia, es lo que dicen las fuentes de referencia:
         · Baymard, con más de 1.000 campos de formulario medidos en
           móvil: con la etiqueta delante "la pantalla estrecha deja muy
           poco espacio para el campo en sí"; el valor sale truncado y la
           persona NO puede ver si se equivocó al escribir — acababan
           borrando y reescribiendo el campo entero.
         · Wroblewski (eye-tracking, replicado muchas veces): etiqueta
           encima ≈ el doble de rápido de completar.
         · SAP Fiori —el sistema de diseño de ERPs, nuestro mismo
           terreno— lo llama 'popinDisplay: Block' en su Responsive
           Table: cuando una columna no cabe, baja debajo de la fila CON
           SU ETIQUETA ENCIMA.
       Con esto el valor recupera los 168 px enteros de la media ficha. */
    table.tabla td { flex-direction:column; align-items:stretch; gap:0; }
    table.tabla td::before { max-width:100%; line-height:1.35; }
    /* La etiqueta encima gana claridad pero suma una línea por campo: se
       recupera apretando el interlineado del valor, NO escondiendo
       campos (REGLA WEB: en móvil se reorganiza, no se oculta). */
    table.tabla td { line-height:1.35; }
    /* Celdas SIN etiqueta (nº de línea, confianza, acciones): el
       pseudo-elemento queda vacío y solo aportaría un hueco de más. */
    table.tabla td:not([data-col])::before { display:none; }

    /* En FICHA no hay columna vecina que invadir, así que el recorte con
       puntos suspensivos de la tabla redimensionable —correcto y
       necesario en ESCRITORIO, donde las columnas se arrastran— aquí
       solo sirve para esconder datos. Se deja que el texto largo pase a
       varias líneas, que es justo lo que una ficha permite. */
    table.tabla.tabla-redimensionable td { overflow:visible; text-overflow:clip; white-space:normal; }
    .col-truncar { max-width:100%; white-space:normal; }

    /* Campos de TEXTO LARGO, a ancho completo. Es la "prioridad de
       columnas" del pop-in de Fiori aplicada a nuestro caso: lo que no
       cabe en media ficha no se recorta, ocupa la ficha entera. */
    table.tabla td.celda-ancha { grid-column:1 / -1; }

    /* --- LA TABLA DE TOTALES NO PASA A FICHAS -------------------------
       Fiori lo dice y aquí se ve claro: la ficha es para una fila con
       muchos campos heterogéneos, no para una tabla que existe PARA
       COMPARAR cifras entre sí. Son 4 columnas cortas y numéricas
       (impuesto, base, cuota, total) que caben de sobra en 375 px; en
       ficha cada importe salía suelto en su línea y no había forma de
       leer el desglose, que es justo su única razón de ser. */
    table.tabla.tabla-totales-scan { display:table; }
    table.tabla.tabla-totales-scan thead { display:table-header-group; }
    table.tabla.tabla-totales-scan tbody { display:table-row-group; }
    table.tabla.tabla-totales-scan tr { display:table-row; padding:0; border-bottom:none; }
    table.tabla.tabla-totales-scan td,
    table.tabla.tabla-totales-scan th { display:table-cell; padding:7px 5px; font-size:12px; }
    table.tabla.tabla-totales-scan td::before { display:none; }
    table.tabla.tabla-totales-scan td.num, table.tabla.tabla-totales-scan th.num { text-align:right; }
    /* HASTA EL 23/8/2026 aquí ponía 'table:is(.datos,.tabla)': existían DOS
       nombres para el mismo componente y había que nombrar los dos. Ya no:
       '.datos' se retiró y todo es '.tabla'. Antes esta ficha era solo
       para '.datos' porque '.tabla' no llevaba 'data-col' en sus celdas y
       habría salido sin etiquetas — peor que nada. Ya no es cierto: el
       'data-col' lo rellena sola 'prepararTabla' leyendo la cabecera
       (app.js), así que las 15 tablas 'tabla compacta' —Empresas,
       Almacenes, Categorías, Departamentos, Bandeja, Cartera…— pasan a
       ficha igual que el resto. Antes de esto, en un móvil salían como
       una tabla apretada con scroll lateral (medido y visto). */
    .col-sel, .col-acciones { width:auto; text-align:left; }
    /* --- Columnas "angostas" en modo ficha ------------------------------
       'Doc. origen' y 'Tipo' llevan 'width:1%' — el truco clásico para que
       una columna de tabla se encoja a su contenido. En una FICHA no hay
       tabla: ese 1 % es el 1 % del ancho de la ficha, y las celdas medían
       4 px y 2 px (medido) con su etiqueta y su valor desbordando encima de
       la celda vecina. Se devuelven a 'auto' y se alinean como las demás.
       El 'sticky' de Id tampoco tiene sentido aquí (no hay desplazamiento
       horizontal que seguir) y su sombra dibujaba una raya suelta. */
    table.tabla td.col-id, table.tabla td.col-tipo { width:auto; text-align:left; }
    table.tabla td.col-id,
    table.tabla td.col-acciones,
    table.tabla td.col-check { position:static; box-shadow:none; background:transparent; }

    /* --- CABECERA DE LA FICHA DE UNA LÍNEA ----------------------------
       El nº de línea y la confianza, juntos en la primera fila de la
       ficha: uno a la izquierda y otra a la derecha, como el encabezado
       de una tarjeta. Llevan 'width:1%' (el truco de tabla para que una
       columna se encoja a su contenido) y en modo ficha ese 1 % son
       12 px y 14 px REALES — medido: el número necesitaba 29 y la
       confianza 59, así que los dos salían ilegibles y dejaban esa
       franja vacía al principio de cada línea. Mismo caso que
       'col-id'/'col-tipo' de aquí arriba, que ya estaba resuelto.
       Estos dos NO llevan etiqueta, así que van en fila, no en columna.

       OJO CON LA ESPECIFICIDAD: hay que ganarle a 'table.tabla td:first-child'
       (0-2-2) de más arriba, que da la fila entera al primer campo. Por eso
       estos selectores llevan 'table.tabla' delante — con '.tabla-linea-scan
       td:first-child' (0-2-1) NO bastaba y el número seguía ocupando toda la
       fila (comprobado en el móvil, no supuesto). */
    table.tabla.tabla-linea-scan td.col-orden,
    table.tabla.tabla-linea-scan td.col-confianza {
      width:auto; flex-direction:row; align-items:center; }
    /* El nº de línea ES la cabecera de la tarjeta: hereda la barrita de
       'td:first-child' de arriba y solo se le pone el número en negrita. */
    table.tabla.tabla-linea-scan td.col-orden { text-align:left;
      font-weight:700; color:var(--tinta2); }
    /* La confianza va DENTRO de esa misma barrita, a la derecha. Se saca
       del flujo de la rejilla (el <tr> ya es 'position:relative') porque
       si no, el hueco entre columnas partiría la barra en dos con una
       franja blanca justo en medio. */
    table.tabla.tabla-linea-scan td.col-confianza { position:absolute; top:8px; right:12px;
      width:auto; padding:0; margin:0; justify-content:flex-end; }
    .col-acciones { grid-column:1 / -1; justify-content:flex-end; padding-top:6px; }
    .col-acciones::before { content:""; flex:0; }
    .num { text-align:left; }

    /* --- CABECERA DEL RESOLVER: QUE PARTA POR DENTRO ------------------
       Cada '.cab-item' es una tira horizontal (etiqueta + valor +
       pastillas + botones). '.cab-linea' ya envolvía, pero el
       '.cab-item' de dentro no, así que uno solo —el de "En Ágora", con
       proveedor, CIF y cuatro botones— pedía 536 px en una pantalla de
       375 y se salía 196 px, arrastrando con él todo lo que llevaba
       dentro. Se le deja partir también. */
    .cab-item, .cab-val { flex-wrap:wrap; max-width:100%; min-width:0; }

    .dialogo-cuerpo { max-height:calc(100dvh - 190px); }
  }

  .vista { display:none; }
  .vista.activa { display:block; }

  /* Foco visible y coherente en todo lo que se puede pulsar (accesibilidad). */
  :focus-visible { outline:2px solid var(--rojo); outline-offset:2px; }

  /* ===================================================================
     TABLET Y MÓVIL

     Tres tamaños, y en ninguno se pierde información:

       ≥1024  ordenador           barra lateral fija, todo a la vista
       768-1023  tablet           barra lateral más estrecha
       <768   móvil               la barra lateral pasa a ser un CAJÓN que se
                                  abre con el botón de menú

     El cajón es la solución correcta en móvil: esconder la ficha del equipo y
     el estado de conexiones —como se hacía antes— dejaba al usuario sin poder
     consultarlos. Ahora están a un toque.
     =================================================================== */
  /* El botón SIEMPRE se ve, también en el ordenador: allí pliega el panel para
     ganar pantalla, y en móvil abre el cajón. Un solo control, dos usos. */
  .boton-menu { display:inline-flex; align-items:center; justify-content:center; width:40px; height:40px;
                margin-left:-8px; background:none; border:1px solid transparent; border-radius:var(--r);
                color:var(--tinta2); cursor:pointer; flex:0 0 auto; }
  .boton-menu:hover { background:var(--papel3); color:var(--tinta); }
  .boton-menu svg { width:20px; height:20px; transition:transform .2s ease; }
  .velo { display:none; position:fixed; inset:0; background:rgba(16,24,40,.45); z-index:60; }

  /* Tirador redondo en el canto del panel: la forma más reconocible de decir
     "esto se pliega". Aparece al acercar el ratón o al enfocarlo con teclado. */
  .lateral { position:relative; transition:opacity .18s ease; }
  body { transition:grid-template-columns .18s ease; }
  .tirador { position:absolute; top:64px; right:-13px; width:26px; height:26px; z-index:5;
             display:grid; place-items:center; border-radius:50%;
             background:var(--control-fondo); border:1px solid var(--control-borde); color:var(--tinta2);
             cursor:pointer; box-shadow:var(--sombra); opacity:0; transition:opacity .15s ease; }
  .lateral:hover .tirador, .tirador:focus-visible { opacity:1; }
  .tirador:hover { color:var(--rojo); border-color:var(--rojo); }
  .tirador svg { width:14px; height:14px; }

  /* Plegado: el panel se va del todo y el contenido ocupa el ancho completo.

     El estado vive en el <html> ('data-plegado'), no en una clase del <body>.
     Así lo puede poner el script de la cabecera ANTES de pintar, cuando el
     <body> todavía no existe; si no, el panel daba un salto en cada F5. */
  :root[data-plegado="1"] body { grid-template-columns:0 1fr; }
  :root[data-plegado="1"] .lateral { opacity:0; pointer-events:none; }
  :root[data-plegado="1"] .boton-menu svg { transform:rotate(180deg); }

  /* Borde para REDIMENSIONAR: franja fina en el canto derecho del panel. Se
     ilumina al acercarse, para que se vea que se puede arrastrar. */
  .redimensionar { position:absolute; top:0; right:-3px; bottom:0; width:6px; z-index:4;
                   cursor:col-resize; background:transparent; }
  .redimensionar:hover, body.redimensionando .redimensionar { background:var(--rojo); opacity:.3; }
  /* Mientras se arrastra: sin transición (o el panel iría a rastras) y sin
     seleccionar texto sin querer. */
  body.redimensionando { user-select:none; cursor:col-resize; }
  body.redimensionando, body.redimensionando .lateral { transition:none; }
  /* Con el dedo no se arrastra un borde de 6 px: en táctil no existe. */
  @media (max-width:767px), (pointer:coarse) { .redimensionar, .tirador { display:none; } }

  @media (max-width:1023px) {
    :root { --lateral:200px; }
    .barra-sup { padding:0 16px; }
  }

  /* --- CAJÓN LATERAL: hasta 900 px, NO hasta 767 -----------------------
     Medido en la matriz de la REGLA WEB: en tablet de pie (768x1024) la
     barra lateral fija se comía 200 px —un 26 % del ancho— y la lista de
     Revisión, que pide 1707 px, se quedaba trabajando en 568. Con el cajón,
     la tablet trabaja a ancho completo y el panel sigue a un toque.
     900 px NO es un número nuevo: es EXACTAMENTE el mismo punto en el que
     el Resolver ya apila documento y líneas ('.resolver-cuerpo' más abajo,
     y 'ajustarAltoDoc' en comun.js) — un único umbral "esto ya no es un
     escritorio" en todo el producto, en vez de dos que se contradigan.
     Va en su PROPIO bloque, aparte del de 767: lo que queda debajo (rejilla
     a una columna, chat, tipografía apretada) es de TELÉFONO, y aplicarlo a
     una tablet de 768 px la dejaría pobre. --- */
  @media (max-width:900px) {
    body { grid-template-columns:1fr; }

    /* EL PLEGADO NO EXISTE EN MÓVIL, y hay que anularlo a propósito.
       Si el usuario plegó el panel en el ordenador, eso queda guardado en su
       navegador; al abrir en el móvil, el plegado dejaba el cajón con
       opacidad 0 y sin poder tocarlo: se abría INVISIBLE. Aquí se recupera. */
    :root[data-plegado="1"] body { grid-template-columns:1fr; }
    :root[data-plegado="1"] .lateral { opacity:1; pointer-events:auto; }
    :root[data-plegado="1"] .boton-menu svg { transform:none; }

    /* La barra lateral sale del flujo y se convierte en cajón. */
    .lateral { position:fixed; inset:0 auto 0 0; width:min(84vw, 300px); z-index:70;
               transform:translateX(-100%); transition:transform .22s ease;
               box-shadow:var(--sombra-menu);
               /* En pantalla corta —un móvil tumbado— el contenido del panel no
                  cabe: se desplaza dentro de él, nunca se recorta. */
               overflow-y:auto; }
    .lateral.abierta { transform:translateX(0); }
    .velo.visible { display:block; }

    .boton-menu { display:inline-flex; }
  }

  @media (max-width:767px) {
    .barra-sup { padding:0 12px; gap:8px; height:52px; }
    .titulo-pagina { font-size:15px; }
    /* En pantalla estrecha la pastilla de desarrollo sobra: ocupa sitio y no
       es información que el usuario necesite. */
    .barra-sup .pastilla { display:none; }
    .barra-sup .derecha { gap:6px; }
    .selector-nombre { display:none; }        /* solo la bandera */

    /* --- Lo que se retira de la barra en un teléfono, y por qué ----------
       Medido a 390 px: los siete elementos de la derecha sumaban 428 px en
       una barra de 390 — la página se desplazaba de lado (el defecto que la
       REGLA WEB prohíbe). Se recorta por orden de prescindibilidad, sin
       perder NINGUNA función:
         · el bloque de IA es de PGM y ya se oculta la pastilla de desarrollo
           por el mismo motivo: en un móvil no es información que nadie use;
         · del usuario queda el icono (quién soy se sigue viendo), no el
           nombre completo;
         · 'Cerrar sesión' pasa a icono — la acción sigue ahí, a un toque, y
           con su 'title' para saber qué es.
       Resultado medido: 327 → 190 px, sin desplazamiento lateral. --- */
    .barra-sup .ia-pgm { display:none; }
    .sesion-usuario #sesionNombre { display:none; }
    .sesion-usuario { gap:0; margin-right:0; }
    .boton-salir span { display:none; }
    .boton-salir .boton-salir-icono { display:block; }
    .boton-salir { padding:0; width:40px; justify-content:center; }

    .rejilla { grid-template-columns:1fr; gap:12px; }
    .lienzo { padding:32px 16px; }
    .lienzo-alto { min-height:calc(100vh - 150px); }

    /* Chat: el alto se calcula con la unidad dinámica para que la barra del
       navegador móvil no deje la caja de escribir fuera de pantalla. */
    .chat { height:calc(100dvh - 200px); }
    .msg { max-width:92%; }
    .chat-barra { gap:8px; padding:8px 10px; }
    .chat-gasto { margin-left:0; width:100%; order:9; }
    .chat-pie { padding:9px 10px; }
    .chat-enviar { padding:9px 14px; }
  }

  /* Móvil TUMBADO: queda muy poca altura. Se recorta lo vertical para que la
     caja de escribir siga estando en pantalla. */
  @media (max-width:919px) and (orientation:landscape) and (max-height:500px) {
    .chat { height:calc(100dvh - 165px); }
    .barra-sup { height:46px; }
    .lienzo-alto { min-height:auto; padding:24px 16px; }
    .marca-caja { padding:10px 14px; }
  }

  /* Dedos, no ratón. Dos cosas cambian respecto al ratón:
       - nada pulsable por debajo de 40x40 (medida de Apple y de Google);
       - nada de letra por debajo de 11 px, que en una pantalla pequeña y a
         medio metro no se lee.
     Los valores salen de medir, no de estimar (ComprobarResponsivo.js). */
  @media (pointer:coarse) {
    .nav-item { padding:12px 10px; }          /* 11px daba 39: un píxel corto */
    .selector-opcion { padding:11px 10px; }
    .selector-boton, .boton-tema, .chat-limpiar, .chat-clip,
    .boton-menu, .chat-enviar, .subpantalla-volver { min-height:40px; min-width:40px; }
    .menu-item { padding:15px; }

    /* Las medidas mínimas de lo pulsable NO están aquí: van al FINAL de la
       hoja (buscar "TODO LO PULSABLE"). Motivo real, no estético — varias
       reglas base ('.campo-tipo', '.cab-item .boton-icono', '.visor-x'…)
       están escritas MÁS ABAJO que este bloque, y a igual especificidad
       gana la última: puestas aquí no llegaban a aplicarse nunca. Una
       media query no suma especificidad. */

    .lema { font-size:9.5px; letter-spacing:.1em; }
    .nav-item .pronto { font-size:11px; padding:2px 7px; }
    .ficha-linea, .ficha-linea b { font-size:12px; }
    .ficha-linea b.mono { font-size:11.5px; }
    .msg-pie { font-size:11px; }
    .chat-gasto { font-size:11.5px; }
  }

  /* ===================================================================
     RESOLVER DE SCAN — pantalla completa (no diálogo): documento a la
     izquierda con zoom/pan, líneas a la derecha, cabecera estructurada
     arriba. El panel nunca lleva tope de ancho (REGLA APROVECHAMIENTO).
     Es una pantalla de trabajo densa; el "a hueso" (sin relleno del
     contenido, panel sin marco) nació aquí y ahora es el criterio GENERAL
     de todo el producto — está aplicado en '.contenido' y '.panel'. ----- */
  /* Cabecera COMPACTA en dos líneas densas (pedido explícito: la cabecera
     no puede comerse media pantalla — el trabajo real es el documento y
     las líneas). Formato "ETIQUETA valor" seguido, al estilo de un panel
     de operación: máxima información en mínimo alto. */
  .resolver-cab { display:flex; flex-direction:column; gap:6px; padding:8px 20px;
                   border-bottom:1px solid var(--linea); background:var(--papel2); }

  /* SOLO LECTURA: el documento lo tiene otra persona. La banda explica por
     qué no se puede tocar —un campo que no responde y no dice nada es peor
     que uno bloqueado con su motivo— y la clase apaga la edición de golpe.
     Se apaga por CONTENEDOR y no control a control a propósito: así los
     campos que la pantalla gane mañana quedan cubiertos sin acordarse de
     nada. Quien lo hace cumplir de verdad es el servidor (423). */
  /* Va EN LA BARRA DE MIGAS, no en una banda propia (pedido explícito
     22/8/2026): una fila entera para un aviso de una línea es espacio
     robado al trabajo real. Pastilla corta, y el motivo completo en el
     tooltip. */
  /* "En uso por X" en la lista de Revisión: al lado del estado, no en
     columna propia — es información pasajera y una columna vacía el 95%
     del tiempo sería espacio robado. */
  /* La celda apila: estado arriba, "en uso por X" DEBAJO (pedido explícito
     22/8/2026). Puestos en la misma línea, la columna crecía a lo ancho y se
     comía sitio de las demás; apilados, la fila sube unos píxeles solo
     cuando de verdad hay alguien dentro. */
  td.col-estado { display:flex; flex-direction:column; align-items:flex-start; gap:3px; }
  .pastilla-enuso { display:inline-flex; align-items:center; gap:4px;
                    padding:1px 8px; border-radius:999px;
                    background:var(--ambar-suave, #fff4d6);
                    color:var(--ambar-texto, #6b4b00);
                    border:1px solid var(--ambar-borde, #e8c97a);
                    font-size:11px; font-weight:600; white-space:nowrap; }

  /* La tecla de "Pedir" dentro de la pastilla de en-uso: pequeña pero
     pulsable (los 40px táctiles los da el alto de la barra de migas). */
  .enuso-avisar { margin-left:8px; padding:1px 8px; border-radius:999px;
                  border:1px solid var(--ambar-borde, #e8c97a);
                  background:var(--papel); color:inherit; font-size:11px;
                  font-weight:600; cursor:pointer; }
  .enuso-avisar:hover { background:var(--ambar-suave, #fff4d6); }

  .resolver-bloqueo { display:inline-flex; align-items:center; gap:6px;
                      margin-left:10px; padding:2px 10px; border-radius:999px;
                      background:var(--ambar-suave, #fff4d6);
                      color:var(--ambar-texto, #6b4b00);
                      border:1px solid var(--ambar-borde, #e8c97a);
                      font-size:12px; font-weight:600; white-space:nowrap; }
  /* Se deja VIVO el visor del documento (mirar siempre es libre) y la
     navegación; se apaga la cabecera editable y la tabla de líneas. */
  /* Un select DENTRO de una celda de tabla nunca queda más bajo que una
     zona pulsable digna (26/8/2026, medido: el de Almacenes salía con 19px
     de alto al heredar el line-height compacto de la fila). Por componente,
     no pantalla a pantalla: cubre los de hoy y los que vengan. */
  table.tabla td select { min-height:28px; }
  .solo-lectura .resolver-cab,
  .solo-lectura .resolver-lineas,
  .solo-lectura .resolver-totales,
  .solo-lectura .resolver-impuestos { pointer-events:none; opacity:.72; }
  /* El Revisar de Documental usa el MISMO modo (misma reserva, mismo 423):
     su lado de ficha entero queda mudo — paneles, escalera y botones. */
  #revdLado.solo-lectura .cab-panel { pointer-events:none; opacity:.72; }
  #revdLado.solo-lectura select { background:var(--papel2); }
  .solo-lectura .resolver-cab input,
  .solo-lectura .resolver-cab select,
  .solo-lectura .resolver-lineas input,
  .solo-lectura .resolver-lineas select { background:var(--papel2); }
  /* Cada línea es su PROPIA tarjeta (pedido explícito): el proveedor —lo
     primero que hay que resolver— se pinta antes, en su panel; los datos
     del documento van debajo, en el suyo. */
  .cab-panel { background:var(--papel); border:1px solid var(--linea2); border-radius:var(--r); padding:5px 14px; }
  /* EL PANEL DE LOS CAMPOS FUNDAMENTALES de una revisión (25/8/2026,
     pedido explícito: "destaca el panel que tiene todos los campos
     fundamentales para esta revisión").

     Va al SISTEMA y no a una pantalla suelta a propósito: la pantalla
     Revisar va a servir para las DOS capas —Gestión Documental y Gestión
     Económica— cambiando solo QUÉ campos lleva dentro, con la misma
     presentación. Si el realce viviera dentro de una de ellas, la otra
     nacería distinta (REGLA COMPONENTES DE INTERFAZ).

     Se destaca con el color de marca en el borde izquierdo y un fondo
     apenas teñido: suficiente para que la vista aterrice ahí primero, sin
     el ruido de un recuadro de color entero. */
  .cab-panel.cab-panel-clave {
    border-left:3px solid var(--rojo);
    background:color-mix(in srgb, var(--rojo) 3%, var(--papel));
  }
  /* Aviso breve de "lo que falta para Listo" en Resolver — una sola linea,
     no ocupa un panel entero (pedido explicito: no molestar).
     'flex-wrap:wrap' A PROPÓSITO (21/8/2026, pedido explícito): con el
     interruptor de descuadre + "Confirmar" + "Desconfirmar" pueden caber
     más pastillas/botones de los que entran en el ancho del panel — la
     barra tiene que CRECER HACIA ABAJO (una fila más), nunca hacia la
     derecha empujando "Confirmar" fuera de la vista. 'overflow:hidden' se
     retira: con 'height:auto' (de fábrica) nunca recortaba nada, pero
     mantenerlo aquí invitaba a pensar que sí protegía del desborde. */
  .resolver-pendiente { padding:0 14px; display:flex; flex-wrap:wrap; align-items:center; row-gap:6px; column-gap:0; }
  .resolver-pendiente .pastilla { font-size:11px; max-width:100%; white-space:nowrap;
                                   overflow:hidden; text-overflow:ellipsis; }
  /* Interruptor de "aceptar descuadre" (BD-004): mismo lenguaje que la
     pastilla vecina (borde ámbar, igual que un aviso) — vive DENTRO de la
     fila flex de arriba, así que envuelve solo si hace falta, nunca
     desborda ni tapa el botón de confirmar. */
  .interruptor-pastilla { display:inline-flex; min-height:auto; margin:0 8px 0 4px;
                           padding:3px 10px 3px 8px; border-radius:20px; border:1px solid var(--ambar);
                           background:var(--ambar-tenue); color:var(--ambar); font-size:11px; font-weight:600; }
  /* La casilla NO fija su propio tamaño aquí: en ratón hereda el nativo
     (13x13, como cualquier checkbox del producto); en táctil la agranda a
     40x40 la regla general del final de la hoja ("TODO LO PULSABLE") — la
     MISMA que usa toda la aplicación, para no inventar una segunda medida
     táctil solo para esta pastilla. */
  .pastilla-boton { border:none; cursor:pointer; font-family:inherit; }
  .pastilla-boton:hover { filter:brightness(0.94); }
  /* Selector de TIPO al subir (pedido explícito 17/8/2026: "le informamos a
     la IA del tipo, para facilitarle el trabajo") — pastillas de color
     CLICABLES, una sola marcada a la vez (mismo look que el resto del
     producto, REGLA COMPONENTES: ningún estilo nuevo suelto). El color
     identifica el tipo de un vistazo; el anillo marca cuál está elegido. */
  .tipo-selector { display:flex; gap:8px; justify-content:center; flex-wrap:wrap; margin-top:12px; }
  .tipo-selector .pastilla-boton { padding:5px 12px 5px 10px; font-size:13px; font-weight:600;
                                    box-shadow:0 0 0 2px transparent; transition:box-shadow .12s ease; }
  .tipo-selector .pastilla-boton.activo { box-shadow:0 0 0 2px currentColor; }
  /* Detalle de "lo que falta" (diálogo al pinchar la pastilla). */
  .pendiente-lista { list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:8px; }
  .pendiente-item { padding:8px 10px; border:1px solid var(--linea2); border-radius:var(--r); font-size:13px; }
  .pendiente-item-accion { display:flex; justify-content:space-between; align-items:center; gap:10px;
                            cursor:pointer; }
  .pendiente-item-accion:hover { background:var(--papel2); }
  .pendiente-ir { font-size:12px; font-weight:600; color:var(--acento); white-space:nowrap; }
  .cab-linea { display:flex; flex-wrap:wrap; align-items:center; gap:5px 22px; min-height:24px; }
  /* Un poco más de aire ANTES de esta línea (pedido explícito, 26/8/2026):
     sirve para separar un grupo de campos del siguiente dentro del mismo
     '.cab-panel' —aquí, los Departamentos de la escalera Empresa/Tipo/
     Concepto que los propone— sin inventar un margen distinto por
     pantalla (REGLA COMPONENTES DE INTERFAZ). */
  .cab-linea-separada { margin-top:10px; }
  /* Bloque de campos LIBRES (referencia/observaciones): se separa un poco
     más de la escalera de arriba (27/8/2026, pedido explícito "separar un
     poco del último campo, departamentos") — no son escalón, y el aire
     extra lo deja claro de un vistazo. */
  .cab-linea-grupo-libre { margin-top:22px; }
  /* Aviso informativo bajo un campo de la escalera (concepto/departamentos
     sin dar de alta): mismo rojo de aviso que tenía el enlace, pero SIN
     comportarse como enlace — solo informa, no navega (REGLA UN SOLO SITIO:
     las dos filas lo usan igual). */
  .cab-aviso { font-size:12.5px; font-weight:600; color:var(--rojo); }
  .cab-item { display:inline-flex; align-items:center; gap:7px; }
  .cab-etq { font-size:10px; font-weight:700; color:var(--tinta3); text-transform:uppercase; letter-spacing:.04em; white-space:nowrap; }
  .cab-val { font-size:13.5px; color:var(--tinta); display:inline-flex; align-items:center; gap:6px; }
  .cab-val strong { font-size:14.5px; }
  /* Valor de cabecera que en realidad es un AVISO (p. ej. "sin almacenes
     asignados"): se lee como advertencia, no como un dato normal. */
  .cab-val-aviso { color:var(--ambar); font-style:italic; }
  .cab-chips { gap:6px; flex-wrap:wrap; }
  .cab-chip { font-size:12px; padding:2px 9px; border:1px solid var(--linea2); border-radius:12px;
              background:var(--papel); color:var(--tinta); white-space:nowrap; }
  .cab-flecha { color:var(--tinta3); font-size:15px; }
  /* En estrecho los dos lados se apilan y la flecha queda huérfana en su
     propia línea: se retira, la relación ya se lee por el orden. */
  @media (max-width:700px) { .cab-flecha { display:none; } }
  .cab-item .campo-tipo { height:28px; }
  /* Descuento de cabecera editable (BD-006): campo pequeño, en línea con su
     etiqueta — no toda la anchura, es un número corto.
     FALLO REAL corregido (21/8/2026): '.campo-cabecera' y '.campo-linea'
     tienen la MISMA especificidad (una clase); como '.campo-linea{width:
     100%}' va DESPUÉS en este fichero, ganaba él y el campo se estiraba a
     un ancho variable según el hueco disponible — Dto % y Dto € acababan
     con anchos distintos aunque los dos llevaran esta clase. Con las DOS
     clases en el selector, la especificidad ya no empata y este ancho
     manda siempre, sea cual sea el orden en el fichero. */
  .campo-linea.campo-cabecera { width:76px; height:28px; }
  .cab-item .boton-icono { min-height:0; padding:4px 8px; min-width:32px; }
  .resolver-proveedor { position:relative; min-width:260px; }
  /* Desglose de impuestos: un documento puede traer varias bases (tipos
     distintos de IGIC/IVA en el mismo albarán), así que es una tablita, no
     un campo suelto. Compacta: solo son 2-3 filas normalmente. */
  .resolver-impuestos { padding:0 20px 14px; }
  .resolver-impuestos table.tabla-compactos th, .resolver-impuestos table.tabla-compactos td { padding:5px 10px; font-size:12.5px; }
  .campo-tipo { height:34px; padding:0 10px; border:1px solid var(--linea2); border-radius:var(--r-s); background:var(--papel); color:var(--tinta); font-size:14px; }

  /* Celda de mapeo (proveedor en cabecera, artículo en cada línea): la
     pastilla dice el estado, el icono siempre abre la MISMA utilidad —
     tanto para mapear como para cambiar lo ya mapeado. */
  .mapeo-celda { display:inline-flex; align-items:center; gap:6px; }
  /* Envuelve la celda de mapeo + el aviso de "mapeo dudoso" debajo
     (21/8/2026, pedido explícito) — columna, no fila, para que el texto
     pequeño caiga en su propia línea sin ensanchar la celda. */
  .mapeo-celda-col { display:flex; flex-direction:column; gap:1px; min-width:0; }
  /* Descripción con la que se aprendió el código, cuando no se parece a lo
     que lee ESTE documento — pequeño A PROPÓSITO ("que no ocupe mucho"),
     una sola línea con puntos suspensivos; el texto completo va en el
     'title'. 11px es el mínimo legible (REGLA WEB), no 10. */
  /* Mismo efecto que las pastillas '.p-aviso' de arriba (21/8/2026, pedido
     explícito: "mismo efecto, naranja") — mismas variables de color, mismo
     padding y radio que '.pastilla'. No se reutiliza la clase tal cual
     porque '.pastilla' es 'inline-flex' con un punto '::before', y aquí hace
     falta truncar con puntos suspensivos UN SOLO texto largo en una línea;
     mezclar las dos cosas rompía el truncado. */
  .mapeo-anterior { display:inline-block; font-size:11px; font-weight:600;
                     padding:3px 10px 3px 8px; border-radius:20px;
                     color:var(--ambar); background:var(--ambar-tenue);
                     border:1px solid color-mix(in srgb, var(--ambar) 25%, transparent);
                     max-width:220px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  /* Columna de casilla de selección (envío múltiple a Ágora): ancho fijo
     mínimo, no se deja crecer como el resto de columnas de texto. */
  .col-check { width:36px; text-align:center; }
  /* Columna "Activo/Activa" de los mantenimientos de Organización (26/8/2026,
     pedido explícito: "el primero que no ocupe mucho"). Va SIEMPRE la
     primera de la tabla — es lo primero que se quiere saber de una fila de
     catálogo. NO reutiliza '.col-check' a propósito: esa lleva enganchado el
     posicionamiento sticky de una columna de selección múltiple, que aquí no
     aplica y solo traería comportamiento sin pedir.

     SIN TEXTO EN LA CABECERA, a propósito (26/8/2026, corregido: el botón
     "Elegir columnas" de 'ss.prepararTabla' se inyecta SIEMPRE dentro de la
     primera cabecera de la tabla — es la esquina donde vive esa utilidad en
     TODO el producto — y aquí competía por el sitio con la palabra
     "Activo/Activa", quedando los dos apretados y el texto cortado en
     "···". Mismo criterio que ya usa '.col-check' (su cabecera tampoco
     lleva texto): el nombre de la columna se explica con un tooltip
     (data-t-titulo), no escrito encima del icono. */
  .col-activo { width:64px; min-width:64px; text-align:center; }

  /* EL SELECTOR DE EMPRESA de los mantenimientos de Organización (26/8/2026,
     pedido explícito: estaba con el aspecto crudo del navegador y se veía
     mal, y lo mismo en las varias pantallas que lo llevan — se corrige UNA
     vez para las cinco). Mismo lenguaje que '.filtro-dias select', que ya
     resuelve exactamente este problema en otra pantalla: flecha propia,
     borde y foco marcado — en vez de inventar un tercer aspecto de select
     en el producto. */
  .select-empresa { min-height:36px; padding:7px 32px 7px 12px; font:inherit; font-size:13.5px;
                     font-weight:600; border:1px solid var(--linea2); border-radius:var(--r);
                     background:var(--papel) url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="%23888" stroke-width="2"><path d="m6 9 6 6 6-6"/></svg>') no-repeat right 10px center/16px;
                     color:var(--tinta); cursor:pointer; appearance:none; -webkit-appearance:none; }
  .select-empresa:focus { outline:none; border-color:var(--rojo); box-shadow:0 0 0 3px rgba(212,35,42,.14); }
  /* Overlay de envío a Ágora: cubre TODO el panel de la lista (no solo un
     popover) — pedido explícito: mientras se envía, no se puede tocar nada
     ni pulsar el botón dos veces. Mismo lenguaje visual que
     '.mapeo-popover-fondo' (REGLA COMPONENTES DE INTERFAZ). 'position:
     absolute' necesita un ancestro posicionado — '#scanListaPanel' se marca
     'relative' aquí mismo, ya que '.panel' en general no lo es. */
  #scanListaPanel { position:relative; }
  .envio-espera-fondo { position:absolute; inset:0; z-index:10;
                         background:color-mix(in srgb, var(--papel) 88%, transparent);
                         display:flex; align-items:center; justify-content:center; }
  /* Fondo semitransparente detrás del popover de mapeo: refuerza que es
     una capa POR ENCIMA de la pantalla (como una ventana flotante), sin
     ser un diálogo modal de verdad — un clic fuera cierra igual. */
  .mapeo-popover-fondo { position:fixed; inset:0; z-index:59; background:rgba(16,24,40,.15); }
  /* Utilidad de mapeo: un único popover FLOTANTE, reutilizado por
     proveedor y por cada línea. 'position:fixed' porque se ancla al
     icono con getBoundingClientRect (con volteo automático si no cabe
     al lado o debajo — ver abrirMapeoPopover), sin depender del scroll
     de la tabla que lo contiene. Sombra marcada y un fondo semitransparente
     detrás (::before, a toda la ventana) para que se note claramente que
     es una capa POR ENCIMA de la pantalla, no parte de la fila. */
  .mapeo-popover { position:fixed; z-index:60; width:400px; max-width:calc(100vw - 20px); background:var(--papel);
                   border:1px solid var(--linea2); border-radius:var(--r-g); box-shadow:0 12px 32px rgba(16,24,40,.22), 0 2px 8px rgba(16,24,40,.12);
                   padding:14px; padding-right:44px; animation:mapeoPopoverEntra .12s ease-out; }
  /* Mismo patrón que '.caract-popover-cerrar' (REGLA COMPONENTES): un
     único estilo de "cerrar" en todos los popover flotantes del producto.
     Flotante en la esquina, no ocupa fila propia — el buscador sigue
     empezando arriba del todo. */
  .mapeo-popover-cerrar { position:absolute; top:10px; right:10px; width:32px; height:32px; border:none;
                           background:transparent; color:var(--tinta3); font-size:20px; line-height:1;
                           cursor:pointer; border-radius:var(--r-s); }
  .mapeo-popover-cerrar:hover { background:var(--rojo-tenue); color:var(--rojo); }
  /* Selector Referencia/Artículo (solo mapeo de línea): dos mitades, la
     activa en rojo — mismo lenguaje que el resto de segmentados del
     producto, sin inventar un componente nuevo. */
  .mapeo-popover-modo { display:flex; gap:4px; margin-bottom:8px; background:var(--papel3); border-radius:var(--r-s); padding:3px; }
  .mapeo-popover-modo-btn { flex:1; border:none; background:transparent; color:var(--tinta2); font-size:12.5px; font-weight:600;
                             padding:5px 0; border-radius:calc(var(--r-s) - 2px); cursor:pointer; min-height:30px; }
  .mapeo-popover-modo-btn.activo { background:var(--papel); color:var(--rojo); box-shadow:0 1px 3px rgba(16,24,40,.12); }
  /* Descripción COMPLETA del producto tal cual se leyó (21/8/2026, pedido
     explícito): el buscador de debajo solo lleva las 2 primeras palabras
     (busca mejor así), pero la descripción entera tiene que verse SIEMPRE,
     no solo al pasar el ratón — por eso va aparte, no como 'title'. Letra
     grande (mismo pedido): es el dato que de verdad importa mirar aquí. */
  .mapeo-popover-descripcion { font-size:15px; font-weight:600; color:var(--tinta); line-height:1.3; margin-bottom:8px; }
  .lista-elegir-ref { margin:0 6px; font-family:monospace; font-size:11px; }
  /* Nombre comercial junto al fiscal+CIF en el buscador de proveedor:
     dato secundario, atenuado, para no competir con el fiscal (el que de
     verdad se guarda al elegir). */
  .lista-elegir-comercial { color:var(--tinta3); font-size:11.5px; }
  /* Capa de espera al confirmar un mapeo: cubre TODO el popover (no solo la
     lista) para que no se pueda tocar nada mientras se guarda. */
  .mapeo-popover-espera { position:absolute; inset:0; z-index:1; border-radius:var(--r-g);
                           background:color-mix(in srgb, var(--papel) 88%, transparent); }
  @keyframes mapeoPopoverEntra { from { opacity:0; transform:translateY(-4px); } to { opacity:1; transform:translateY(0); } }
  .mapeo-popover-buscar { width:100%; height:44px; padding:0 12px; border:1.5px solid var(--linea2); border-radius:var(--r-s);
                           background:var(--papel); color:var(--tinta); font-size:15px; }
  .mapeo-popover-buscar:focus { border-color:var(--rojo); }
  /* Destello al abrir: para que se note que el foco saltó al buscador
     (pedido explícito), sin quedarse parpadeando para siempre. */
  .mapeo-popover-destello { animation:mapeoPopoverDestello .9s ease-out; }
  @keyframes mapeoPopoverDestello {
    0%, 30%, 60% { box-shadow:0 0 0 3px var(--rojo-tenue); border-color:var(--rojo); }
    15%, 45%, 100% { box-shadow:none; border-color:var(--linea2); }
  }
  .mapeo-popover-lista { margin-top:10px; max-height:280px; overflow:auto; grid-template-columns:1fr; }
  /* Selector de UNIDAD, dos pasos (BD-005): mismo popover flotante que el
     de mapeo (REGLA COMPONENTES DE INTERFAZ), con su propia cabecera para
     el título del paso actual + volver al paso 1. Ancho fijo, más estrecho
     que el de mapeo — aquí no hay buscador, solo una lista corta. */
  .unidad-popover { width:280px; }
  .unidad-popover-cab { display:flex; align-items:center; gap:10px; font-size:13.5px; font-weight:650;
                         letter-spacing:-.01em; margin-bottom:4px; }
  .mapeo-popover-desenlazar { width:100%; margin-top:10px; padding-top:10px; border:none; border-top:1px solid var(--linea);
                              background:transparent; color:var(--rojo); font-size:13px; text-align:center; cursor:pointer; }
  .mapeo-popover-desenlazar:hover { text-decoration:underline; }
  /* Panel de "Características del proveedor": SIEMPRE centrado en la
     ventana (nunca pegado a un botón — pedido explícito, se veía pequeño y
     descolocado) y notablemente más grande que un popover de mapeo simple:
     lleva lista + imagen + texto + dos botones, necesita respirar. */
  .caract-popover { width:720px; max-width:calc(100vw - 40px); max-height:calc(100vh - 60px); overflow:auto;
                     display:flex; flex-direction:column; gap:10px; padding:22px; }
  .caract-popover-cab { display:flex; align-items:center; justify-content:space-between; gap:12px; }
  .caract-popover h3 { margin:0; font-size:18px; }
  .caract-popover-cerrar { flex:none; width:32px; height:32px; border:none; background:transparent; color:var(--tinta3);
                            font-size:20px; line-height:1; cursor:pointer; border-radius:var(--r-s); }
  .caract-popover-cerrar:hover { background:var(--rojo-tenue); color:var(--rojo); }
  .caract-popover .ayuda { margin:0 0 4px; font-size:13px; color:var(--tinta3); }
  /* Lista con SU PROPIO scroll (mismo patrón que '.mapeo-popover-lista',
     REGLA COMPONENTES): con varias características, la lista sola ya
     llenaba la ventana y la parte de "añadir nueva" (imagen + texto +
     botones) quedaba fuera de la vista, obligando a bajar por TODA la
     lista para llegar a ella (pedido explícito, 18/8/2026: "no veo lo de
     abajo, mal mal"). Con tope propio, la lista se desplaza sola y lo de
     añadir se ve siempre. */
  .caract-lista { max-height:220px; overflow:auto; border:1px solid var(--linea); border-radius:var(--r-s); padding:0 10px; }
  .caract-item { display:flex; align-items:flex-start; justify-content:space-between; gap:8px; padding:6px 0; border-bottom:1px solid var(--linea); }
  .caract-item:last-child { border-bottom:none; }
  .caract-item label { display:flex; align-items:flex-start; gap:8px; font-size:13px; cursor:pointer; flex:1; min-width:0; }
  .caract-item input { margin-top:3px; flex:none; }
  .caract-borrar { flex:none; width:26px; height:26px; border:none; background:transparent; color:var(--tinta3); font-size:16px; line-height:1; cursor:pointer; border-radius:var(--r-s); }
  .caract-borrar:hover { background:var(--rojo-tenue); color:var(--rojo); }
  .caract-texto { height:auto; min-height:56px; padding:8px 10px; font-size:13px; font-family:inherit; resize:vertical; }
  /* Veredicto de la IA al validar una característica contra el documento:
     ámbar = sugerencia (mejora opcional), rojo = no se pudo confirmar. */
  .caract-veredicto { border-radius:var(--r-s); padding:10px 12px; font-size:12.5px; display:flex; flex-direction:column; gap:8px; }
  .caract-veredicto.sugerencia { background:var(--ambar-tenue); border:1px solid var(--ambar); color:var(--ambar); }
  .caract-veredicto.invalida { background:var(--rojo-tenue); border:1px solid var(--rojo); color:var(--rojo-oscuro); }
  .caract-veredicto-acciones { display:flex; gap:8px; }
  .caract-veredicto-acciones .boton { padding:6px 10px; font-size:12px; }
  /* 'min-height', NO 'height': con altura fija, una lista de líneas larga
     quedaba encajonada con su propio scroll interno en vez de crecer con
     la página — pedido explícito: "crece hacia abajo". Con el mínimo, la
     fila ocupa el hueco normal cuando hay pocas líneas y crece con la
     página cuando hay muchas. */
  /* Ficha de documento con visor (Revisar de Gestión Documental, #44):
     el documento a MÁXIMA pantalla a la izquierda, la ficha a la derecha.
     Patrón de SISTEMA (Regla Componentes): cualquier pantalla futura de
     "papel + ficha" usa estas clases, no se reinventa. */
  /* ALTO DEFINIDO por defecto, no 'min-height' (27/8/2026, bug real: al
     arrastrar el documento SOLO se movía en horizontal, no en vertical).
     Con 'min-height' el cuerpo crecía hasta la altura de la imagen entera,
     así que el marco del visor nunca tenía overflow vertical y el arrastre
     no tenía nada que desplazar hacia arriba/abajo (sí a los lados, porque
     el ancho SÍ estaba acotado al 62%). Con altura definida, el visor y la
     ficha (que ya lleva 'overflow-y:auto') se acotan y desplazan por dentro
     — igual que en pantalla completa, donde el arrastre ya funcionaba en
     los dos ejes. El divisor horizontal sigue pudiendo ALARGARLO (fija un
     'height' en línea que manda sobre éste). En móvil (abajo) vuelve a
     'auto' para que las dos partes se apilen y crezcan con la página. */
  .ficha-doc-cuerpo { display:flex; gap:14px; padding:12px 18px 18px; align-items:stretch; height:calc(100vh - 300px); }
  .ficha-doc-visor { flex:1 1 62%; min-width:0; border:1px solid var(--linea2); border-radius:var(--r); overflow:hidden; background:var(--papel2); position:relative; }
  .ficha-doc-lado { flex:0 0 38%; max-width:520px; min-width:300px; display:flex; flex-direction:column; gap:10px; overflow-y:auto; }
  .ficha-doc-lado .cab-panel { padding:10px 14px; }
  /* EL DIVISOR ARRASTRABLE entre el papel y la ficha (25/8/2026). Barra
     fina que se colorea al acercar el puntero, para que se vea que se
     puede coger sin robar sitio al contenido. Zona de agarre AMPLIADA con
     un ::after invisible: 6 px se ven bien pero se cazan mal, y con el
     dedo directamente no (REGLA WEB). Se retira en móvil, donde las dos
     partes van una debajo de otra y no hay nada que repartir. */
  .divisor-v { flex:0 0 6px; align-self:stretch; cursor:col-resize; border-radius:3px;
               background:var(--linea); position:relative; touch-action:none; }
  .divisor-v::after { content:""; position:absolute; inset:0 -7px; }
  .divisor-v:hover, .divisor-v.arrastrando { background:var(--rojo); }
  @media (pointer:coarse) { .divisor-v { flex-basis:12px; } }
  /* El mismo divisor, tumbado: reparte el ALTO en vez del ancho. Se usa
     bajo el bloque papel+ficha para poder ALARGARLO y ver más documento de
     una vez (25/8/2026). Mismo aspecto y mismo mecanismo — solo cambia la
     medida (ver 'ss.divisorAjustable', opción eje:'y'). */
  .divisor-h { height:6px; margin:0 18px 10px; border-radius:3px; cursor:row-resize;
               background:var(--linea); position:relative; touch-action:none; }
  .divisor-h::after { content:""; position:absolute; inset:-7px 0; }
  .divisor-h:hover, .divisor-h.arrastrando { background:var(--rojo); }
  @media (pointer:coarse) { .divisor-h { height:12px; } }
  /* Campo corregible de la ficha documental: input discreto que parece
     texto hasta que se toca; corregido = borde ámbar (hay una corrección
     humana pisando lo leído, el tooltip enseña qué decía la IA). */
  .campo-edita { height:28px; font-size:13.5px; padding:2px 8px; border:1px solid transparent; border-radius:7px; background:transparent; color:var(--tinta); min-width:0; width:100%; }
  .campo-edita:hover:not(:disabled), .campo-edita:focus { border-color:var(--linea2); background:var(--papel); outline:none; }
  .campo-edita:disabled { opacity:.7; }
  .campo-edita-corto { max-width:150px; }
  .campo-edita-corregido { border-color:var(--ambar); background:var(--papel); }
  /* Campo MÍNIMO que falta para poder confirmar: destacado en el propio
     campo (no solo en la barra de abajo) — el rojo señala dónde escribir. */
  .campo-edita-falta, select.campo-edita-falta { border-color:var(--rojo); background:var(--rojo-tenue); }
  @media (max-width: 900px) {
    /* En columna, el alto definido no aplica: las dos partes se apilan y
       crecen con la página (el arrastre vertical del visor sigue funcionando
       porque en móvil el visor SÍ tiene su propio 'min-height:44dvh'). */
    .ficha-doc-cuerpo { flex-direction:column; height:auto; }
    .ficha-doc-visor { min-height:44dvh; }
    /* El ancho guardado se ANULA a propósito aquí: en columna no significa
       nada, y si se dejara aplicado dejaría la ficha con el ancho de otra
       pantalla (REGLA PREFERENCIAS: una preferencia se anula donde no
       aplica, y se anula a propósito). */
    .ficha-doc-lado { flex:1 1 auto !important; max-width:none; min-width:0; width:auto !important; }
    .divisor-v { display:none; }
  }

  .resolver-cuerpo { display:flex; align-items:stretch; min-height:calc(100vh - 360px); }
  /* El documento ocupa 1/3 del ancho de fábrica, pero es AJUSTABLE a mano
     (arrastrando '.resolver-resize') y el ancho elegido se recuerda por
     navegador (REGLA PREFERENCIAS) — por eso no lleva max-width fijo: el
     límite lo pone el propio arrastre (70% del hueco disponible). */
  /* PEGAJOSO y con la altura de la ventana: el cuerpo crece hacia abajo con
     las líneas (min-height, no height), y sin esto la columna del documento
     crecería también con la imagen — el visor perdía su tope y el
     desplazamiento vertical de la imagen no existía (bug real: se movía a
     los lados pero no arriba/abajo, sin barras). Así el documento acompaña
     al hacer scroll por las líneas y el visor conserva su área interna. */
  /* 'overflow-y:auto; overflow-x:hidden' (20/8/2026, pedido explícito:
     "incómodo ver las bases, quitar barra horizontal, que crezca para
     abajo y moverse con la barra vertical"): antes las bases tenían SU
     PROPIO mini-scroll a altura fija dentro de la columna, con una barra
     horizontal añadida de regalo (un 'overflow-y' sin su 'overflow-x'
     explícito hace que el navegador trate el eje X como 'auto' aunque no
     haga falta — ahí salía la barra horizontal, no de contenido que de
     verdad desbordara). Ahora las bases miden lo que necesiten
     ('height:auto' en '.resolver-doc .resolver-totales' más abajo) y si
     entre la imagen y las bases no cabe todo en el alto de la ventana, es
     la COLUMNA ENTERA la que se desplaza con una única barra vertical,
     normal y corriente — nunca una horizontal. */
  .resolver-doc { flex:0 0 33.333%; min-width:240px; border-right:1px solid var(--linea); background:var(--papel3); display:flex; flex-direction:column;
                   align-self:flex-start; position:sticky; top:0; height:calc(100dvh - 49px); overflow-y:auto; overflow-x:hidden; }
  /* Separador arrastrable entre el documento y las líneas. Zona de agarre
     ancha (14px) para el dedo, aunque la línea visible sea fina. */
  .resolver-resize { flex:none; width:14px; margin:0 -7px; cursor:col-resize; position:relative; z-index:3; touch-action:none; }
  .resolver-resize::after { content:""; position:absolute; top:0; bottom:0; left:6px; width:2px; background:var(--linea2); }
  .resolver-resize:hover::after, .resolver-resize:active::after { background:var(--rojo); }
  /* Aloja el visor COMPARTIDO (visor.js): mismo componente que en el resto
     del producto, sin la cabecera del diálogo (aquí no hay nada que
     cerrar — es parte fija de la pantalla, no una ventana flotante). */
  /* min-height (20/8/2026, pedido explícito: "el visor tengo un alto
     mínimo para ver el documento"): por mucho que se arrastren las bases
     hacia abajo, el documento nunca se queda sin sitio para verse.
     'flex:1' es el reparto POR DEFECTO (el visor toma lo que sobra tras
     las bases); el asa de abajo ('.resolver-lienzo-resize') puede fijar
     un alto EXPLÍCITO en px cuando el usuario quiere ver la imagen más
     grande — ver 'conectarRedimensionLienzo' en comun.js. */
  .resolver-doc-lienzo { flex:1; min-height:180px; position:relative; overflow:hidden; display:flex; align-items:center; justify-content:center; background:var(--fondo); }
  /* Asa para agrandar/encoger el visor de la IMAGEN (20/8/2026, segundo
     pedido explícito: al quitar el asa de las BASES —que ahora crecen
     solas— se perdió la única forma de darle más alto a la imagen. Mismo
     patrón que '.resolver-resize' del ancho: zona de agarre alta para el
     dedo, línea fina visible. */
  .resolver-lienzo-resize { flex:none; height:12px; margin:-6px 0; cursor:row-resize; position:relative; z-index:3; touch-action:none; }
  .resolver-lienzo-resize::after { content:""; position:absolute; left:0; right:0; top:5px; height:2px; background:var(--linea2); }
  .resolver-lienzo-resize:hover::after, .resolver-lienzo-resize:active::after { background:var(--rojo); }
  /* Igual que en la ventana: si hay barra de zoom (imagen), se apila en
     columna en vez de centrar. */
  .resolver-doc-lienzo:has(.visor-img-marco) { flex-direction:column; align-items:stretch; justify-content:flex-start; }
  .resolver-doc-texto { flex:1; margin:0; padding:16px; overflow:auto; white-space:pre-wrap; font-size:12.5px; color:var(--tinta); background:var(--papel); }
  /* Bases/totales DENTRO de la columna de la imagen, como pie bajo el
     visor (20/8/2026, pedido explícito: "poder comparar que la base y el
     pie de la factura son los mismos" sin salir de esa zona). Ya NO lleva
     asa de redimensionado manual ni scroll propio (20/8/2026, segundo
     pedido explícito, ver la nota de '.resolver-doc' más arriba): mide lo
     que su contenido necesite ('height:auto') y si no cabe entero, es la
     columna la que se desplaza entera con una sola barra. */
  .resolver-doc .resolver-totales { flex:none; height:auto; overflow:visible; border-top:1px solid var(--linea); background:var(--papel); padding:10px 14px; gap:6px; }
  /* CON SCROLL PROPIO, con el MISMO alto que la columna de la imagen —
     medido y aplicado por JS ('ajustarAltoDoc' en comun.js), NUNCA un
     'calc(100dvh - ...)' propio en CSS (20/8/2026, fallo real corregido:
     un número mágico aparte no coincidía pixel a pixel con el alto real
     de la columna pegajosa del documento, y se veía un desnivel entre las
     dos — con la MISMA fuente de medida, miden idéntico siempre). Antes
     del primer cálculo, un mínimo razonable para que no colapse. Sin
     esto, la tabla crecía con la página entera y la barra horizontal
     quedaba al final de un documento larguísimo, inalcanzable sin bajar
     del todo — pedido explícito: "la barra la tengo abajo". 'cursor:grab'
     es la pista de que además se puede ARRASTRAR con el ratón para
     moverse a los lados (ver 'activarArrastreLineas') sin rozar ninguna
     barra de desplazamiento. */
  .resolver-lineas { flex:1 1 auto; min-width:280px; min-height:240px; overflow:auto; cursor:grab; }
  .resolver-lineas.arrastrando { cursor:grabbing; user-select:none; }
  /* Botón para mostrar/ocultar el documento: SIEMPRE pegado al borde
     izquierdo de la tabla de líneas (a la altura de su cabecera), tanto si
     el documento está visible como oculto — pedido explícito. */
  .resolver-doc-botones { display:flex; flex-direction:column; gap:8px; flex:none; align-self:flex-start; margin:10px 8px 0 0; }
  /* "Detalle del documento": lo leído por la IA, en lenguaje llano, sin
     nombres internos de campo — para comparar contra la foto (13/8/2026). */
  .detalle-dato { display:flex; gap:8px; padding:6px 0; border-bottom:1px solid var(--linea2); font-size:13px; }
  .detalle-etq { flex:0 0 220px; color:var(--tinta2); }
  .detalle-val { flex:1; color:var(--tinta); font-weight:600; word-break:break-word; }
  .detalle-general { padding:8px 0 12px; font-size:13.5px; font-weight:600; color:var(--tinta2); }
  /* Insignia de confianza: cuánto se fía la IA de ESE dato — nunca cambia
     el valor, solo avisa qué comparar contra la foto. */
  .confianza-badge { display:inline-block; padding:1px 7px; border-radius:999px; font-size:11px; font-weight:700; vertical-align:middle; }
  .confianza-alta { background:var(--verde-tenue); color:var(--verde); }
  .confianza-media { background:var(--ambar-tenue); color:var(--ambar); }
  .confianza-baja { background:var(--rojo-tenue); color:var(--rojo-oscuro); }
  /* Columnas "#" (orden de la línea tal como se lee en el documento) y
     "Confianza": lo más estrechas posible (pedido explícito), solo cabe
     el número/porcentaje. */
  .tabla-linea-scan .col-orden,
  .tabla-linea-scan .col-confianza { width:1%; white-space:nowrap; text-align:center; padding-left:6px; padding-right:6px; }
  /* REGLA 9: toda cabecera de columna ordena — cursor de acción e
     indicador ▲/▼ de la columna y sentido activos (activarOrdenTabla). */
  table.tabla th.ordenable { cursor:pointer; user-select:none; }
  table.tabla th.orden-asc::after { content:' ▲'; font-size:9px; opacity:.8; }
  table.tabla th.orden-desc::after { content:' ▼'; font-size:9px; opacity:.8; }
  .confianza-general-cab { display:inline-flex; align-items:center; vertical-align:middle; }
  /* La confianza general de la cabecera del Resolver es el dato más
     importante del documento entero (pedido explícito, 19/8/2026: "un poco
     más grande") — más grande que la insignia de un dato suelto, sin tocar
     ésta última en el resto de la app (REGLA COMPONENTES: se extiende con
     una regla más específica, no se cambia la base). */
  .confianza-general-cab .confianza-badge { font-size:15px; padding:3px 12px; }
  @media (max-width:600px) { .detalle-dato { flex-direction:column; gap:2px; } .detalle-etq { flex:none; } }
  /* Documento oculto (REGLA PREFERENCIAS): las líneas se quedan a todo el
     ancho. El documento sigue montado (no se pierde el recorte/zoom al
     reabrirlo), solo deja de verse. */
  .resolver-cuerpo.doc-oculto .resolver-doc, .resolver-cuerpo.doc-oculto .resolver-resize { display:none; }
  /* Selector de impuesto de línea: verde si coincide con el de compra del
     artículo mapeado en Ágora, ámbar si se tomó de Ágora porque el
     documento no traía ninguno. */
  .campo-impuesto { height:30px; padding:0 6px; border:1px solid var(--linea2); border-radius:var(--r-s); background:var(--papel); color:var(--tinta); font-size:12.5px; }
  /* Cantidad/Precio/Descuento/Código de línea editables a mano (BD-005/006,
     pedido explícito 21/8/2026: "está claro que afecta al cálculo global").
     Mismo tamaño y lenguaje que '.campo-impuesto' — un solo estilo de campo
     de línea en toda la tabla (REGLA COMPONENTES DE INTERFAZ). Números con
     su propio 'type=number': el navegador ya impide letras, no hace falta
     validarlo aquí — la validación DE VERDAD (rango, negativos) la hace
     siempre el servidor (Regla 4.1). */
  .campo-linea { width:100%; height:30px; padding:0 6px; border:1px solid var(--linea2); border-radius:var(--r-s);
                 background:var(--papel); color:var(--tinta); font-size:12.5px; font-family:inherit; }
  .campo-linea:focus { border-color:var(--rojo); outline:none; }
  input.campo-linea[type=number] { text-align:right; }
  /* Sin flechitas de subir/bajar en NINGÚN campo numérico editable de
     Resolver (pedido explícito, 21/8/2026): con step tan fino como
     0,00001 la flechita no sirve para nada real y solo resta ancho útil
     al campo — se escribe el número a mano, como el resto del producto. */
  input.campo-linea[type=number]::-webkit-outer-spin-button,
  input.campo-linea[type=number]::-webkit-inner-spin-button { -webkit-appearance:none; margin:0; }
  input.campo-linea[type=number] { -moz-appearance:textfield; }
  .campo-linea-texto { text-align:left; min-width:80px; }
  /* Avisos bajo el campo (cantidad/precio pactado o pedido): antes eran
     '<br>' sueltos, ahora se apilan en columna dentro de la celda.
     FALLO REAL corregido (21/8/2026): 'display:flex' puesto en el propio
     '<td>' rompía el cálculo de alto de FILA de toda la tabla — un '<td>'
     con 'display:flex' deja de participar en el algoritmo normal de
     'table-layout', así que esa celda se quedaba con su alto propio
     (53 px) mientras el resto de la fila (celdas normales, que SÍ siguen
     el alto de fila) se estiraban a 198 px copiando el alto de OTRA fila
     de la tabla — filas completamente descuadradas, y el puntito rojo de
     "localizar en la imagen" (top:2px;right:2px de la celda) apareciendo
     lejísimos del campo al que apunta. El flex va en un DIV DE DENTRO,
     nunca en la celda — el '<td>' se queda como celda normal siempre. */
  .celda-columna { display:flex; flex-direction:column; gap:2px; align-items:stretch; }
  .campo-linea-aviso { white-space:nowrap; align-self:flex-start; }
  .campo-impuesto-ok { border-color:var(--verde); background:var(--verde-tenue); color:var(--verde); }
  .campo-impuesto-agora { border-color:var(--ambar); background:var(--ambar-tenue); color:var(--ambar); }
  /* El documento trae un tipo que NO existe entre los activos de Ágora
     (ej. 21 % en una instalación solo con IGIC) — antes se perdía como
     "—" en silencio; ahora se ve el dato real con aviso rojo. */
  .campo-impuesto-sindato { border-color:var(--rojo); background:var(--rojo-tenue); color:var(--rojo-oscuro); }
  @media (max-width:900px) {
    .resolver-cuerpo { flex-direction:column; height:auto; }
    /* 'height:auto' a propósito (20/8/2026): con las bases ahora DENTRO de
       esta columna, fijar 44vh las recortaría — el alto fijo pasa a la
       IMAGEN sola ('.resolver-doc-lienzo' abajo) y la columna crece con
       ella + las bases debajo, sin clip. */
    .resolver-doc { flex-basis:auto !important; border-right:none; border-bottom:1px solid var(--linea); height:auto;
                     position:static; align-self:stretch; overflow:visible; }
    .resolver-doc-lienzo { flex:none; height:44vh; min-height:0; }
    .resolver-lienzo-resize, .resolver-resize { display:none; }
    .resolver-lineas { height:60vh; }

    /* Los dos botones del documento (mostrar/ocultar y detalle) son una
       TIRA VERTICAL entre el documento y las líneas cuando el cuerpo va
       en fila — correcto en escritorio. Al apilarse en columna quedaban
       como una columna suelta pegada a la izquierda con medio ancho
       vacío al lado: parecía roto (visto en un móvil real). Aquí pasan a
       barra HORIZONTAL, que es lo que corresponde a un cuerpo apilado. */
    .resolver-doc-botones { flex-direction:row; align-self:stretch; justify-content:flex-end;
                             margin:0; padding:6px 10px; border-bottom:1px solid var(--linea); }
  }

  /* Inspector de campos (Ctrl+clic, SOLO en PGM — ver app.js): mismo
     lenguaje visual que '.mapeo-popover' (REGLA COMPONENTES), pero más
     compacto y de solo lectura — no lleva buscador ni botón de cerrar,
     se cierra clicando fuera o con Escape. */
  .inspector-popover { position:fixed; z-index:80; width:300px; max-width:calc(100vw - 20px); background:var(--papel);
                        border:1px solid var(--ambar); border-radius:var(--r-g); box-shadow:0 12px 32px rgba(16,24,40,.22), 0 2px 8px rgba(16,24,40,.12);
                        padding:12px; font-size:12.5px; animation:mapeoPopoverEntra .12s ease-out; }
  .inspector-titulo { font-weight:700; color:var(--ambar); margin-bottom:6px; word-break:break-all; }
  .inspector-fila { padding:2px 0; color:var(--tinta2); }

  /* Aviso INFORMATIVO fijo dentro de una pantalla (nace en la Papelera,
     23/8/2026 — "esto se borra a los 30 días"): franja tenue ámbar, texto
     corto, sin botones. Componente del sistema (REGLA COMPONENTES DE
     INTERFAZ): cualquier pantalla que necesite avisar de una condición
     permanente usa esta clase, nunca un estilo en línea propio. */
  .aviso-suave { padding:9px 14px; border:1px solid var(--linea); border-left:4px solid var(--ambar);
                 border-radius:var(--r); background:var(--ambar-tenue); color:var(--tinta2); font-size:13px; }

  /* Mensajes emergentes: aviso flotante, no bloquea, se cierra solo pasados
     unos segundos o al pulsarlo (REGLA MENSAJES: nada de MessageBox/alert).
     Réplica exacta del mismo patrón del Supervisor (REGLA COMPONENTES DE
     INTERFAZ) — un solo diseño de "aviso flotante" en todo el aplicativo. */
  .pila-mensajes { position:fixed; right:20px; bottom:20px; z-index:200; display:flex; flex-direction:column; gap:8px;
                   max-width:min(360px, calc(100vw - 40px)); }
  .mensaje-emergente { background:var(--papel); border:1px solid var(--linea); border-left:4px solid var(--tinta3);
                        border-radius:var(--r); box-shadow:var(--sombra-menu); padding:10px 14px; font-size:13px;
                        color:var(--tinta); cursor:pointer; opacity:0; transform:translateX(12px);
                        transition:opacity .2s ease, transform .2s ease; }
  .mensaje-emergente.visible { opacity:1; transform:translateX(0); }
  .mensaje-emergente.info       { border-left-color:var(--azul); }
  .mensaje-emergente.bien       { border-left-color:var(--verde); }
  .mensaje-emergente.alerta     { border-left-color:var(--ambar); }
  .mensaje-emergente.error      { border-left-color:var(--rojo); }
  .mensaje-emergente.importante { border-left-color:var(--morado); font-weight:600; }
  @media (prefers-reduced-motion:reduce) {
    .mensaje-emergente { transition:opacity .25s ease; transform:none; }
  }
  @media (max-width:600px) {
    .pila-mensajes { right:10px; left:10px; bottom:10px; max-width:none; }
  }
  .inspector-fila b { color:var(--tinta); font-weight:600; margin-right:4px; }
  .inspector-valor { margin-top:4px; padding-top:6px; border-top:1px dashed var(--linea2); word-break:break-word; }
  /* ===================================================================
     TODO LO PULSABLE, A 40x40 — SOLO EN TÁCTIL
     ---------------------------------------------------------------------
     VA AL FINAL DE LA HOJA A PROPÓSITO. Una @media no suma especificidad:
     a igual peso gana la regla escrita más abajo, y varias de las bases que
     hay que corregir ('.campo-tipo', '.cab-item .boton-icono', '.visor-x',
     '.mapeo-popover-modo-btn', '.campo-impuesto') están definidas después
     del bloque '(pointer:coarse)' de más arriba. Escrito allí, esto no se
     aplicaba; escrito aquí, sí. Si algún día se añade una regla base nueva
     por debajo, este bloque tiene que seguir siendo el último.

     Qué corrige, medido con la matriz de la REGLA WEB (390x844, 844x390,
     768x1024, 1024x768) en Revisión, Resolver y visor:
       · botones de acción de cada fila ............ 36x36
       · botones del visor (zoom, girar, ajustar) .. 30x30
       · botones de la cabecera del Resolver ....... 32x25
       · tipo / almacén / impuesto (desplegables) .. 28-30 de alto
       · enlace al fichero en la lista ............. 94x16
       · pastilla "lo que falta" del Resolver ...... 322x20
       · copiar Id del panel lateral ............... 22x22
     Con el ratón 36 px se aciertan; con el dedo, no — y son precisamente
     los que más se usan. En escritorio no cambia ni un píxel.
     =================================================================== */
  @media (pointer:coarse) {
    .boton { min-height:40px; }
    .boton-icono { min-width:40px; min-height:40px; padding:7px 9px; }
    .cab-item .boton-icono { min-width:40px; min-height:40px; padding:4px 8px; }
    .adj button, .mapeo-popover-modo-btn, .ficha-copiar,
    .visor-x, .visor-pdf-boton,
    /* Cerrar de los popover flotantes (mapeo y características): 32x32 de
       fábrica, y es el botón con el que se sale de una capa que tapa media
       pantalla — fallarlo con el dedo es de lo más molesto que hay. */
    .mapeo-popover-cerrar, .caract-popover-cerrar { min-width:40px; min-height:40px; }
    /* El hueco reservado para ese botón crece con él: 44 px bastaban para
       32+10, con 40+10 el contenido se le metía debajo. */
    .mapeo-popover { padding-right:56px; }
    .visor-pdf-boton { width:40px; height:40px; }
    .visor-cab .boton { height:40px; }
    .campo-tipo, .cab-item .campo-tipo, .campo-impuesto, .campo-linea { height:40px; }
    /* Se le da cuerpo al enlace SIN cambiar cómo se ve (sigue truncando). */
    .col-truncar { display:inline-flex; align-items:center; min-height:40px; }
    /* Toda pastilla que además sea BOTÓN: filtros de Bandeja, selector de
       tipo al subir, "lo que falta" del Resolver. Como pastilla mide 26 px;
       como botón tiene que medir 40. */
    .pastilla-boton { min-height:40px; }
    /* Filas-contador de Bandeja (Pendientes / Procesados / Con error): son
       botones de filtro de verdad, no rótulos — 30 px no se aciertan. */
    .menu-rejilla-mini .menu-item { min-height:40px; align-items:center; }
    /* Casillas de selección de la lista de envío: 16x16 de fábrica. Se
       agranda la CAJA, no el tic, con 'transform' — así el dedo tiene sus
       40 px reales sin descuadrar la fila ni cambiar el diseño. */
    /* Casillas y radios: 13x13 de fábrica en cualquier navegador. Se agranda
       la marca a 20 y se le da un margen simétrico de 10, que suma los 40 px
       de zona pulsable REAL sin descuadrar nada — el margen es tan tocable
       como la casilla. Vale para toda la aplicación (listas de envío,
       filtros, listas de permisos), no solo para las tablas.
       'flex:0 0 auto' es obligatorio: dentro de una celda flex la casilla se
       encogía de 20 a 13 px al repartir el hueco. */
    input[type=checkbox], input[type=radio],
    /* Nombrados uno a uno porque llevan medida propia con más peso que un
       'input[type=…]' suelto: una @media no suma especificidad. */
    table.tabla input[type="checkbox"], .login-recordar input { width:20px; height:20px; margin:10px; flex:0 0 auto; }
    select, input[type=text], input[type=search], input[type=number],
    input[type=date], input[type=password] { min-height:40px; }
    /* Selectores con altura propia declarada con clase: una regla de clase
       gana al 'select' de arriba (más específica), hay que nombrarlos. */
    .filtro-dias select { min-height:40px; }
    /* 10 px no se leen en un móvil a medio metro (REGLA WEB): son las
       etiquetas de la cabecera del Resolver. A 11 px siguen igual de
       compactas y ya se leen. */
    .cab-etq { font-size:11px; }
  }

  /* ===================================================================
     REMATES DE MÓVIL QUE TIENEN QUE IR LOS ÚLTIMOS
     ---------------------------------------------------------------------
     MISMO MOTIVO que el bloque '(pointer:coarse)' de aquí arriba, y por eso
     van juntos al final: una '@media' NO suma especificidad, así que a igual
     peso gana la regla escrita más abajo. Estas dos correcciones pisan
     reglas base declaradas MUY abajo en la hoja (línea ~1200 y ~1780);
     escritas dentro del bloque de fichas (~1400) no se aplicaban — se
     comprobó midiendo en el navegador, no suponiendo.
     Si algún día se añade una regla base por debajo de aquí, este bloque
     tiene que seguir siendo el último.
     =================================================================== */
  @media (max-width:767px) {

    /* El aviso de "lo que falta" ocupa varias líneas en vez de cortarse.
       Medido: le faltaban 70 px y se perdía el final de la frase — se leía
       "...no coincide con el total ca". En ESCRITORIO el recorte es
       correcto (mantiene la barra en una línea y evita que empuje al botón
       Confirmar fuera de la vista), pero allí sobra ancho; en un móvil ese
       mismo recorte se come media frase, que es justo la parte que explica
       qué ha pasado. */
    .resolver-pendiente .pastilla { white-space:normal; overflow:visible; text-overflow:clip; }

    /* EL "GRUPO ÁGORA" NO EXISTE EN UNA FICHA.
       En la tabla, un fondo gris y un corchete verde de dos líneas
       verticales marcan qué columnas vienen de Ágora: agrupar columnas
       vecinas es información útil cuando hay columnas. En la ficha no las
       hay — cada campo lleva ya su propia etiqueta — así que el fondo gris
       cae sobre campos sueltos y las dos líneas verdes quedan FLOTANDO en
       mitad de la tarjeta, con pinta de fallo de pintado (visto en un móvil
       real, 22/8/2026). Se retira el grupo y se deja el campo limpio. */
    .tabla-linea-scan td:nth-of-type(2),
    .tabla-linea-scan td:nth-of-type(3) { border-left:none; border-right:none; }
    .tabla-linea-scan td.col-agora { background:transparent; }
  }

/* --- Miniatura de la bandeja de subida (22/8/2026) -------------------------
   Estaba escrito con 'style=' en línea sobre el propio botón, reescribiendo a
   mano el tamaño, el relleno y el radio de '.boton'. Así, cualquier cambio del
   sistema de botones se lo saltaba. Al sistema (REGLA COMPONENTES DE INTERFAZ). */
.miniatura-subir { position: relative; }
.miniatura-subir img {
  width: 80px; height: 80px; object-fit: cover;
  border-radius: 6px; border: 1px solid var(--borde);
}
.miniatura-quitar {
  position: absolute; top: -8px; right: -8px;
  width: 22px; height: 22px; min-width: 0; min-height: 0;
  padding: 0; border-radius: 50%;
}
