/* ==========================================================================
   TEMA ISES — capa de presentación
   ==========================================================================
   Mismo molde que css/liquid-glass.css: hoja ADITIVA, cargada al final, con
   TODO colgando de `body.theme-ises`. Quitar esa clase del <body> devuelve la
   app al diseño base de styles.css sin tocar ningún otro archivo.

   Reglas duras de esta hoja (heredadas del handoff anterior y del diseño):

   1. Es un cambio de PRESENTACIÓN. No se renombra ni se elimina ninguna clase
      ni ningún id: js/app.js toca 252 ids distintos y escribe ~180 clases
      desde template literals. Aquí solo se escriben selectores.
   2. CERO `box-shadow`. La profundidad del diseño es borde de 1px +
      backdrop-filter, nunca sombra. Es su decisión visual más fuerte.
   3. `backdrop-filter` en las superficies GRANDES: barra superior, dock,
      paneles, tarjetas, modales, cajón y panel del asistente. Es lo que hace
      el diseño y sin ello el fondo se ve nítido a través del vidrio.
      Sigue PROHIBIDO en elementos que se repiten decenas o cientos de veces
      —filas de tabla, insignias, botones, chips—: el fondo es un canvas
      animado y cada superficie con desenfoque obliga al compositor a releer
      el backdrop en CADA frame, no solo al hacer scroll. Si en un equipo
      modesto va justo, el modo "Estático" del selector de fondo lo resuelve
      sin tocar código.
   4. Ni un selector `.pdf-*`. La vista previa de la cotización es un espejo
      del documento que genera whatsapp-gateway/src/pdf.ts, que no lee este
      CSS. Si el tema la repinta, el usuario ve un documento y recibe otro.
   5. Todo input `min-width:0; width:100%` y todo grid con `minmax(0,1fr)`.
   6. Presupuesto de `!important`: 6. Eran 4; el interruptor de movimiento
      reducido (bloque 18) gasta 2. Un interruptor que tiene que ganarle a
      animaciones declaradas en cinco hojas distintas no puede hacerlo por
      especificidad sin dejar de ser un interruptor.

   Bloques:
     1  Tokens
     2  Fondos animados
     3  Cascarón
     4  Dock
     5  Barra superior, pastillas y selector de fondo
     6  Detalles globales
     7  Primitivas compartidas
     8  Cotizaciones guardadas
     9  Métricas
    10  Campos de formulario
    11  Productos y catálogo
    12  Cajón lateral de alta
    13  Catálogo Syscom
    14  Nueva cotización
    15  Login
    16  Modales, toasts, asistente, leads, usuarios, diseñador
    17  Responsive
    18  prefers-reduced-motion
   ========================================================================== */


/* ==========================================================================
   1. TOKENS — la única definición del lenguaje visual ISES
   ==========================================================================
   Igual que liquid-glass.css: NO se abre un `:root` propio. Se repintan los
   tokens que css/styles.css ya define en su `:root` (línea 1) y se añaden los
   `--i-*` que este tema necesita. Quitar .theme-ises del <body> devuelve
   todos los valores originales de golpe.

   La decisión de diseño más fuerte del original: CERO sombras. Por eso los
   tres tokens de sombra se neutralizan aquí en vez de perseguir cada
   `box-shadow` suelto por la hoja base.
   ========================================================================== */

body.theme-ises {
  /* ---------- Superficies ---------- */
  --canvas:     #000000;                  /* era #0a0a0c */
  --rail:       rgba(10, 10, 12, 0.72);   /* era #0c0c0f — el dock es vidrio */
  --surface:    rgba(10, 10, 12, 0.66);   /* era #131316 — la tarjeta estándar */
  --surface-2:  #161618;                  /* era #1a1a1f — campos y divisores */
  --surface-3:  #1a1a1c;                  /* era #1c1c22 — chips neutros */
  --seg-active: #1d1d20;                  /* era #242430 */

  /* ---------- Líneas ----------
     Todas salen de DOS variables, y por eso el mismo mecanismo sirve a dos
     funciones que parecían distintas:

       --linea-rgb   de qué color es una línea. En oscuro, blanco translúcido;
                     en claro, negro translúcido. Sobre blanco un borde blanco
                     no existe, así que el tema claro no podría heredarlas.
       --edge        0–100, lo mueve el slider "Definición de bordes" del
                     configurador. Es un NÚMERO pelado a propósito: así el
                     tema decide con qué curva se traduce a opacidad.

     El resto de la escala son múltiplos de la alfa base, no valores sueltos:
     mover el slider mantiene las proporciones del diseño original en vez de
     aplanarlas. Con --edge en su valor por omisión (50) sale exactamente la
     paleta de antes, así que nadie ve un cambio hasta que toca el control. */
  --edge:        50;
  --linea-rgb:   255 255 255;
  /* El velo: los rellenos de hover y los biseles de vidrio, que en la hoja
     estaban escritos veinte veces como blanco translucido. Sobre blanco un
     velo blanco no existe y el hover se queda SIN respuesta visible, que es
     una regresion de uso, no de estetica. */
  --velo-rgb:    255 255 255;
  --i-dock-sombra: 0 12px 32px rgba(0, 0, 0, 0.55);
  /* El HUECO: una superficie hundida respecto de la que la contiene (la
     columna de filtros del selector, su barra superior). Translucida a
     proposito, para que el desenfoque siga viendose a traves. En oscuro se
     hunde ennegreciendo; en claro tambien es negro, pero al 4 %: al 30 %
     serian pozos negros sobre papel. */
  --hueco-rgb:     0 0 0;
  --hueco-a:       0.30;
  --hueco-a-suave: 0.20;
  --i-velo-modal:  rgba(4, 4, 6, 0.72);
  --linea-alfa:  calc(0.03 + var(--edge) * 0.001);

  --line:        rgb(var(--linea-rgb) / var(--linea-alfa));                    /* era #26262b */
  --line-soft:   rgb(var(--linea-rgb) / calc(var(--linea-alfa) * 0.875));      /* era #1f1f24 */
  --line-strong: #2a2a2e;                    /* era #2b2b31 — borde de campo */

  /* ---------- Texto ---------- */
  --ink:        #f5f5f7;  /* era #e4e4e8 */
  --ink-strong: #f5f5f7;  /* era #f4f4f5 */
  --muted:      #c7c7cc;  /* era #9a9aa2 */
  --steel:      #6e6e73;  /* era #7a7a82 — etiquetas de formulario */
  --faint:      #86868b;  /* era #6a6a72 — pistas y metadatos */

  /* ---------- Acento ---------- */
  --accent:         #0a84ff;  /* era #6aa6f2 */
  --accent-hover:   #3d9bff;  /* era #8fbdf7 */
  --accent-deep:    #0a84ff;  /* era #2f6bc4 — el diseño no tiene azul oscuro */
  --accent-ink:     #ffffff;  /* era #061020 — texto sobre el botón azul */
  --accent-surface: #0e1a2b;  /* era #0e1520 */
  --accent-line:    #1c3a5c;  /* era #22303e */

  /* ---------- Estados semánticos ---------- */
  --green:          #30d158;  /* era #6ee7a0 */
  --green-surface:  #0f2a18;  /* era rgba(110,231,160,.10) */
  --green-line:     #1b4429;  /* era #24402e */
  --amber:          #ffd60a;  /* era #e6a15a */
  --amber-surface:  #231c05;  /* era rgba(230,161,90,.12) */
  --danger:         #ff453a;  /* era #e0686a */
  --danger-surface: #25100f;  /* era rgba(224,104,106,.12) */
  --danger-line:    #4a1d1a;  /* era #3a2023 */

  /* ---------- Sombras: el diseño no tiene ninguna ---------- */
  --shadow-sm: none;
  --shadow:    none;
  --shadow-lg: none;

  /* ---------- Geometría ---------- */
  --radius:    14px;  /* coincide con la hoja base: la tarjeta estándar */
  --radius-sm: 10px;  /* coincide: campos y botones */
  --radius-lg: 16px;  /* era 18px — el dock */
  /* El foco del diseño es un BORDE azul, no un halo. Son 2px y no 1: con uno
     solo apenas se distinguía del borde normal del campo, que ya es claro, y
     quien navega con el teclado perdía de vista dónde estaba. Sigue sin ser un
     halo —no hay desenfoque ni separación— así que no rompe el lenguaje. */
  --ring: 0 0 0 2px var(--accent);
  --rail-w: 64px;     /* era 68px — el ancho del dock */

  /* ---------- Tipografía ---------- */
  --font-body:    'Elms Sans', 'Sora', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-display: 'Sora', 'Elms Sans', system-ui, -apple-system, sans-serif;
  --font-mono:    'JetBrains Mono', 'IBM Plex Mono', ui-monospace, 'SF Mono', monospace;

  /* ---------- Cifras ----------
     Var APARTE de --font-mono, no un alias. --font-mono también viste SKUs,
     folios y etiquetas, que deben seguir monoespaciados pase lo que pase; lo
     que el configurador cambia es solo cómo se ven los IMPORTES. */
  --font-cifras: var(--font-mono);
  --cifras-num:  normal;   /* pasa a tabular-nums cuando las cifras no son mono */

  /* ---------- Densidad ----------
     Multiplicador de los paddings de las superficies DENSAS (catálogo, tablas,
     listados). No se aplica a la app entera: el resto de los espaciados están
     escritos en px y calibrados a mano contra el dock y el header. */
  --dens: 1;


  /* ==================== TOKENS PROPIOS DEL TEMA ====================
     Todo lo que el diseño usa y la hoja base no tenía nombre para ello. */

  /* Vidrio: la receta que en el original se repite ~30 veces idéntica.
     --vidrio-rgb es el color del cristal: casi negro en oscuro, blanco en
     claro. Sin él, el tema claro tendría paneles negros translúcidos. */
  --vidrio-rgb:        10 10 12;
  --i-glass-bg:        rgb(var(--vidrio-rgb) / 0.66);
  --i-glass-brd:       rgb(var(--linea-rgb) / var(--linea-alfa));
  --i-glass-blur:      blur(20px);
  --i-glass-blur-alto: blur(24px);          /* barra superior y dock */
  --i-header-bg:       rgba(0, 0, 0, 0.62);
  --i-dock-bg:         rgb(var(--vidrio-rgb) / 0.72);
  --i-dock-brd:        rgb(var(--linea-rgb) / calc(var(--linea-alfa) * 1.125));

  /* Escala de bordes translúcidos. Los nombres conservan el alfa ORIGINAL
     (07 = .07) porque están escritos en ~200 sitios de esta hoja; lo que
     cambia es que ahora son proporciones de --linea-alfa, no constantes. */
  --i-brd-07: rgb(var(--linea-rgb) / calc(var(--linea-alfa) * 0.875));
  --i-brd-08: rgb(var(--linea-rgb) / var(--linea-alfa));
  --i-brd-09: rgb(var(--linea-rgb) / calc(var(--linea-alfa) * 1.125));
  --i-brd-10: rgb(var(--linea-rgb) / calc(var(--linea-alfa) * 1.25));
  --i-brd-14: rgb(var(--linea-rgb) / calc(var(--linea-alfa) * 1.75));
  --i-brd-16: rgb(var(--linea-rgb) / calc(var(--linea-alfa) * 2));

  /* Superficies opacas. */
  --i-sup-1: #141416;  /* botón ghost, hueco de imagen */
  --i-sup-2: #161618;  /* campos, textareas, divisores */
  --i-sup-3: #1a1a1c;  /* insignias */
  --i-sup-4: #1c1c1f;
  --i-sup-5: #1d1d20;  /* segmento activo */
  --i-campo-brd:       #2a2a2e;
  --i-campo-brd-hover: #3a3a3e;

  /* Escala de texto completa del diseño. */
  --i-txt-1: #f5f5f7;
  --i-txt-2: #c7c7cc;
  --i-txt-3: #98989d;
  --i-txt-4: #86868b;
  --i-txt-5: #6e6e73;  /* etiquetas y placeholders */
  --i-txt-6: #48484a;
  --i-txt-7: #3a3a3c;

  /* Piezas que el tema claro NO puede heredar.
     Todas estaban escritas como hex suelto por la hoja. No es limpieza por
     limpieza: cada una es blanco puro o negro puro, y en claro o desaparece
     o se invierte. Tenerlas con nombre es lo que hace que `tema-claro` sea un
     bloque de valores y no una segunda hoja. */
  --i-fondo-base:    #050506;  /* el telón plano detrás de la app */
  /* La tira de pastillas de navegación es una INVERSIÓN: cápsula de un color,
     tinta y blob del contrario, y al pasar el cursor los dos se intercambian.
     Se sostiene sola sin depender de la escala de texto, y por eso el tema
     claro solo tiene que darle la vuelta al par. */
  --i-inverso-bg:    #ffffff;  /* cápsula y tinta sobre el blob */
  --i-inverso-ink:   #000000;  /* tinta en reposo y color del blob */
  --i-logo-bg:       #000000;  /* el círculo del logo: NO se invierte */
  --i-brillo:        #ffffff;  /* el destello del degradado del modo IA */
  --i-brillo-base:   #8e8e93;  /* y su color de reposo */
  --danger-hover:    #f0918f;  /* rojo de hover: aclara en oscuro, oscurece en claro */

  /* Acentos secundarios. */
  --i-azul-claro:    #64a9ff;  /* precios en la tarjeta de producto */
  --i-azul-line-2:   #1b3350;
  --i-verde-sup-2:   #0a1a0f;
  --i-verde-line-2:  #1b3a25;
  --i-rojo-sup-2:    #1c0f0e;
  --i-rojo-line-2:   #3d1815;
  --i-amarillo-line: #4a3b08;
  --i-marca:         #1f0393;  /* el azul de marca del fondo */

  /* Alturas de control. */
  --i-h-campo:       34px;
  --i-h-campo-barra: 32px;
  --i-h-btn-sm:      26px;
  --i-h-btn:         32px;
  --i-h-badge:       22px;
  --i-h-header:      76px;

  /* Radios que no están en la hoja base. */
  --i-r-2: 2px;
  --i-r-5: 5px;
  --i-r-6: 6px;   /* insignias */
  --i-r-7: 7px;
  --i-r-9: 9px;   /* botones pequeños y segmentos */
  --i-r-11: 11px; /* cápsula del control segmentado */
  --i-r-pill: 9999px;

  /* Curva del dock. */
  --i-ease-dock: cubic-bezier(0.32, 0.72, 0, 1);
}


/* ==========================================================================
   1b. EXCLUSIÓN — vista previa del PDF
   ==========================================================================
   `.pdf-document` es un espejo del documento que generan jsPDF (final de
   js/app.js) y el servidor (whatsapp-gateway/src/pdf.ts). Ninguno de los dos
   lee este CSS: si el tema repinta la vista previa, el usuario ve un
   documento y recibe otro.

   Por eso aquí se devuelven los valores originales de styles.css. Es un solo
   bloque y no obliga a tocar app.js. Regla dura de esta hoja: ni un selector
   `.pdf-*` fuera de aquí.
   ========================================================================== */

body.theme-ises .pdf-document {
  --canvas: #0a0a0c;
  --rail: #0c0c0f;
  --surface: #131316;
  --surface-2: #1a1a1f;
  --surface-3: #1c1c22;
  --seg-active: #242430;
  --line: #26262b;
  --line-soft: #1f1f24;
  --line-strong: #2b2b31;
  --ink: #e4e4e8;
  --ink-strong: #f4f4f5;
  --muted: #9a9aa2;
  --steel: #7a7a82;
  --faint: #6a6a72;
  --accent: #6aa6f2;
  --accent-hover: #8fbdf7;
  --accent-deep: #2f6bc4;
  --accent-ink: #061020;
  --accent-surface: #0e1520;
  --accent-line: #22303e;
  --green: #6ee7a0;
  --green-surface: rgba(110, 231, 160, 0.10);
  --green-line: #24402e;
  --amber: #e6a15a;
  --amber-surface: rgba(230, 161, 90, 0.12);
  --danger: #e0686a;
  --danger-surface: rgba(224, 104, 106, 0.12);
  --danger-line: #3a2023;
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.35);
  --shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
  --shadow-lg: 0 24px 60px rgba(0, 0, 0, 0.55);
  --radius: 14px;
  --radius-sm: 10px;
  --radius-lg: 18px;
  --ring: 0 0 0 3px rgba(106, 166, 242, 0.30);
  --font-body: 'IBM Plex Sans', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-display: 'IBM Plex Sans', system-ui, -apple-system, sans-serif;
  --font-mono: 'IBM Plex Mono', 'SF Mono', ui-monospace, 'Courier New', monospace;

  /* Los tokens que añade el configurador vuelven aquí a su valor neutro. Es la
     misma razón que todo lo de arriba: el documento no puede cambiar porque
     alguien eligió cifras tabulares o bordes marcados. Un importe con otro
     grosor en pantalla y otro en el PDF es exactamente el desfase que este
     bloque existe para impedir. */
  --edge: 50;
  --linea-rgb: 255 255 255;
  --velo-rgb: 255 255 255;
  --vidrio-rgb: 10 10 12;
  --font-cifras: var(--font-mono);
  --cifras-num: normal;
  --dens: 1;
}


/* ==========================================================================
   1c. TEMA CLARO
   ==========================================================================
   Un bloque de VALORES, no una segunda hoja: no se escribe ni un selector de
   componente aquí. Eso es posible porque la app está tokenizada de verdad
   (~2 300 usos de `var(--)`), así que gastos, facturas, notas y el asistente
   voltean sin tocarlos.

   No es el tema oscuro invertido. Dos cosas se rehacen de raíz:

   1. LA JERARQUÍA SE DA VUELTA. En oscuro el lienzo es negro y cada superficie
      sube hacia el gris; en claro el lienzo es gris y las tarjetas suben hacia
      el blanco. Invertir los valores uno a uno habría dejado tarjetas grises
      sobre papel blanco: elevación al revés.
   2. LOS COLORES SEMÁNTICOS SE RECALCULAN, no se aclaran. El verde #30d158 da
      1.9:1 sobre blanco y el ámbar #ffd60a es sencillamente invisible. Aquí
      son las variantes oscuras de la misma familia, todas por encima de 4.5:1
      contra su superficie.

   Y una regla del tema oscuro se rompe a propósito: la número 2, «cero
   box-shadow». Sobre negro, un borde de 1px al 8 % separa perfectamente dos
   superficies. Sobre blanco no separa nada, y sin sombra la app queda plana.
   Por eso aquí los tres tokens de sombra vuelven a tener valor real — que es
   justo lo que reactiva las sombras que styles.css ya tenía escritas, sin
   añadir un solo selector.
   ========================================================================== */

body.theme-ises.tema-claro {
  /* ---------- Superficies: el lienzo es gris, las tarjetas son blancas ------ */
  --canvas:     #ececee;
  --rail:       rgb(255 255 255 / 0.72);
  --surface:    rgb(255 255 255 / 0.72);
  --surface-2:  #e8e8eb;   /* campos y divisores: hunde respecto de la tarjeta */
  --surface-3:  #e2e2e5;   /* chips neutros */
  --seg-active: #ffffff;   /* el segmento activo sube a blanco, no baja a gris */

  /* ---------- Líneas: negro translúcido, y algo más marcado ----------------
     La misma curva del slider, pero desplazada: una línea negra al 8 % sobre
     blanco se lee bastante más débil que una blanca al 8 % sobre negro. Con
     --edge en 50 sale 0.12, que es donde el borde vuelve a separar. */
  --linea-rgb:   0 0 0;
  --linea-alfa:  calc(0.05 + var(--edge) * 0.0014);
  --velo-rgb:    0 0 0;
  --i-dock-sombra: 0 8px 24px rgb(0 0 0 / 0.12);
  --hueco-rgb:     0 0 0;
  --hueco-a:       0.045;
  --hueco-a-suave: 0.028;
  --i-velo-modal:  rgb(28 28 30 / 0.34);
  --line-strong: #c6c6c8;

  /* ---------- Texto ---------- */
  --ink:        #1c1c1e;
  --ink-strong: #000000;
  --muted:      #3a3a3c;
  --steel:      #66666c;
  --faint:      #66666c;

  /* ---------- Acento ----------
     #0a84ff es el azul de MODO OSCURO de la familia; sobre blanco se queda en
     3.6:1 y no alcanza para texto. Este es el de modo claro: 4.6:1. */
  --accent:         #0071e3;
  --accent-hover:   #0058b0;
  --accent-deep:    #0071e3;
  --accent-ink:     #ffffff;
  --accent-surface: #e8f1fd;
  --accent-line:    #b9d7f8;

  /* ---------- Estados semánticos: recalculados, no aclarados ---------- */
  --green:          #1c7c3e;
  --green-surface:  #e6f5ea;
  --green-line:     #b3ddc0;
  --amber:          #8a6100;
  --amber-surface:  #fdf3d9;
  --danger:         #d70015;
  --danger-surface: #fdeaea;
  --danger-line:    #f5bcbc;
  --danger-hover:   #a3000f;   /* en claro el hover OSCURECE; en oscuro aclaraba */

  /* ---------- Sombras: aquí sí ---------- */
  --shadow-sm: 0 1px 2px rgb(0 0 0 / 0.06);
  --shadow:    0 4px 14px rgb(0 0 0 / 0.08);
  --shadow-lg: 0 20px 48px rgb(0 0 0 / 0.14);

  /* ---------- Vidrio: el cristal es blanco ---------- */
  --vidrio-rgb:  255 255 255;
  --i-header-bg: rgb(255 255 255 / 0.72);

  /* ---------- Superficies opacas ---------- */
  --i-sup-1: #f2f2f4;   /* botón ghost, hueco de imagen */
  --i-sup-2: #e8e8eb;   /* campos, textareas, divisores */
  --i-sup-3: #e2e2e5;   /* insignias */
  --i-sup-4: #dcdce0;
  --i-sup-5: #ffffff;   /* segmento activo */
  --i-campo-brd:       #c6c6c8;
  --i-campo-brd-hover: #98989d;

  /* ---------- Escala de texto ----------
     --i-txt-5 viste etiquetas y placeholders, así que es el que marca el piso.
     Y el piso NO se mide contra el blanco: se mide contra --i-sup-2 (#e8e8eb),
     que es el relleno de los campos, o sea la superficie sobre la que de
     verdad se lee un placeholder. Medido contra blanco daba 4.71 y parecía
     correcto; contra el campo se quedaba en 3.9. Con #6b6b71 son 5.2. */
  --i-txt-1: #1c1c1e;
  --i-txt-2: #3a3a3c;
  --i-txt-3: #4e4e52;
  --i-txt-4: #5a5a60;
  --i-txt-5: #66666c;
  --i-txt-6: #86868b;
  --i-txt-7: #a6a6ab;

  /* ---------- Piezas que no se heredan ---------- */
  --i-fondo-base:  #ececee;
  /* La cápsula se vuelve oscura, que es el espejo honesto de lo que hace en
     oscuro: allí es blanco puro sobre un header casi negro, o sea el elemento
     más marcado de la barra. Aquí es lo mismo al revés, no una versión
     apagada. El blob y la tinta la siguen. */
  --i-inverso-bg:  #1c1c1e;
  --i-inverso-ink: #ffffff;
  --i-logo-bg:     #1c1c1e;
  --i-brillo:      #1c1c1e;   /* el destello del modo IA se oscurece */
  --i-brillo-base: #8e8e93;

  /* ---------- Acentos secundarios ---------- */
  --i-azul-claro:    #0062cc;  /* precios: tiene que leerse, no solo verse */
  --i-azul-line-2:   #cfe2f9;
  --i-verde-sup-2:   #eef8f1;
  --i-verde-line-2:  #c3e4cd;
  --i-rojo-sup-2:    #fdf0f0;
  --i-rojo-line-2:   #f7c9c9;
  --i-amarillo-line: #ecd9a0;
}

/* El lienzo de <html> no puede salir de --canvas: la variable vive en el
   <body> y `:has()` mira desde fuera. Se repite el valor a mano, igual que ya
   se hacía con el negro. */
html:has(body.theme-ises.tema-claro) { background: #ececee; }


/* ==========================================================================
   1d. COMBOS TIPOGRÁFICOS
   ==========================================================================
   Tres, y las tres familias ya están auto-hospedadas en css/fuentes.css. No
   hay una cuarta a propósito: el diseño pedía cinco combos con ocho familias
   de Google Fonts, y esta app quitó el enlace a fonts.googleapis.com por una
   razón que sigue siendo válida (hoja render-blocking en un tercero, con DNS
   y TLS antes del primer píxel). Un ajuste de gusto no vale ese precio.

   Así que estos tres combos cuestan CERO bytes nuevos: son recombinaciones de
   lo que el navegador ya descargó.

   `ises` no aparece aquí porque es lo que ya declara el bloque 1: no elegir
   nada tiene que dejar la app exactamente como estaba. */

body.theme-ises[data-tipo="sora"] {
  --font-body:    'Sora', 'Elms Sans', system-ui, -apple-system, sans-serif;
  --font-display: 'Sora', 'Elms Sans', system-ui, -apple-system, sans-serif;
}

body.theme-ises[data-tipo="plex"] {
  --font-body:    'IBM Plex Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-display: 'IBM Plex Sans', system-ui, -apple-system, sans-serif;
}


/* ==========================================================================
   1e. VARIANTES DEL CONFIGURADOR
   ==========================================================================
   Todo lo que el panel de apariencia puede cambiar, como bloques de tokens.
   Ni un selector de componente: si algo de aquí necesitara tocar una clase
   concreta, sería señal de que ese componente todavía no está tokenizado.

   NOTA DE ESPECIFICIDAD, que aquí decide el resultado:
   `body.theme-ises[data-acento="verde"]` y `body.theme-ises.tema-claro` pesan
   lo MISMO (0,2,1), así que entre ellos manda el orden. Por eso las variantes
   del modo claro llevan `.tema-claro` delante —(0,3,1), gana siempre— y las
   del oscuro llevan `:not(.tema-claro)` cuando el valor no sirve en claro.
   Sin eso, elegir verde en modo claro devolvería el verde de modo oscuro.
   ========================================================================== */

/* --- Contraste del fondo (solo en modo oscuro) ---------------------------
   Tres grados de negro. No cambian la jerarquía, solo dónde arranca: en un
   monitor brillante el negro puro cansa, y el gris de Grafito se lee igual
   con menos fatiga. */
body.theme-ises:not(.tema-claro)[data-contraste="carbon"] {
  --canvas:        #111116;
  --vidrio-rgb:    22 22 27;
  --i-fondo-base:  #111116;
  --surface-2:     #1c1c22;
  --surface-3:     #202026;
  --seg-active:    #24242b;
  --i-sup-1:       #1a1a1f;
  --i-sup-2:       #1c1c22;
  --i-sup-3:       #202026;
  --i-sup-4:       #24242b;
  --i-sup-5:       #26262e;
}
html:has(body.theme-ises:not(.tema-claro)[data-contraste="carbon"]) { background: #111116; }

body.theme-ises:not(.tema-claro)[data-contraste="grafito"] {
  --canvas:        #1a1a20;
  --vidrio-rgb:    32 32 39;
  --i-fondo-base:  #1a1a20;
  --surface-2:     #25252d;
  --surface-3:     #2a2a33;
  --seg-active:    #2f2f39;
  --i-sup-1:       #232329;
  --i-sup-2:       #25252d;
  --i-sup-3:       #2a2a33;
  --i-sup-4:       #2f2f39;
  --i-sup-5:       #33333d;
}
html:has(body.theme-ises:not(.tema-claro)[data-contraste="grafito"]) { background: #1a1a20; }

/* --- Color de acento -----------------------------------------------------
   Cada acento son SEIS valores, no uno. Cambiar solo --accent dejaría el
   texto del botón, su panel tenue y su borde con el azul de antes.
   Y cada uno va dos veces: el tono que funciona sobre negro no es el que
   funciona sobre blanco. El verde #30d158 da 1.9:1 sobre papel. */

body.theme-ises[data-acento="verde"] {
  --accent:         #30d158;
  --accent-hover:   #5ce07d;
  --accent-deep:    #30d158;
  --accent-ink:     #04160a;
  --accent-surface: #0f2a18;
  --accent-line:    #1b4429;
  --i-azul-claro:   #5ce07d;
}
body.theme-ises.tema-claro[data-acento="verde"] {
  --accent:         #1c7c3e;
  --accent-hover:   #145c2d;
  --accent-deep:    #1c7c3e;
  --accent-ink:     #ffffff;
  --accent-surface: #e6f5ea;
  --accent-line:    #b3ddc0;
  --i-azul-claro:   #176934;
}

body.theme-ises[data-acento="ambar"] {
  --accent:         #ffd60a;
  --accent-hover:   #ffe45c;
  --accent-deep:    #ffd60a;
  --accent-ink:     #1a1500;
  --accent-surface: #231c05;
  --accent-line:    #4a3b08;
  --i-azul-claro:   #ffe45c;
}
body.theme-ises.tema-claro[data-acento="ambar"] {
  --accent:         #8a6100;
  --accent-hover:   #664700;
  --accent-deep:    #8a6100;
  --accent-ink:     #ffffff;
  --accent-surface: #fdf3d9;
  --accent-line:    #ecd9a0;
  --i-azul-claro:   #7a5600;
}

body.theme-ises[data-acento="violeta"] {
  --accent:         #8b5cf6;
  --accent-hover:   #a883f8;
  --accent-deep:    #8b5cf6;
  --accent-ink:     #ffffff;
  --accent-surface: #1b1233;
  --accent-line:    #3a2a63;
  --i-azul-claro:   #a883f8;
}
body.theme-ises.tema-claro[data-acento="violeta"] {
  --accent:         #6b32d4;
  --accent-hover:   #5325a8;
  --accent-deep:    #6b32d4;
  --accent-ink:     #ffffff;
  --accent-surface: #f0e9fd;
  --accent-line:    #d4c2f7;
  --i-azul-claro:   #5f2cbd;
}

/* --- Esquinas ------------------------------------------------------------
   `suave` es lo que ya declara el bloque 1 y por eso no tiene regla aquí. */
body.theme-ises[data-esquinas="cuadrado"] {
  --radius: 4px;  --radius-sm: 4px;  --radius-lg: 6px;
  --i-r-5: 2px;   --i-r-6: 2px;      --i-r-7: 3px;
  --i-r-9: 4px;   --i-r-11: 5px;
}
body.theme-ises[data-esquinas="redondo"] {
  --radius: 20px; --radius-sm: 14px; --radius-lg: 22px;
  --i-r-5: 8px;   --i-r-6: 9px;      --i-r-7: 10px;
  --i-r-9: 12px;  --i-r-11: 14px;
}

/* --- Densidad ------------------------------------------------------------ */
body.theme-ises[data-densidad="compacta"] { --dens: 0.68; }
body.theme-ises[data-densidad="comoda"]   { --dens: 1.35; }

/* --- Fuente de las cifras ------------------------------------------------
   `tabular` cambia a la fuente de texto pero le exige cifras de ancho fijo:
   sin `tabular-nums` una columna de importes deja de alinearse por la coma,
   que es justo lo que hacía útil la monoespaciada. */
body.theme-ises[data-cifras="tabular"] {
  --font-cifras: var(--font-body);
  --cifras-num:  tabular-nums;
}


/* ==========================================================================
   2. FONDOS ANIMADOS
   ==========================================================================
   Dos fondos WebGL2 mutuamente excluyentes (<grainient-bg> y <meta-balls>),
   más un modo estático sin canvas. Los elige el usuario desde la barra
   superior y la elección se guarda; la lógica está en js/tema-ises.js.

   El <div> contenedor NO es decorativo: los dos custom elements se ponen a sí
   mismos `position:absolute; inset:0` dentro de connectedCallback y pisan
   cualquier `style` inline que traigan escrito. Sin un ancestro fijo, el
   canvas se ancla al bloque contenedor inicial y se va con el scroll.

   Aquí no hay reglas para esconder un fondo u otro: el JS monta el del modo
   activo y desmonta el otro. Ocultarlos con display:none los dejaría con el
   canvas a 1x1 (miden 0 al conectarse) y además gastando GPU sin verse.
   ========================================================================== */

/* El fondo del tema anterior queda inerte. */
body.theme-ises .glass-aurora { display: none !important; }

/* El negro va en <html> para que el rebote del scroll tampoco enseñe el color
   viejo. El <body> se vuelve transparente para dejar ver el canvas. */
html:has(body.theme-ises) { background: #000000; }
body.theme-ises { background: transparent; }

body.theme-ises .ises-fondo {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
  /* Aísla el repintado del canvas del resto del árbol. Sin `size`: el tamaño
     lo dan las inserciones y no queremos que se colapse. */
  contain: layout paint style;
}
body:not(.theme-ises) .ises-fondo { display: none; }

/* Metaballs: capa base sólida, con el campo de bolas encima muy atenuado. */
body.theme-ises.fondo-metaballs .ises-fondo { background: var(--i-fondo-base); }
body.theme-ises.fondo-metaballs meta-balls {
  opacity: 0.22;
  filter: blur(0.4px);
}

/* El vidrio conserva su opacidad en los dos modos: lo que impide que las
   metaballs se vean atravesando los paneles es el desenfoque del bloque 7,
   no subir la opacidad. */

/* Estático: la válvula de escape de rendimiento y el valor por defecto cuando
   no hay WebGL2 o el sistema pide menos movimiento. El contenedor se queda
   vacío, así que basta con darle color. */
body.theme-ises.fondo-estatico .ises-fondo { background: var(--i-fondo-base); }

/* La app va por encima del fondo. Los z-index existentes no se tocan: solo se
   inserta el 0 por debajo de todos ellos. */
body.theme-ises .app-shell,
body.theme-ises .login-screen,
body.theme-ises .auth-loading { position: relative; z-index: 1; }

/* Estas dos pantallas tienen fondo opaco en styles.css y taparían el canvas. */
body.theme-ises .auth-loading { background: rgba(0, 0, 0, 0.6); }
body.theme-ises .login-screen { background: transparent; }


/* ==========================================================================
   3. CASCARÓN
   ==========================================================================
   El diseño separa el dock del borde de la pantalla y lo deja flotando sobre
   el fondo. La hoja base pega el rail al borde con un `border-right`, así que
   aquí se cambia a un shell con aire alrededor.
   ========================================================================== */

/* El dock sale del flujo y flota sobre el contenido. Así la barra superior
   corre de borde a borde —como en el diseño, donde el chip de estado empieza
   pegado a la izquierda de la ventana, por encima del dock— en vez de
   arrancar después de una columna de 64px. */
body.theme-ises .app-shell {
  gap: 0;
  padding: 0;
}

body.theme-ises .main {
  min-width: 0;
  width: 100%;
}

/* El hueco de la izquierda lo deja el dock, que flota: 14 de margen + 64 de
   ancho + 14 de aire. Se repite el MISMO valor a la derecha para que el
   bloque de contenido quede centrado respecto a la ventana; con 30px a la
   derecha quedaba corrido hacia ese lado. */
body.theme-ises .content {
  padding: 24px 92px 64px;
  position: relative;
  z-index: 1;
  /* Reserva el hueco de la barra de desplazamiento a los DOS lados: sin esto
     los 8px de la barra se comen solo el margen derecho y el bloque queda
     descentrado, y además el contenido salta cuando aparece o desaparece. */
  scrollbar-gutter: stable both-edges;
}

/* Ancho cómodo e igual en todas las vistas. La hoja base le da a Nueva
   cotización un máximo propio más estrecho (1040px, con especificidad de id),
   así que hay que igualarlo aquí. */
body.theme-ises .section {
  max-width: 1500px;
  margin-inline: auto;
}
body.theme-ises #sectionNueva { max-width: 1500px; }

body.theme-ises ::-webkit-scrollbar { width: 8px; height: 8px; }
body.theme-ises ::-webkit-scrollbar-track { background: transparent; }
body.theme-ises ::-webkit-scrollbar-thumb {
  background: var(--i-campo-brd);
  border-radius: 4px;
}
body.theme-ises ::-webkit-scrollbar-thumb:hover { background: var(--i-campo-brd-hover); }


/* ==========================================================================
   4. DOCK (el rail)
   ==========================================================================
   Cápsula flotante de 64px con vidrio, no una barra pegada al borde. La
   magnificación al pasar el cursor la calcula js/dock-ises.js escribiendo
   width/height inline; aquí solo va la transición para que el retorno sea
   suave y los tooltips laterales.
   ========================================================================== */

/* La cápsula NO ocupa todo el alto: se centra verticalmente en su columna de
   64px, que es lo que hace el diseño (un contenedor de 100vh con el dock
   centrado dentro). Como .rail es hijo flex de .app-shell, basta con dejar
   que su altura la marque el contenido y centrarlo con align-self. */
body.theme-ises .rail {
  position: fixed;
  left: 14px;
  top: 50%;
  transform: translateY(-50%);
  flex: 0 0 auto;
  width: var(--rail-w);
  height: auto;
  margin: 0;
  padding: 6px;
  gap: 8px;
  border: 1px solid var(--i-dock-brd);
  border-right: 1px solid var(--i-dock-brd);
  border-radius: var(--radius-lg);
  background: var(--i-dock-bg);
  backdrop-filter: var(--i-glass-blur-alto);
  -webkit-backdrop-filter: var(--i-glass-blur-alto);
  z-index: 6;
}

/* El separador elástico estiraría la cápsula hasta el alto de la pantalla. */
body.theme-ises .rail-spacer { flex: 0 0 auto; display: none; }

/* Igual que en el diseño: cuadrado de 34 con radio 10, no un círculo. Se le
   conserva el giro completo al pasar el cursor. */
body.theme-ises .rail-logo {
  width: 34px;
  height: 34px;
  padding: 8px;
  margin-bottom: 0;
  border: 1px solid var(--i-brd-10);
  border-radius: var(--radius-sm);
  background: rgb(var(--velo-rgb) / 0.05);
  transition: transform 0.35s ease;
}
body.theme-ises .rail-logo:hover { transform: rotate(360deg); }

body.theme-ises .tab-btn {
  position: relative;
  width: 34px;
  height: 34px;
  border-radius: var(--radius-sm);
  color: var(--i-txt-5);
  transition: background-color 0.18s ease, color 0.18s ease,
              width 0.18s var(--i-ease-dock), height 0.18s var(--i-ease-dock);
}

body.theme-ises .tab-btn svg {
  width: 17px;
  height: 17px;
  transition: transform 0.18s var(--i-ease-dock);
}

body.theme-ises .tab-btn:hover {
  background: rgb(var(--velo-rgb) / 0.05);
  color: var(--i-txt-2);
}

body.theme-ises .tab-btn.active {
  background: var(--accent-surface);
  color: var(--accent);
}

/* Tooltip lateral. Sale de aria-label, que los nueve botones ya traen, así
   que no hay que tocar el HTML. El `title` nativo sigue ahí para quien no
   tenga CSS. */
body.theme-ises .rail .tab-btn::after,
body.theme-ises .rail .rail-logout::after {
  content: attr(aria-label);
  position: absolute;
  left: calc(100% + 12px);
  top: 50%;
  transform: translateY(-50%) translateX(-4px);
  padding: 0 9px;
  height: var(--i-h-badge);
  display: flex;
  align-items: center;
  white-space: nowrap;
  border: 1px solid var(--i-brd-08);
  border-radius: var(--i-r-6);
  background: var(--i-sup-3);
  color: var(--i-txt-2);
  font-size: 11px;
  font-weight: 500;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s ease, transform 0.18s var(--i-ease-dock);
  z-index: 7;
}

body.theme-ises .rail .tab-btn:hover::after,
body.theme-ises .rail .rail-logout:hover::after {
  opacity: 1;
  transform: translateY(-50%) translateX(0);
}

body.theme-ises .rail-badge {
  background: var(--danger);
  color: #fff;
  border: 0;
}

body.theme-ises .rail-session {
  gap: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--i-dock-brd);
  width: 34px;
}

body.theme-ises .rail-avatar {
  width: 34px;
  height: 34px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--accent-line);
  background: var(--accent-surface);
  color: var(--i-azul-claro);
  font-family: var(--font-display);
  font-size: 11px;
  font-weight: 600;
}

body.theme-ises .rail-role {
  font-family: var(--font-mono);
  font-size: 8px;
  letter-spacing: 0.08em;
  color: var(--i-txt-5);
}

body.theme-ises .rail-logout {
  position: relative;
  width: 34px;
  height: 34px;
  border-radius: var(--radius-sm);
  color: var(--i-txt-5);
}
body.theme-ises .rail-logout:hover {
  background: var(--danger-surface);
  color: var(--danger);
}


/* ==========================================================================
   5. BARRA SUPERIOR — chips, pastillas y selector de fondo
   ==========================================================================
   76px de alto, vidrio con desenfoque alto y tres zonas: estado a la
   izquierda, pastillas al centro, indicadores y selector de fondo a la
   derecha.
   ========================================================================== */

body.theme-ises .statusbar {
  height: var(--i-h-header);
  padding: 0 22px;
  gap: 14px;
  flex-wrap: nowrap;
  border-bottom: 1px solid var(--i-brd-07);
  background: var(--i-header-bg);
  backdrop-filter: var(--i-glass-blur-alto);
  -webkit-backdrop-filter: var(--i-glass-blur-alto);
  position: sticky;
  top: 0;
  z-index: 5;
}

body.theme-ises .status-online {
  height: 24px;
  padding: 0 10px;
  gap: 6px;
  border-radius: var(--i-r-6);
  border: 1px solid var(--i-azul-line-2);
  background: var(--accent-surface);
  color: var(--i-azul-claro);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.09em;
}

body.theme-ises .status-online .dot {
  width: 5px;
  height: 5px;
  background: var(--accent);
  box-shadow: none;
}

body.theme-ises .status-item {
  font-size: 11px;
  letter-spacing: 0.08em;
  color: var(--i-txt-4);
}

body.theme-ises .status-pill {
  min-width: 0;
  height: 24px;
  display: inline-flex;
  align-items: center;
  padding: 0 10px;
  border-radius: var(--i-r-6);
  border: 1px solid var(--i-verde-line-2);
  background: var(--i-verde-sup-2);
  color: var(--green);
  font-size: 10px;
  letter-spacing: 0.08em;
}

/* En el diseño el orden de la derecha es: selector de fondo y DESPUÉS el chip
   de estado. En el HTML van al revés, y se recoloca con `order` para no mover
   markup que el JS conoce. */
body.theme-ises .ises-fondo-toggle { order: 5; }
body.theme-ises .status-pill { order: 6; margin-left: 0; }

/* --- Navegación central: logo giratorio + pastillas --- */

body:not(.theme-ises) .ises-nav { display: none; }

body.theme-ises .ises-nav {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin: 0 auto;
  min-width: 0;
}

/* El logo da una vuelta completa al pasar el cursor. */
/* Va a la altura de la cápsula para que las dos piezas del centro se lean
   como una sola. La vuelta completa se alarga para acompañar a las pastillas. */
body.theme-ises .ises-nav-logo {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  flex: 0 0 auto;
  overflow: hidden;
  border: 1px solid var(--i-brd-16);
  border-radius: 50%;
  background: var(--i-logo-bg);
  cursor: pointer;
  transition: transform 0.35s ease;
}
body.theme-ises .ises-nav-logo:hover { transform: rotate(360deg); }
body.theme-ises .ises-nav-logo img {
  display: block;
  width: 22px;
  height: 22px;
  object-fit: contain;
}

/* La palabra "ISES" solo existe en la cabecera móvil del diseño. Aquí se
   apaga y se enciende dentro del @media; ninguna otra regla la toca. */
.ises-nav-marca { display: none; }

/* Envoltorio de los tres botones secundarios de "Productos y servicios". Solo
   sirve en móvil (allí se convierte en la caja con borde del diseño); aquí
   desaparece del layout para que la fila de escritorio no cambie ni un píxel. */
.ha-grupo { display: contents; }

body.theme-ises .ises-pills {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  height: 34px;
  padding: 3px;
  border-radius: var(--i-r-pill);
  background: var(--i-inverso-bg);
}

/* Icono a la izquierda del texto, en una sola línea, como en el diseño. */
body.theme-ises .ises-pill {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 28px;
  padding: 0 14px;
  border: 0;
  border-radius: var(--i-r-pill);
  background: var(--i-inverso-bg);
  color: var(--i-inverso-ink);
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.02em;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  overflow: hidden;
}
body.theme-ises .ises-pill[hidden] { display: none; }

/* El círculo negro sube desde abajo y desborda la pastilla. Sale SOLO al
   pasar el cursor: la activa se marca con el punto, no con relleno. Duración
   y curva son las del diseño. */
body.theme-ises .ises-pill-blob {
  position: absolute;
  z-index: 1;
  left: 50%;
  bottom: -42px;
  width: 120px;
  height: 120px;
  border-radius: 50%;
  background: var(--i-inverso-ink);
  pointer-events: none;
  transform: translateX(-50%) scale(0);
  transition: transform 0.42s cubic-bezier(0.2, 0.8, 0.2, 1);
}

body.theme-ises .ises-pill:hover .ises-pill-blob { transform: translateX(-50%) scale(1.25); }

body.theme-ises .ises-pill-cara {
  position: relative;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: inherit;
  transition: color 0.28s ease;
}

body.theme-ises .ises-pill-cara svg { width: 15px; height: 15px; }

body.theme-ises .ises-pill:hover { color: var(--i-inverso-bg); }

/* Globo de leads nuevos. */
body.theme-ises .ises-pill-badge {
  position: absolute;
  z-index: 3;
  top: 1px;
  right: 6px;
  min-width: 15px;
  height: 15px;
  padding: 0 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  background: var(--danger);
  color: #ffffff;
  font-size: 9px;
  font-weight: 600;
  line-height: 1;
}
body.theme-ises .ises-pill-badge[hidden] { display: none; }

/* El punto solo existe en la pastilla activa: azul en reposo y blanco cuando
   el círculo negro la cubre. */
body.theme-ises .ises-pill-dot { display: none; }
body.theme-ises .ises-pill.activa .ises-pill-dot {
  position: absolute;
  z-index: 3;
  display: block;
  left: 50%;
  bottom: 2px;
  width: 10px;
  height: 3px;
  border-radius: var(--i-r-2);
  background: var(--accent);
  transform: translateX(-50%);
  transition: background-color 0.28s ease;
}
body.theme-ises .ises-pill.activa:hover .ises-pill-dot { background: var(--i-inverso-bg); }


/* Las tres secciones que viven en la barra superior salen del dock: tenerlas
   en los dos sitios duplicaría la navegación. Siguen en el HTML y siguen
   siendo los botones que switchSection conoce —las pastillas les reenvían el
   clic—, solo que ya no se pintan. */
body.theme-ises .rail-nav .tab-btn[data-section="whatsapp"],
body.theme-ises .rail-nav .tab-btn[data-section="leads"],
body.theme-ises .rail-nav .tab-btn[data-section="usuarios"] {
  display: none;
}

/* Las pastillas de Calculadoras, Métricas y Facturación existían solo para el
   móvil (en escritorio estaban en display:none) porque en el dock de cinco
   huecos no cabían. Sin dock, se borraron del HTML: esas tres secciones salen
   en el menú del ☰ como las otras diez. */

/* --- El ☰ del menú de secciones ---
   Es la navegación del teléfono. En escritorio no se pinta: ahí está el riel
   lateral con las trece, y este botón sería un segundo camino a lo mismo. Su
   forma, en el bloque 17. */
body.theme-ises .ises-nav-menu { display: none; }
body:not(.theme-ises) .ises-nav-menu { display: none; }

/* --- Selector de fondo --- */

body:not(.theme-ises) .ises-fondo-toggle { display: none; }

body.theme-ises .ises-fondo-toggle {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 2px;
  border: 1px solid var(--i-brd-08);
  border-radius: var(--i-r-9);
  background: rgb(var(--velo-rgb) / 0.04);
}

body.theme-ises .ises-fondo-opt {
  height: 22px;
  padding: 0 10px;
  border: 0;
  border-radius: var(--i-r-7);
  background: transparent;
  color: var(--i-txt-4);
  font-family: inherit;
  font-size: 11px;
  font-weight: 400;
  line-height: 1;
  cursor: pointer;
  transition: background-color 0.18s ease, color 0.18s ease;
}

body.theme-ises .ises-fondo-opt:hover { color: var(--i-txt-2); }

body.theme-ises .ises-fondo-opt.activo {
  background: var(--i-brd-14);
  color: var(--i-txt-1);
  font-weight: 500;
}


/* ==========================================================================
   6. DETALLES GLOBALES
   ========================================================================== */

body.theme-ises {
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 1.6;
}

/* Medido contra el diseño elemento por elemento. Lo que separa el titulo del
   cuerpo NO es el tamaño —eso ya coincidia— sino el PESO: el diseño deja todo
   el cuerpo en 400 y reserva 600/700 para los titulos. Con el cuerpo en 500 la
   jerarquia se aplana y los titulos dejan de destacar. */
/* Los títulos van un escalón por encima del diseño (26/14/12 -> 32/17/14) por
   decisión del dueño: con el cuerpo en 400 y los títulos más grandes, la
   jerarquía se lee mejor a los anchos de pantalla reales. Los pesos y el
   apretado de letras SÍ se mantienen como en el diseño. */
body.theme-ises h1 {
  font-size: 32px;
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -0.022em;
}

body.theme-ises h2 {
  font-size: 17px;
  font-weight: 600;
  letter-spacing: normal;
  line-height: 1.5;
}
body.theme-ises h3 {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: normal;
  line-height: 1.5;
}

/* El subtítulo acompaña al título: si el h1 crece y él no, la cabecera se
   descompensa. */
body.theme-ises .section-header p { font-size: 13px; }

body.theme-ises ::selection { background: var(--accent); color: #fff; }

/* El foco del diseño es un borde azul, no un halo. */
body.theme-ises *:focus-visible {
  outline: none;
  box-shadow: var(--ring);
}

/* Cualquier dato que se lea como cifra va en mono. */
body.theme-ises .status-pill,
body.theme-ises #numeroCotizacion,
body.theme-ises .row-price,
body.theme-ises .item-total,
body.theme-ises .mono { font-family: var(--font-mono); }

/* El texto con brillo de los elementos de IA: el único degradado del tema. */
@keyframes ises-shine {
  0%   { background-position: 150% center; }
  100% { background-position: -50% center; }
}

/* El diseño no usa marco brillante en ningún sitio: lo que brilla es el
   TEXTO. Aquí se apaga el anillo de degradado y el halo de `.border-glow`
   (styles.css:4313) y se pone el barrido sobre las letras. */
body.theme-ises .border-glow::before,
body.theme-ises .border-glow::after { display: none; }

body.theme-ises .ia-panel h2,
body.theme-ises #modoIaBtn {
  background-image: linear-gradient(120deg,
    var(--i-brillo-base) 0%, var(--i-brillo-base) 35%, var(--i-brillo) 50%,
    var(--i-brillo-base) 65%, var(--i-brillo-base) 100%);
  background-size: 200% auto;
  background-clip: text;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: ises-shine 2.6s linear infinite;
}

/* El botón de IA activo deja de brillar: ya está seleccionado y el barrido
   competiría con el fondo del segmento. */
body.theme-ises #modoIaBtn { animation-duration: 2.2s; }
body.theme-ises #modoIaBtn.active {
  background-image: none;
  -webkit-text-fill-color: currentColor;
  animation: none;
}


/* ==========================================================================
   7. PRIMITIVAS COMPARTIDAS
   ==========================================================================
   Paneles, botones, insignias y control segmentado. Ojo con la regla del
   desenfoque: los paneles NO lo llevan. Son decenas y están sobre un canvas
   animado; cada uno obligaría al compositor a releer el fondo en cada frame.
   ========================================================================== */

/* El desenfoque va en el contenedor, nunca en lo que vive dentro: así una
   pantalla con 30 tarjetas son 30 superficies, no 300. */
body.theme-ises .panel {
  padding: 16px;
  margin-bottom: 14px;
  border: 1px solid var(--i-glass-brd);
  border-radius: var(--radius);
  background: var(--i-glass-bg);
  backdrop-filter: var(--i-glass-blur);
  -webkit-backdrop-filter: var(--i-glass-blur);
}

body.theme-ises .section-header { margin-bottom: 18px; }
body.theme-ises .section-header p { color: var(--i-txt-4); }

/* --- Botones --- */

body.theme-ises .btn {
  height: var(--i-h-btn);
  padding: 0 14px;
  gap: 7px;
  border-radius: var(--radius-sm);
  font-size: 12px;
  font-weight: 500;
  border: 1px solid transparent;
  box-shadow: none;
}

body.theme-ises .btn.primary {
  border-color: var(--accent);
  background: var(--accent);
  color: var(--accent-ink);
}
body.theme-ises .btn.primary:hover {
  border-color: var(--accent-hover);
  background: var(--accent-hover);
}

body.theme-ises .btn.secondary,
body.theme-ises .btn.ghost,
body.theme-ises .btn.dark {
  border-color: var(--i-campo-brd);
  background: var(--i-sup-1);
  color: var(--i-txt-2);
}
body.theme-ises .btn.secondary:hover,
body.theme-ises .btn.ghost:hover,
body.theme-ises .btn.dark:hover {
  border-color: var(--i-campo-brd-hover);
  color: var(--i-txt-1);
}

body.theme-ises .btn.danger {
  border-color: var(--danger-line);
  background: var(--danger-surface);
  color: var(--danger);
}

body.theme-ises .btn.warning {
  border-color: var(--i-amarillo-line);
  background: var(--amber-surface);
  color: var(--amber);
}

body.theme-ises .btn.compact,
body.theme-ises .btn.icon {
  height: var(--i-h-btn-sm);
  padding: 0 10px;
  border-radius: var(--i-r-9);
  font-size: 11px;
}
body.theme-ises .btn.icon { width: var(--i-h-btn-sm); padding: 0; }

/* --- Insignias --- */

body.theme-ises .status-badge,
body.theme-ises .type-badge {
  height: var(--i-h-badge);
  display: inline-flex;
  align-items: center;
  padding: 0 9px;
  border-radius: var(--i-r-6);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  box-shadow: none;
}

body.theme-ises .status-badge.pendiente {
  border: 1px solid var(--i-amarillo-line);
  background: var(--amber-surface);
  color: var(--amber);
}
body.theme-ises .status-badge.aprobada {
  border: 1px solid var(--green-line);
  background: var(--green-surface);
  color: var(--green);
}
body.theme-ises .status-badge.rechazada {
  border: 1px solid var(--danger-line);
  background: var(--danger-surface);
  color: var(--danger);
}

/* --- Control segmentado --- */

body.theme-ises .segmented {
  padding: 3px;
  border: 1px solid var(--i-brd-08);
  border-radius: var(--i-r-11);
  background: var(--i-sup-2);
}

body.theme-ises .seg-btn {
  height: var(--i-h-btn-sm);
  padding: 0 12px;
  border-radius: var(--i-r-9);
  font: 400 11px/1 var(--font-body);
  color: var(--i-txt-4);
}
body.theme-ises .seg-btn:hover { color: var(--i-txt-2); }
body.theme-ises .seg-btn.active {
  border: 1px solid var(--i-campo-brd-hover);
  background: var(--i-sup-5);
  color: var(--i-txt-1);
  font-weight: 600;
  box-shadow: none;
}

/* --- Estado vacío --- */

body.theme-ises .empty,
body.theme-ises .items-empty td,
body.theme-ises .pill-empty {
  padding: 36px 16px;
  text-align: center;
  color: var(--i-txt-5);
  font-size: 12px;
}


/* ==========================================================================
   10. CAMPOS DE FORMULARIO
   ==========================================================================
   El diseño usa etiquetas en minúscula de 10px y campos de 34px con borde
   sólido. La hoja base las pone en mayúsculas: aquí se revierte.
   ========================================================================== */

body.theme-ises label {
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.02em;
  text-transform: none;
  color: var(--i-txt-5);
}

body.theme-ises input,
body.theme-ises textarea,
body.theme-ises select {
  min-width: 0;
  width: 100%;
  margin-top: 5px;
  height: var(--i-h-campo);
  padding: 0 10px;
  border: 1px solid var(--i-campo-brd);
  border-radius: var(--radius-sm);
  background: var(--i-sup-2);
  color: var(--i-txt-1);
  font-size: 13px;
  /* El cuerpo va en 400: el peso se reserva para los títulos. */
  font-weight: 400;
}

body.theme-ises textarea {
  height: auto;
  padding: 9px 10px;
  line-height: 1.6;
  resize: none;
}

body.theme-ises input::placeholder,
body.theme-ises textarea::placeholder { color: var(--i-txt-5); }

body.theme-ises input:focus,
body.theme-ises textarea:focus,
body.theme-ises select:focus {
  border-color: var(--accent);
  box-shadow: none;
}

body.theme-ises input[type="checkbox"],
body.theme-ises input[type="radio"] {
  width: auto;
  height: auto;
  min-width: 0;
}

body.theme-ises input[type="file"] { height: auto; padding: 8px 10px; }

/* --- Ni una etiqueta en mayúsculas ---
   El diseño no usa versalitas en ningún sitio: las etiquetas son de 10px en
   minúscula. La hoja base tiene 27 `text-transform: uppercase`; aquí se
   neutralizan todos MENOS tres, y por buenas razones:
     · .pdf-title h2      -> es del documento PDF, que este tema no toca.
     · .rail-avatar
     · .user-badge-avatar -> son iniciales de persona; el diseño también las
                             pone en mayúscula. */
body.theme-ises label,
body.theme-ises th,
body.theme-ises .type-selector legend,
body.theme-ises .ia-checkbox-group legend,
body.theme-ises .mx-kpi-label,
body.theme-ises .mx-mini-label,
body.theme-ises table.data-cards td::before,
body.theme-ises .catalogo-table thead th,
body.theme-ises .usuarios-table thead th,
body.theme-ises #productForm .field-label,
body.theme-ises #userForm .field-label,
body.theme-ises .qh-item .lbl,
body.theme-ises .summary .grand-letra,
body.theme-ises .login-field > label,
body.theme-ises .confirm-reason-label,
body.theme-ises .cat-side-label,
body.theme-ises .cat-valor-label,
body.theme-ises .dz2-glabel,
body.theme-ises .dz2-leg-title,
body.theme-ises .dz-metric-k,
body.theme-ises .dz-dori-head,
body.theme-ises .dz-modelo-grupo {
  text-transform: none;
  letter-spacing: 0.02em;
}

/* Las etiquetas de la app son todas del mismo tamaño y color. */
body.theme-ises .qh-item .lbl,
body.theme-ises .mx-kpi-label,
body.theme-ises .mx-mini-label,
body.theme-ises #productForm .field-label,
body.theme-ises #userForm .field-label,
body.theme-ises .login-field > label,
body.theme-ises .cat-side-label {
  font-size: 10px;
  font-weight: 500;
  color: var(--i-txt-5);
}


/* ==========================================================================
   8/9/11/13/14. TABLAS Y LISTADOS
   ==========================================================================
   El diseño no usa <table>: son rejillas con filas separadas por una línea de
   1px. La app sí usa tablas de verdad y eso no se toca; lo que se copia es el
   aspecto: cabecera tenue de 10px, separadores #161618 y cifras en mono.

   Regla del desenfoque: NUNCA en filas. Son cientos y están sobre un canvas
   animado.
   ========================================================================== */

body.theme-ises table { border-collapse: collapse; }

/* Los paddings de las superficies DENSAS pasan por --dens. Solo aqui: el
   resto de los espaciados de la app estan calibrados a mano contra el dock y
   el header, y escalarlos descuadraria la cabecera antes que ayudar a nadie. */
body.theme-ises th {
  padding: 0 calc(10px * var(--dens)) calc(8px * var(--dens));
  border-bottom: 1px solid var(--i-sup-2);
  background: transparent;
  color: var(--i-txt-5);
  font-size: 10px;
  font-weight: 500;
}

body.theme-ises td {
  padding: calc(10px * var(--dens));
  border-bottom: 1px solid var(--i-sup-2);
  font-size: 12px;
  font-weight: 400;
  color: var(--i-txt-2);
}

body.theme-ises tbody tr:hover td { background: rgb(var(--velo-rgb) / 0.03); }

/* Folios, SKUs y fechas: monoespaciados SIEMPRE. Son identificadores, y su
   valor esta en que se puedan comparar caracter a caracter de un renglon a
   otro. El configurador no los toca. */
body.theme-ises .quote-num,
body.theme-ises .quote-fecha,
body.theme-ises .cat-card-sku,
body.theme-ises .user-date,
body.theme-ises .lead-fecha { font-family: var(--font-mono); }

/* Importes: esto SI lo gobierna el configurador (pestana Datos). Por omision
   es la misma mono de siempre, asi que sin tocar nada se ve como antes. */
body.theme-ises .item-total,
body.theme-ises .item-price,
body.theme-ises .item-stock,
body.theme-ises .row-price,
body.theme-ises .quote-total,
body.theme-ises .cat-card-precio,
body.theme-ises .mx-kpi-value,
body.theme-ises .syscom-price {
  font-family: var(--font-cifras);
  font-variant-numeric: var(--cifras-num);
}

/* Las negritas van por CLASE y no por token, y no es un capricho: cada cifra
   trae ya su propio peso calibrado (el KPI grande es 400 justamente para que
   no grite). Un token compartido los aplanaria a todos al mismo numero. Asi,
   apagado = la app de siempre, y encendido = los importes suben un escalon
   desde donde cada uno estaba. La especificidad extra de `.cifras-fuertes`
   es la que le gana al `font-weight: 400` que .mx-kpi-value declara despues. */
body.theme-ises.cifras-fuertes .item-total,
body.theme-ises.cifras-fuertes .item-price,
body.theme-ises.cifras-fuertes .item-stock,
body.theme-ises.cifras-fuertes .row-price,
body.theme-ises.cifras-fuertes .quote-total,
body.theme-ises.cifras-fuertes .cat-card-precio,
body.theme-ises.cifras-fuertes .mx-kpi-value,
body.theme-ises.cifras-fuertes .syscom-price { font-weight: 600; }


/* ==========================================================================
   9. MÉTRICAS
   ========================================================================== */

body.theme-ises .mx-kpi {
  border: 1px solid var(--i-glass-brd);
  border-radius: var(--radius);
  background: var(--i-glass-bg);
  backdrop-filter: var(--i-glass-blur);
  -webkit-backdrop-filter: var(--i-glass-blur);
  box-shadow: none;
}

body.theme-ises .mx-kpi-value {
  font-size: 24px;
  font-weight: 400;
  letter-spacing: -0.01em;
  color: var(--i-txt-1);
}

body.theme-ises .mx-kpi-hint { font-size: 11px; color: var(--i-txt-4); }

body.theme-ises .mx-topbar {
  border-bottom: 1px solid var(--i-brd-07);
  background: transparent;
}

/* La hoja base esconde la barra superior en Métricas porque esa vista trae su
   propio encabezado (styles.css:1136). En el diseño ISES la barra es parte de
   la identidad y no desaparece nunca: las pastillas y el selector de fondo
   tienen que seguir ahí. El encabezado propio de la vista se queda debajo,
   igual que en el diseño. */
body.theme-ises .main:has(#sectionMetricas.active) .statusbar { display: flex; }

/* Métricas es la única vista que corre de borde a borde: escapa del padding
   de .content con un margen negativo, que hay que igualar al nuevo valor. Su
   cuerpo se centra con el mismo máximo que el resto. */
body.theme-ises #sectionMetricas.active { margin: -24px -92px 0; }
body.theme-ises .mx-body,
body.theme-ises .mx-topbar > * { margin-inline: auto; max-width: 1500px; }

/* ...PERO EL CANALÓN IZQUIERDO NO ES SUYO: AHÍ FLOTA EL RIEL.
   Los 92px que `.content` deja a la izquierda son el hueco de la cápsula del
   dock (fija en left:14, 64 de ancho). Métricas los cancelaba con el margen
   negativo y metía su contenido debajo: a 1440px el riel (x 14–78) tapaba
   medio botón "7 días" (x 42–98), el punto verde de la línea de estado y la
   esquina izquierda de la PRIMERA tarjeta de KPI. Se veía todo entero y no
   respondía al clic, que es la peor forma de estar roto. Pasaba en todo el
   rango de 760 a ~1600px, o sea en cualquier portátil.

   Lo que se aparta es el CONTENIDO, no la superficie: la banda del encabezado
   y su línea inferior siguen corriendo de canto a canto, que es lo aprobado.
   Los 84px dejan el contenido arrancando en x=92, alineado con el de las otras
   trece secciones. Por encima de ~1600px el centrado ya dejaba el riel atrás y
   esto solo mueve el bloque 27px: imperceptible a ese ancho.

   No aplica por debajo de 760px: ahí no hay riel que esquivar (el bloque 17 lo
   apaga entero) y el bloque móvil de más abajo pone estos paddings a cero. */
@media (min-width: 760px) {
  body.theme-ises .mx-topbar,
  body.theme-ises .mx-statusline,
  body.theme-ises .mx-body { padding-left: 84px; }
}

/* Y a partir de 1620px se devuelve la simetría: ahí el `max-width: 1500px` con
   márgenes automáticos ya empuja el contenido más allá del riel por su cuenta
   (arranca en x≥90) y el canalón extra solo serviría para dejar el bloque 27px
   descentrado. El umbral sale de la cuenta, no del aire: con 1500 de máximo y
   8 de sangría, el bloque empieza en 8 + (100vw − 1516) / 2, y hacen falta 1620
   para que sus 30px de relleno propio caigan ya fuera del riel.

   LA LÍNEA DE ESTADO NO ENTRA en esta excepción: es la única de las tres que no
   lleva `max-width`, así que ningún centrado la aparta y a cualquier ancho su
   texto arrancaría en x=38, debajo del riel. Se queda con el canalón siempre. */
@media (min-width: 1620px) {
  body.theme-ises .mx-topbar,
  body.theme-ises .mx-body { padding-left: 30px; }
}


/* ==========================================================================
   11. PRODUCTOS Y CATÁLOGO
   ========================================================================== */

body.theme-ises .catalogo-card,
body.theme-ises .cat-card,
body.theme-ises .syscom-card {
  border: 1px solid var(--i-glass-brd);
  border-radius: var(--radius);
  background: var(--i-glass-bg);
  backdrop-filter: var(--i-glass-blur);
  -webkit-backdrop-filter: var(--i-glass-blur);
  box-shadow: none;
}

/* El hueco de la foto: relación 4/3 y el rótulo "sin foto" en mono diminuto. */
body.theme-ises .cat-card-img,
body.theme-ises .product-thumb {
  background: var(--i-sup-1);
  border-bottom: 1px solid var(--i-brd-08);
}

/* El cuerpo de la tarjeta tambien sigue la densidad: es la superficie donde
   "ver mas de un vistazo" de verdad se nota. */
body.theme-ises .cat-card-body {
  gap: calc(6px * var(--dens));
  padding: calc(13px * var(--dens));
}

body.theme-ises .cat-card-sku {
  font-size: 10px;
  color: var(--i-txt-5);
  letter-spacing: 0.06em;
}

/* La insignia de stock se apoya sobre la FOTO del producto, así que styles.css
   le pone un velo casi opaco para que se lea sobre cualquier imagen. Pero ese
   velo es negro fijo (`rgba(10,10,12,.82)`) y en claro la tinta ya es verde o
   rojo OSCUROS: quedaba texto oscuro sobre pastilla negra, que no es que se
   viera feo, es que no se leía.

   En claro el velo se da la vuelta —blanco casi opaco—, que es el mismo recurso
   y no una versión apagada: la tinta semántica del tema claro está calculada
   justo para fondos así (#1c7c3e y #d70015 pasan de 5:1 sobre blanco). */
body.theme-ises.tema-claro .cat-card-stock {
  background: rgba(255, 255, 255, 0.92);
}

body.theme-ises .cat-card-precio {
  font-size: 14px;
  color: var(--i-azul-claro);
}

body.theme-ises .cat-side,
body.theme-ises .cat-side-card {
  border: 1px solid var(--i-glass-brd);
  border-radius: var(--radius);
  background: var(--i-glass-bg);
  backdrop-filter: var(--i-glass-blur);
  -webkit-backdrop-filter: var(--i-glass-blur);
}

body.theme-ises .cat-cat {
  border-radius: var(--i-r-9);
  color: var(--i-txt-4);
  font-size: 12px;
}
body.theme-ises .cat-cat:hover { background: rgb(var(--velo-rgb) / 0.04); color: var(--i-txt-2); }
/* La clase que pone app.js es `is-active`, no `active`. */
body.theme-ises .cat-cat.is-active {
  background: var(--accent-surface);
  color: var(--accent);
}

/* El alta de producto ya no es una columna de esta rejilla: vive en
   #productoModal. La regla se queda por si la hoja base vuelve a poner dos
   columnas; el catálogo se lleva todo el ancho. */
body.theme-ises .catalogo-layout { grid-template-columns: minmax(0, 1fr); }

body.theme-ises .cat-grid { grid-template-columns: 248px minmax(0, 1fr); }

/* La raya decorativa que precede a cada categoría no existe en el diseño y se
   comía 60px de los 220 del aside, truncando los nombres. */
body.theme-ises .cat-cat-dash { display: none; }

/* ---- Que el nombre de la categoría se lea COMPLETO ----------------------
   Esconder la raya (arriba) no devolvía su espacio: el hueco lo reservaba el
   `padding-left` de `.cat-cat`, calculado sobre `--marca-largo` (70px), más el
   `margin-right` de `.cat-cat-label`, que reserva de antemano el corrimiento
   del hover (`--desplaza`, 30px). Entre los dos se comían 100 de los 220px de
   la columna y al nombre le quedaban ~50: de ahí "01 Almace…".

   Se ataca por los tres lados a la vez, porque quitar solo el ellipsis dejaría
   los nombres largos partidos en cuatro renglones. */
body.theme-ises .cat-cats {
  --marca-largo: 0px;   /* la raya está oculta: no hay nada que hospedar */
  --desplaza: 8px;      /* corrimiento de hover más corto, pero vivo */
}

/* La rayita separadora entre categorías medía `--marca-largo * --marca-tick`:
   con la variable en 0 desaparecía. Se le da un ancho propio. */
body.theme-ises .cat-cat::after { width: 24px; }

/* Solo de tableta para arriba: por debajo de 760px la lista deja de ser una
   columna y se vuelve una tira horizontal de chips, donde envolver el texto
   rompería la tira. */
@media (min-width: 760px) {
  /* El `align-items: baseline` de la hoja base se queda: con un nombre de dos
     renglones, la línea base del `.cat-cat-label` es la de su PRIMER renglón,
     así que el número y el conteo siguen alineados con la primera línea, que es
     justo como se ve en el diseño. */
  body.theme-ises .cat-cat-name {
    overflow: visible;
    text-overflow: clip;
    white-space: normal;
    line-height: 1.25;
    /* Un modelo o una palabra larguísima sin espacios no debe empujar la
       columna ni sacar el conteo de la caja. */
    overflow-wrap: anywhere;
  }
}

/* Rejilla del diseño: tantas columnas como quepan a partir de 200px. */
body.theme-ises .cat-cards {
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: calc(12px * var(--dens));
}

/* Numero fijo de columnas, cuando el configurador lo pide. Va AQUI, antes de
   los media queries del bloque 17, porque en telefono manda el ancho de la
   pantalla y no la preferencia: tres columnas en 375px no son una preferencia,
   son un error. Al ser la misma especificidad, el que va despues gana, y el
   que va despues es el media query. */
body.theme-ises[data-cat-cols="2"] .cat-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
body.theme-ises[data-cat-cols="3"] .cat-cards { grid-template-columns: repeat(3, minmax(0, 1fr)); }
body.theme-ises[data-cat-cols="4"] .cat-cards { grid-template-columns: repeat(4, minmax(0, 1fr)); }


/* ==========================================================================
   12. ENTRADA DE SECCIÓN
   ==========================================================================
   Aquí vivía el cajón lateral de alta de producto. El formulario se mudó a
   #productoModal (bloque 24) y de este bloque solo queda la animación de
   entrada, que sigue haciendo falta por lo que explica el comentario.
   ========================================================================== */

/* La animación de entrada de sección de la hoja base trae dos efectos
   secundarios, y los dos vienen de `animation-fill-mode: both`, que deja la
   animación aplicada PARA SIEMPRE cuando termina:

   1. Acaba en `transform: translateY(0)`, y ese transform, al quedarse, hace
      que la sección sea el contenedor de bloque de sus descendientes
      `position: fixed`, y les recorta el alcance a la caja de la sección.
   2. Un elemento con una animación de opacidad aplicada es un "backdrop
      root": el `backdrop-filter` de lo que hay dentro deja de ver el fondo de
      la página. Los paneles quedaban translúcidos pero SIN desenfocar, y con
      el fondo de metaballs las formas se leían atravesándolos.

   Se arregla con una entrada solo de opacidad y `fill-mode: none`: la
   animación desaparece al acabar (200 ms) y la sección vuelve a ser un
   elemento normal, sin transform y sin cortar el backdrop. */
@keyframes ises-section-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
body.theme-ises .section.active {
  animation-name: ises-section-in;
  animation-fill-mode: none;
}

/* Aquí vivía el cajón lateral de alta de producto: `.catalogo-side` fijado a la
   derecha, su velo `.glass-scrim` y el botón `.glass-drawer-close`. El
   formulario se mudó a #productoModal (bloque 24), que es un modal normal y no
   necesita nada de eso. La regla `.glass-only` se queda porque el tema Liquid
   Glass la sigue usando en su propia hoja. */
body:not(.theme-ises) .glass-only { display: none; }


/* ==========================================================================
   15. LOGIN
   ========================================================================== */

/* La hoja base coloca la pantalla de acceso con `position: fixed; inset: 0`,
   pero el bloque 2 la pasa a `position: relative` para poder ponerla POR
   ENCIMA del canvas del fondo — y con eso `inset: 0` deja de darle alto. La
   pantalla medía 419px de los 900 de la ventana, así que la tarjeta se
   quedaba pegada arriba en vez de centrada. Se le devuelve el ALTO, no la
   posición: `100dvh` y no `100vh` porque en el móvil la barra del navegador
   entra y sale. */
body.theme-ises .login-screen { min-height: 100dvh; }

body.theme-ises .login-card {
  /* El relleno lo pone el tema porque el tema es quien la convierte en
     tarjeta: la hoja base la deja sin caja, y con borde y fondo pero sin
     relleno el título y el logo tocaban el borde. */
  padding: 26px 24px;
  border: 1px solid var(--i-glass-brd);
  border-radius: var(--radius);
  background: var(--i-glass-bg);
  backdrop-filter: var(--i-glass-blur);
  -webkit-backdrop-filter: var(--i-glass-blur);
  box-shadow: none;
}

/* Los campos del login llevan su propio hueco a la izquierda para el icono
   (`padding: 0 44px 0 40px` en la hoja base). El relleno que el tema da a
   TODOS los campos pesa más y se lo comía, así que el icono acababa impreso
   encima del texto: se ve en el placeholder, que quedaba como "👤usuario". */
body.theme-ises .login-input input { padding: 0 44px 0 40px; }

body.theme-ises .login-aside { background: transparent; }

body.theme-ises .auth-spinner { border-top-color: var(--accent); }


/* ==========================================================================
   16. MODALES, AVISOS, ASISTENTE Y RESTO DE PANTALLAS
   ==========================================================================
   El diseño no maqueta nada de esto. Se extrapola con sus mismas reglas:
   contenedor de vidrio con borde de 1px y radio 14, separadores #161618,
   cifras en mono, etiquetas de 10px y ningún color fuera de la paleta.
   ========================================================================== */

body.theme-ises .modal { background: rgba(0, 0, 0, 0.72); }

body.theme-ises .modal-content,
body.theme-ises .confirm-content {
  border: 1px solid var(--i-glass-brd);
  border-radius: var(--radius);
  background: rgba(10, 10, 12, 0.86);
  backdrop-filter: var(--i-glass-blur);
  -webkit-backdrop-filter: var(--i-glass-blur);
  box-shadow: none;
}

body.theme-ises .toast {
  border: 1px solid var(--i-glass-brd);
  border-radius: var(--radius-sm);
  background: rgba(10, 10, 12, 0.92);
  box-shadow: none;
}

body.theme-ises .suggestion-item {
  border-bottom: 1px solid var(--i-sup-2);
  font-size: 12px;
}
body.theme-ises .suggestion-item:hover { background: rgb(var(--velo-rgb) / 0.04); }

body.theme-ises .product-suggestions {
  border: 1px solid var(--i-glass-brd);
  border-radius: var(--radius-sm);
  background: rgba(10, 10, 12, 0.92);
  box-shadow: none;
}

body.theme-ises .asistente-panel {
  border: 1px solid var(--i-glass-brd);
  border-radius: var(--radius);
  background: rgba(10, 10, 12, 0.86);
  backdrop-filter: var(--i-glass-blur);
  -webkit-backdrop-filter: var(--i-glass-blur);
  box-shadow: none;
}

body.theme-ises .asistente-fab {
  border: 1px solid var(--accent);
  background: var(--accent);
  box-shadow: none;
}

/* --------------------------------------------------------------------------
   LA MITAD QUE LE FALTABA AL TEMA CLARO

   Ocho superficies de la app llevan su velo escrito A MANO en negro
   —`rgba(10,10,12,.8x)` o `#141418`— en vez de salir de un token, y ninguna
   recibió su versión clara cuando se añadió el tema. En claro quedaban con
   fondo casi negro y la tinta ya calculada para fondo blanco encima: no es que
   se vieran oscuras, es que NO SE LEÍAN. Medido: las ocho con tinta
   rgb(28,28,30) o rgb(102,102,108) sobre un velo de luminancia 0.04.

   Las peores eran el cajón de captura de Gastos (un panel de 980 × 858 con el
   título en negro sobre negro) y las tres cajas de subir foto/CFDI.

   TRES DECISIONES:

   1. Se arregla AQUÍ y no en cada archivo porque tres de ellas viven en hojas
      que se cargan a demanda (gastos.css, facturas.css) y por tanto DESPUÉS de
      esta: con la misma especificidad ganarían ellas. `body.theme-ises
      .x` es (0,2,0) y esto es (0,3,0), así que manda sin depender del orden ni
      gastar !important.
   2. Un solo valor de velo para las siete de vidrio, aunque en oscuro alternen
      0.86 y 0.92: a esa opacidad la diferencia no se ve, y tener un número en
      vez de dos es lo que evita que la próxima vuelva a divergir.
   3. La caja de subida no lleva velo sino relleno sólido, porque en la hoja
      base tampoco lo lleva: es una superficie, no un panel flotante.
   -------------------------------------------------------------------------- */

body.theme-ises.tema-claro .modal-content,
body.theme-ises.tema-claro .confirm-content,
body.theme-ises.tema-claro .gastos-modal,
body.theme-ises.tema-claro .gastos-side,
body.theme-ises.tema-claro .facturas-side,
body.theme-ises.tema-claro .toast,
body.theme-ises.tema-claro .product-suggestions,
body.theme-ises.tema-claro .asistente-panel {
  background: rgba(252, 252, 253, 0.94);
}

body.theme-ises.tema-claro .upload-box {
  background: var(--i-sup-1);
}

/* --------------------------------------------------------------------------
   LA FILA SEÑALADA

   `styles.css` pinta el hover de CUALQUIER fila con `#16161a` a fuego. El tema
   ya le ponía su propio realce a las CELDAS —un 3 % del velo, que se invierte
   con el modo— pero el fondo del `<tr>` seguía debajo: en oscuro no se nota
   porque casi coincide con el lienzo, y en claro la fila entera se volvía un
   rectángulo NEGRO con el texto oscuro encima.

   La fila pasa al mismo velo que las celdas en vez de a `transparent`: sobre
   negro, `#16161a` es un 8,5 % de blanco, y quitarlo del todo habría dejado el
   realce del modo oscuro en el 3 % de las celdas, más flojo que antes. Con un
   4 % aquí y el 3 % de la celda encima se queda en un 7 % — el mismo peso de
   siempre en oscuro, y su espejo en claro. */
body.theme-ises tbody tr:hover { background: rgb(var(--velo-rgb) / 0.04); }

body.theme-ises .skeleton-bar,
body.theme-ises .skeleton-row { background: var(--i-sup-2); }


/* --- Barra de folio de la cotización ---
   El diseño la resuelve como una rejilla con separaciones de 1px sobre un
   fondo claro: los huecos del grid HACEN de líneas divisorias. */
body.theme-ises .quote-head {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr)) auto;
  align-items: stretch;
  gap: 0;
  padding: 0;
  background: var(--i-glass-bg);
  backdrop-filter: var(--i-glass-blur);
  -webkit-backdrop-filter: var(--i-glass-blur);
  border: 1px solid var(--i-glass-brd);
  border-radius: var(--radius);
  overflow: hidden;
}

/* Las divisiones son bordes, no huecos: así la insignia de estado puede
   conservar su propio fondo sin dejar una franja clara detrás. */
body.theme-ises .quote-head .qh-item {
  justify-content: center;
  gap: 3px;
  padding: 14px 16px;
  border-left: 1px solid var(--i-brd-08);
}
body.theme-ises .quote-head .qh-item:first-child { border-left: 0; }

body.theme-ises .quote-head .qh-badge {
  place-self: center end;
  margin-right: 16px;
}

body.theme-ises .qh-item .val { font-size: 13px; font-weight: 400; }
body.theme-ises .qh-folio .val {
  font-family: var(--font-mono);
  font-size: 16px;
  font-weight: 400;
  letter-spacing: normal;
}

/* Atajos de la cabecera: solo existen con el tema puesto. */
body:not(.theme-ises) .ises-solo { display: none; }
body.theme-ises .section-actions { display: flex; gap: 8px; flex: 0 0 auto; }


/* --- Botones destructivos ---
   La app pinta Borrar y Rechazar con `.btn.warning` (ámbar). En el diseño los
   destructivos son rojos, y el ámbar queda para lo que está pendiente. Se
   afina por `data-action` para no repintar todos los avisos ámbar de la app. */
body.theme-ises .btn.warning[data-action="delete"],
body.theme-ises .btn.warning[data-action="borrar"],
body.theme-ises .btn.warning[data-action="reject"] {
  border-color: var(--danger-line);
  background: var(--danger-surface);
  color: var(--danger);
}

/* Etiquetas que envuelven su propio campo: el margen superior del campo las
   separa de más y descoloca la fila. */
body.theme-ises .search-label input,
body.theme-ises .inline-form input { margin-top: 0; }


/* ==========================================================================
   17. RESPONSIVE
   ==========================================================================
   Los breakpoints son los cinco de la hoja base (1500 / 980 / 820 / 720 /
   480) y no se inventa ninguno. Aquí solo se adapta lo que el tema añade: el
   dock flotante, la barra de 76px, las pastillas y el selector de fondo.
   ========================================================================== */

/* El diseño móvil tiene UN SOLO umbral: 760px (`w < 760` en su JS, y su única
   media query es `max-width: 759px`). No hay estado intermedio de tableta: a
   800px pinta el escritorio completo. Aquí se respeta ese corte, aunque no
   esté entre los cinco de la hoja base, porque partirlo en 820/720 dejaría
   una franja con medio layout de cada uno.

   TODO lo de este bloque vive dentro de media queries: el escritorio, que ya
   está aprobado, no puede verse afectado. */

@media (max-width: 759px) {

  /* --- Cascarón ---------------------------------------------------------
     La cabecera se reduce a 64px con SOLO el logo y las pastillas centrados,
     y se fija arriba. Los chips y el selector de fondo bajan a una tira
     dentro del contenido, que se va con el scroll; eso se consigue sacando
     .ises-nav de la barra y dejando el resto de .statusbar en el flujo, sin
     tocar el HTML. */
  body.theme-ises .app-shell {
    display: block;
    height: auto;
    min-height: 100dvh;
    gap: 0;
    padding: 0;
    overflow: visible;
  }

  body.theme-ises .main {
    overflow: visible;
    padding-top: 64px;   /* el hueco de la cabecera fija */
  }

  body.theme-ises .statusbar {
    position: static;
    height: auto;
    /* Sin relleno: por debajo del umbral esta barra ya no PINTA nada. Sus tres
       indicadores de diagnóstico están en display:none, .ises-nav sale de aquí
       como cabecera fija y el engrane de apariencia se fija en la esquina, así
       que lo único que quedaba era una franja vacía de 16px empujando hacia
       abajo el contenido de las catorce pantallas. Sigue siendo el contenedor
       de esos dos hijos posicionados; solo deja de ocupar sitio. */
    padding: 0;
    gap: 8px;
    flex-wrap: wrap;
    border-bottom: 0;
    background: transparent;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }

  body.theme-ises .ises-nav {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 5;
    /* [☰] [marca ISES] ······ [apariencia] [salir]. Los dos de la derecha viven
       en otras ramas del DOM (#ajustesBtn en .statusbar y el de salir en
       .rail-session) y van fijos en esa esquina, así que su hueco se reserva
       aquí con el relleno derecho: 2 × 40px + los huecos. */
    justify-content: flex-start;
    align-items: center;
    height: 64px;
    margin: 0;
    padding: 0 108px 0 12px;
    gap: 10px;
    border-bottom: 1px solid var(--i-brd-07);
    background: var(--i-header-bg);
    backdrop-filter: var(--i-glass-blur-alto);
    -webkit-backdrop-filter: var(--i-glass-blur-alto);
  }

  body.theme-ises .content {
    /* Los 88px de abajo YA NO SON EL DOCK —ese desapareció— sino la pista de
       aterrizaje del botón flotante del asistente, que se posa en esa esquina
       (56px de alto + los 24 del borde + un respiro).

       Sin ellos, al llegar al final del scroll el último campo del formulario
       quedaba DEBAJO del botón: en "Nueva cotización" era el correo del
       cliente, en la Calculadora el selector de unidades y en el catálogo el
       segmentado de vista. Se veían enteros y no respondían al toque, que es
       la peor forma de estar roto.

       Cuesta 8px menos que el hueco del dock y sirve para algo que se ve. */
    padding: 16px 16px calc(88px + env(safe-area-inset-bottom, 0px));
    scrollbar-gutter: auto;
    overflow: visible;
  }

  body.theme-ises .section { max-width: none; }
  body.theme-ises #sectionNueva { max-width: none; }

  /* Métricas trae su propio cascarón (cabecera y barra de estado de canto a
     canto, con sus paddings) y en móvil se salía del contenido con un margen
     negativo. El diseño la trata como una sección más: mismo arranque a 16px
     que las otras seis, título y botones apilados a la izquierda. */
  body.theme-ises #sectionMetricas.active { margin: 0; }
  /* El `margin-inline: auto` que centra los bloques dentro de los 1500px del
     escritorio dejaba los botones flotando en mitad de la pantalla. */
  body.theme-ises .mx-body,
  body.theme-ises .mx-topbar > * { max-width: none; margin-inline: 0; }
  body.theme-ises .mx-topbar {
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
    padding: 0;
    border-bottom: 0;
  }
  body.theme-ises .mx-body { padding: 16px 0 0; }

  /* La barra de estado del sistema pasa de banda a tarjeta, dentro del ancho
     del contenido. El hueco apretado deja que sus cinco datos caigan de dos en
     dos en vez de en cinco renglones, sin ocultar ninguno. */
  body.theme-ises .mx-statusline {
    gap: 4px 14px;
    margin-top: 14px;
    padding: 12px 14px;
    border: 1px solid var(--i-brd-08);
    border-radius: 14px;
    font-size: 11px;
  }
  body.theme-ises .mx-status-right { margin-left: 0; }

  /* Métricas no usa .section-header sino su propia cabecera: se le da el
     mismo trato que a las demás, ahora que la descripción se muestra. */
  body.theme-ises .mx-topbar > div > p { display: block; font-size: 12px; }

  /* --- Navegación: el ☰, y nada más ------------------------------------
     NO HAY BARRA ABAJO. El dock horizontal repartía siete botones en 392px
     —unos 50px cada uno, por debajo del piso táctil de 44 que este mismo
     bloque declara más abajo— y encima competía con los seis círculos de la
     cabecera y con el buscador de secciones, que era el único de los tres que
     daba los nombres. Ahora la navegación del teléfono es UNA: el ☰ de la
     cabecera abre el menú de secciones (css/menu-secciones.css), con las trece
     agrupadas por familia, con su descripción y su buscador. De paso se
     recuperan los 96px de hueco inferior en todas las pantallas.

     El patrón no es nuevo: es el que el Asistente ya usaba para sí mismo
     (`body.seccion-asistente .rail { display: none }`), subido a toda la app.

     El riel NO se puede esconder entero: dentro vive .rail-session, que es el
     ÚNICO cerrar sesión de la app y que en teléfono va fijo en la esquina de
     la cabecera. Así que se vacía el contenedor y se apagan sus hijos uno a
     uno. Los .tab-btn siguen en el DOM y eso es load-bearing: el menú los lee
     para saber qué secciones existen y navega haciendo click() sobre ellos
     (js/menu-secciones.js). */
  body.theme-ises .rail {
    position: fixed;
    top: 0;
    right: 0;
    left: auto;
    bottom: auto;
    transform: none;
    z-index: 6;
    width: 0;
    height: 0;
    margin: 0;
    padding: 0;
    gap: 0;
    border: 0;
    border-radius: 0;
    background: transparent;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    overflow: visible;
    pointer-events: none;
  }

  body.theme-ises .rail-nav,
  body.theme-ises .rail > picture,
  body.theme-ises .rail-logo-btn,
  body.theme-ises .rail-spacer { display: none; }

  /* El tooltip lateral del riel no tiene sentido sin riel, y .rail-logout lo
     hereda estando ya en la cabecera. */
  body.theme-ises .rail .rail-logout::after { display: none; }

  /* Aquí vive el ÚNICO cerrar sesión de la app: se mueve a la esquina de la
     cabecera en vez de ocultarlo con el resto del riel, que dejaría al usuario
     sin poder salir. */
  body.theme-ises .rail-session {
    position: fixed;
    top: 12px;
    right: 16px;
    z-index: 6;
    width: auto;
    flex-direction: row;
    align-items: center;
    gap: 6px;
    padding: 0;
    border-top: 0;
    pointer-events: auto;
  }
  /* La inicial del usuario es decoración: el diseño no lleva sesión en la
     cabecera y ese hueco lo necesitan los botones que sí hacen algo. Cerrar
     sesión SÍ se queda —es el único de la app— con la misma forma de vidrio
     que los otros dos. */
  body.theme-ises .rail-avatar,
  body.theme-ises .rail-role { display: none; }

  /* --- Cabecera móvil: [☰] [marca ISES] ····· [apariencia] [salir] ---------
     Cuatro controles y ni uno más. Los seis círculos de secciones que había
     aquí (calculadora, métricas, facturas, mensajes, leads, usuarios) se
     fueron al menú del ☰, donde salen con su nombre; lo que queda son las dos
     cosas que NO son navegación —la apariencia y cerrar sesión— más la marca,
     que sigue siendo el camino al Asistente.

     Los dos de la derecha viven en otras ramas del DOM y van fijos en la
     esquina; su hueco lo reserva el padding derecho de .ises-nav. */

  /* El ☰: mismo vidrio de 40px que los otros dos, a la izquierda del todo. */
  body.theme-ises .ises-nav-menu {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 40px;
    height: 40px;
    padding: 0;
    cursor: pointer;
  }
  body.theme-ises .ises-nav-menu svg { width: 20px; height: 20px; }

  /* La apariencia es hermana de .ises-nav dentro de .statusbar, que en móvil se
     queda en el flujo del contenido: se fija en la esquina con el mismo truco
     que .rail-session, a la izquierda del botón de salir.

     El selector lleva .statusbar delante a propósito. css/ajustes-ises.css se
     enlaza DESPUÉS de esta hoja y define `body.theme-ises .ises-ajustes-btn`
     con su ancho, su radio y su fondo; con la misma especificidad ganaría él
     por orden. Un descendiente más y este bloque manda, sin !important. */
  body.theme-ises .statusbar .ises-ajustes-btn {
    position: fixed;
    top: 12px;
    right: 62px;
    z-index: 6;
    width: 40px;
    height: 40px;
    padding: 0;
    /* Dentro del Asistente la .statusbar va con `pointer-events: none` (es un
       hueco que no debe robarle clics al logo de al lado); sin esto el engrane
       heredaría el none y sería el único botón muerto de la cabecera. */
    pointer-events: auto;
  }
  body.theme-ises .statusbar .ises-ajustes-btn svg { width: 18px; height: 18px; }

  /* El diseño pone en la cabecera el ISOTIPO solo (el ojo en el rombo), no el
     logo completo: `Imagenes/logo-132.png` mete además la palabra "I S E S"
     debajo, y a 26px eso se convierte en un borrón ilegible justo al lado de
     la palabra ISES escrita en texto. Se cambia por el isotipo que ya existe
     en el repo (el mismo que va en las bandas negras del PDF) sin tocar el
     HTML: se apaga el <img> del <picture> y el isotipo entra como fondo. */
  body.theme-ises .ises-nav-logo {
    width: 28px;
    height: 28px;
    border: 0;
    border-radius: 0;
    background: url("../Imagenes/isotipo_blanco.png") center / 28px auto no-repeat;
    /* En teléfono este logo es el ÚNICO acceso al Asistente (el menú del ☰ no
       lo lista, porque el Asistente es la pantalla de inicio): tiene que
       parecer lo que es, un botón. */
    cursor: pointer;
  }
  body.theme-ises .ises-nav-logo img { display: none; }
  /* La vuelta de 360° es un efecto de puntero: en táctil no se dispara y con
     el logo ya sin caja se vería como un tirón. */
  body.theme-ises .ises-nav-logo,
  body.theme-ises .ises-nav-logo:hover { transform: none; }

  body.theme-ises .ises-nav-marca {
    display: inline-block;
    margin-left: 10px;
    margin-right: auto;
    font-family: var(--font-display);
    font-size: 19px;
    font-weight: 700;
    line-height: 1;
    letter-spacing: 0.01em;
    color: var(--i-txt-1);
  }

  /* FUERA LAS PASTILLAS. Sus destinos están en el menú del ☰ con nombre y
     descripción; aquí eran seis círculos sin etiqueta, una segunda navegación
     compitiendo con la primera. Se oculta el contenedor y no una por una: así
     una pastilla nueva tampoco reaparece sola en teléfono.
     Siguen en el DOM y su MutationObserver (js/tema-ises.js) sigue corriendo:
     es barato y evita tocar ese archivo por un cambio de presentación. */
  body.theme-ises .ises-pills { display: none; }

  /* Vidrio de la misma receta que tenía el dock, en versión pequeña, para los
     tres botones que quedan en la cabecera. El box-shadow es INTERIOR (el
     realce de arriba), no una sombra proyectada. */
  body.theme-ises .ises-nav-menu,
  body.theme-ises .statusbar .ises-ajustes-btn,
  body.theme-ises .rail-logout {
    width: 40px;
    height: 40px;
    min-width: 0;
    flex: 0 0 auto;
    padding: 0;
    border: 1px solid rgb(var(--velo-rgb) / 0.12);
    border-radius: 9999px;
    background: linear-gradient(
      180deg,
      rgb(var(--velo-rgb) / 0.1) 0%,
      rgb(var(--velo-rgb) / 0.04) 100%
    );
    box-shadow: inset 0 1px 0 rgb(var(--velo-rgb) / 0.22);
    backdrop-filter: blur(20px) saturate(160%);
    -webkit-backdrop-filter: blur(20px) saturate(160%);
    color: var(--i-txt-1);
  }
  body.theme-ises .ises-nav-menu:hover,
  body.theme-ises .statusbar .ises-ajustes-btn:hover,
  body.theme-ises .rail-logout:hover {
    background: rgb(var(--velo-rgb) / 0.1);
    color: var(--i-txt-1);
  }
  body.theme-ises .rail-logout:hover { color: var(--danger); }

  /* Mientras el menú está abierto, su ☰ se queda encendido. */
  body.theme-ises .ises-nav-menu[aria-expanded="true"] {
    background: rgb(var(--velo-rgb) / 0.16);
    color: var(--i-txt-1);
  }

  /* --- Indicadores de diagnóstico ---------------------------------------
     "En línea", el estado de conexión y el usuario se van: son diagnóstico, no
     trabajo, y en dos filas de 44px se comían el 12% del alto útil de cada
     pantalla. No se pierde nada: el estado de conexión sigue avisando por su
     cuenta cuando algo falla. Con esto la .statusbar se queda sin un solo hijo
     en el flujo, que es por lo que arriba va a padding cero. */
  body.theme-ises .status-online,
  body.theme-ises .status-item,
  body.theme-ises .status-pill { display: none; }

  /* --- Controles: piso táctil de 44px -----------------------------------
     El diseño sube TODO control a 44px por debajo del umbral. Y los campos
     van a 16px de fuente: por debajo de eso iOS hace zoom al enfocarlos. La
     hoja base ya lo hacía a ≤720px y el tema lo estaba pisando. */
  /* Los campos miden 44px CLAVADOS, no "mínimo 44". Con altura automática el
     interlineado de 1.6 del cuerpo los estiraba a 47.6 y todo el formulario
     iba quedando descuadrado renglón a renglón. */
  body.theme-ises input,
  body.theme-ises select {
    min-height: 44px;
    height: 44px;
    padding: 0 12px;
    font-size: 16px;
  }
  body.theme-ises textarea {
    min-height: 160px;
    height: auto;
    padding: 10px 12px;
    font-size: 16px;
  }
  body.theme-ises input[type="checkbox"],
  body.theme-ises input[type="radio"] { min-height: 0; height: auto; }

  /* --- Pantalla de acceso ------------------------------------------------
     El centrado, el relleno de la tarjeta y el hueco del icono se arreglan
     para TODOS los anchos (bloque 12); aquí solo se aprieta el margen, que a
     393px de ancho se comía 72px de los 393. */
  body.theme-ises .login-panel { padding: 24px 16px; }
  body.theme-ises .login-card { padding: 22px 18px; }

  /* Mismo criterio que la cabecera: el ISOTIPO solo. `logo-132.png` lleva la
     palabra "I S E S" bajo el ojo y al lado va "ISES" otra vez, escrito en
     texto; a 44px eso son tres veces el nombre en dos dedos de pantalla. Se
     cambia sin tocar el HTML: se apaga el <img> y el isotipo entra como fondo
     del <picture> que ya lo envuelve. */
  body.theme-ises .login-brand picture {
    display: block;
    width: 44px;
    height: 44px;
    flex: 0 0 auto;
    background: url("../Imagenes/isotipo_blanco.png") center / 40px auto no-repeat;
  }
  body.theme-ises .login-brand .login-logo { display: none; }

  body.theme-ises .btn {
    min-height: 44px;
    height: auto;
    padding: 0 16px;
    border-radius: 12px;
  }
  body.theme-ises .btn.compact,
  body.theme-ises .btn.icon { min-height: 44px; height: auto; padding: 0 14px; }
  body.theme-ises .btn.icon { width: 44px; padding: 0; }

  /* El `.segmented` de la hoja base trae `height: 30px` fijo, así que subir el
     botón sin soltar el contenedor solo lo desbordaba. */
  body.theme-ises .segmented {
    height: auto;
    gap: 4px;
    padding: 4px;
    border-radius: 12px;
  }
  /* 44px medidos en el diseño, no los 36 que declara: dentro de su caja los
     botones se estiran a la altura de la fila. */
  body.theme-ises .seg-btn {
    height: auto;
    min-height: 44px;
    padding: 0 16px;
    border-radius: 9px;
    font-size: 12px;
  }

  /* Estos NO son controles: no suben al piso táctil de 44px (su altura la fija
     `pill()` del diseño, más abajo). */
  body.theme-ises .status-badge,
  body.theme-ises .type-badge { min-height: 0; }

  /* --- Rejillas ---------------------------------------------------------- */
  body.theme-ises .quote-cols,
  body.theme-ises .quote-foot,
  body.theme-ises .catalogo-layout,
  body.theme-ises .cat-grid { grid-template-columns: minmax(0, 1fr); }

  /* Empresa y Teléfono se quedan en dos columnas: el diseño las mantiene
     lado a lado también en móvil (151px + 151px a 390 de ancho) y son campos
     lo bastante cortos para leerse. La hoja base las apilaba. */
  body.theme-ises #sectionNueva .grid.two {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 12px;
  }

  /* La barra de folio pasa a 2x2 en vez de recortar el número. Las divisiones
     se rehacen: la de la izquierda de la fila 2 sobra, y hace falta una entre
     Vigencia y el estado, que es un <span> y no admite el borde por sí mismo. */
  body.theme-ises .quote-head { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  body.theme-ises .quote-head .qh-item:nth-child(3) {
    border-left: 0;
    border-right: 1px solid var(--i-brd-08);
  }
  /* La raya que parte las dos filas se dibuja DEBAJO de las dos primeras
     celdas, no encima de las dos últimas: la cuarta celda es el `<span>` de la
     insignia, que no ocupa su casilla entera, y con el borde arriba la línea
     se cortaba a media rejilla. */
  body.theme-ises .quote-head .qh-item:nth-child(-n + 2) {
    border-bottom: 1px solid var(--i-brd-08);
  }
  body.theme-ises .quote-head .qh-badge {
    place-self: center end;
    margin: 0 16px 0 0;
  }

  /* Los KPI se quedan en dos columnas, no en una. */
  body.theme-ises #metricsCards,
  body.theme-ises .mx-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }

  /* El `catGrid` del diseño baja el mínimo a 150px en móvil, y con eso entran
     dos tarjetas por fila en vez de una sola muy ancha.

     El selector lleva `[data-cat-cols]` para poder ganarle a la preferencia de
     columnas del configurador. No basta con ir después: la preferencia se
     escribe como `body.theme-ises[data-cat-cols="4"] .cat-cards`, y el
     atributo le suma un escalón de especificidad que un media query no
     compensa por sí solo. Y tiene que ganar: cuatro columnas en 375px salen a
     84px de ancho, que no es la preferencia de nadie. */
  body.theme-ises .cat-cards,
  body.theme-ises[data-cat-cols] .cat-cards { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }

  /* --- Barra de herramientas del catálogo --------------------------------
     El diseño la sube entera al piso táctil: el buscador ocupa su propia fila
     y los dos selectores se reparten la siguiente. Y la envuelve en una caja
     de vidrio (`catFilterBar`), que es lo que la separa de las tarjetas. */
  body.theme-ises .catalogo-toolbar {
    gap: 8px;
    padding: 8px;
    border: 1px solid var(--i-brd-08);
    border-radius: 14px;
    background: var(--i-glass-bg);
    backdrop-filter: var(--i-glass-blur);
    -webkit-backdrop-filter: var(--i-glass-blur);
  }
  body.theme-ises .catalogo-toolbar .search-field { flex: 1 1 100%; min-width: 0; }
  body.theme-ises .catalogo-toolbar .search-field input,
  body.theme-ises .catalogo-toolbar .cat-filtro { border-radius: 12px; }
  body.theme-ises .cat-toolbar-end,
  body.theme-ises .catalogo-toolbar > .segmented { width: 100%; }
  body.theme-ises .catalogo-toolbar > .segmented .seg-btn { flex: 1 1 0; min-width: 0; }

  /* --- Categorías: una tira horizontal, no una barra lateral -------------
     En Productos había 1186px de scroll antes del primer producto, y 439 de
     ellos eran este aside: la marca decorativa, la lista vertical con su
     propio scroll de 192px, los chips y el valor del inventario. Una barra
     lateral puesta encima del contenido es el patrón de escritorio girado
     90°, no un patrón móvil.

     Las categorías pasan a una tira horizontal de chips, que es lo que se
     espera al filtrar en un teléfono: se ven las primeras, se arrastra para
     ver el resto, y el catálogo empieza 300px más arriba. */
  body.theme-ises .cat-side {
    position: static;
    max-height: none;
    overflow: visible;
    padding: 12px;
  }

  /* La marca del aside (cuadro azul + "ISES") es decoración: el logo ya está
     en la cabecera fija, dos dedos más arriba. La palabra "Categorías" se va
     con ella: es la cabecera del aside, y el diseño la manda a display:none
     en cuanto la lista deja de ser vertical. */
  body.theme-ises .cat-side-brand,
  body.theme-ises .cat-side-label { display: none; }

  /* El "00 · CATEGORÍA" numera una barra lateral que aquí ya no existe. */
  body.theme-ises .cat-head-eyebrow { display: none; }

  /* El efecto de línea (LineSidebar) reacciona a la CERCANÍA DEL CURSOR, que
     en una pantalla táctil no existe. Se apaga por sus propias variables en
     vez de pelearse con las reglas de la hoja base: sin hueco para la raya,
     sin corrimiento del texto y sin separación vertical entre ítems. */
  body.theme-ises .cat-cats {
    --marca-largo: 0px;
    --marca-hueco: 0px;
    --desplaza: 0px;
    --hueco: 0px;
    flex-direction: row;
    flex-wrap: nowrap;
    max-height: none;
    gap: 6px;
    margin-bottom: 12px;
    padding: 2px 0;
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
  }
  body.theme-ises .cat-cats::-webkit-scrollbar { display: none; }
  body.theme-ises .cat-cats li { flex: 0 0 auto; }
  body.theme-ises .cat-cat::after { display: none; }

  body.theme-ises .cat-cat {
    width: auto;
    min-height: 36px;
    align-items: center;
    gap: 7px;
    padding: 0 14px;
    border: 1px solid var(--i-brd-08);
    border-radius: 999px;
    font-size: 12px;
    white-space: nowrap;
  }
  /* En la tira el chip activo se marca con vidrio, no con azul: el azul es el
     acento de la app y en una fila de chips compite con el contenido. Son los
     valores del `categorias[].style` del diseño. */
  body.theme-ises .cat-cat.is-active {
    border-color: rgb(var(--velo-rgb) / 0.2);
    background: rgb(var(--velo-rgb) / 0.1);
    color: var(--i-txt-1);
    font-weight: 500;
  }
  /* El nombre y el conteo se tiñen de azul solos: los pinta un `color-mix`
     que mezcla el acento según `--efecto`, la variable del efecto de línea
     que aquí ya está apagado. Se pone a cero también su tinta. */
  body.theme-ises .cat-cat.is-active .cat-cat-label,
  body.theme-ises .cat-cat.is-active .cat-cat-count {
    --efecto: 0;
    color: var(--i-txt-1);
  }
  body.theme-ises .cat-cat.is-active .cat-cat-count { color: var(--i-txt-3); }
  /* El conteo deja de ser un superíndice y se pone al lado del nombre, en
     mono, como el `countStyle` del diseño. */
  body.theme-ises .cat-cat-count { align-self: center; font-size: 10px; }

  /* El valor del inventario son tres líneas —etiqueta, cifra y "MXN · N
     SKUs"— para un solo dato: 97px. En una sola línea se lee igual. */
  body.theme-ises .cat-valor {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 2px 8px;
    padding: 10px 12px;
  }

  /* Marca y Subcategoría se quedaban una debajo de otra por 5px: cada select
     mide 170 y en la fila caben 343. Con `flex-basis` a la mitad entran las
     dos. Solo los filtros que cuelgan DIRECTAMENTE de la barra: el de orden
     va dentro de .cat-toolbar-end, que ya comparte fila con la vista. */
  body.theme-ises .catalogo-toolbar > .cat-filtro {
    flex: 1 1 calc(50% - 4px);
    min-width: 0;
  }
  /* El "00 / 01 / 02" numera una lista; en una tira de chips no hay lista que
     numerar y el conteo de productos sí sigue sirviendo. */
  body.theme-ises .cat-cat-num { display: none; }
  body.theme-ises .cat-cat-label { align-items: center; }

  /* --- Tablas: la etiqueta va ARRIBA del valor, no al lado --------------
     La hoja base ya convierte .data-cards en tarjetas con la etiqueta a la
     izquierda; el diseño apila etiqueta sobre valor. */

  /* Una celda por renglón, etiqueta arriba y valor debajo, separadas por una
     raya de 5% de blanco. Es el `cell` del diseño, tal cual.

     Hubo un intento de partir estas tarjetas en dos columnas para acortarlas
     (una fila de Guardadas tiene ocho campos y medía 553px). Se retira: el
     diseño las quiere a una columna y esa es la referencia. */
  body.theme-ises table.data-cards td {
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-start;
    gap: 2px;
    padding: 8px 14px;
    text-align: left;
    border-bottom: 1px solid rgb(var(--velo-rgb) / 0.05);
  }

  /* --- Una sola caja por registro ---------------------------------------
     Las tablas viven dentro de `<section class="panel">`, que en escritorio
     es LA tarjeta. En móvil la hoja base convierte además cada `<tr>` en su
     propia tarjeta: quedaban dos marcos concéntricos, con el contenido a
     36px del borde de la pantalla. El diseño tiene una sola caja. Se apaga la
     de fuera —no la del registro—, y solo cuando ese panel contiene una tabla
     de tarjetas: los demás paneles siguen siendo tarjetas de pleno derecho. */
  body.theme-ises .panel:has(> .table-wrap > table.data-cards) {
    padding: 0;
    border: 0;
    border-radius: 0;
    background: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }

  /* El relleno lateral pasa del `<td>` al `<tr>` para que las rayas de
     separación queden metidas 16px, como en el diseño, en vez de cruzar la
     tarjeta de canto a canto. `.concepts-table` queda fuera: sus celdas
     llevan el buscador y los steppers y necesitan el ancho completo. */
  body.theme-ises table.data-cards:not(.concepts-table):not(.items-table) tr {
    padding: 0 16px;
  }
  body.theme-ises table.data-cards:not(.concepts-table):not(.items-table) td {
    padding: 8px 0;
  }

  /* Total y Estado son los dos `cellR` del diseño: etiqueta a la izquierda y
     valor a la derecha, en la misma línea. */
  body.theme-ises table.data-cards td[data-label="Total"],
  body.theme-ises table.data-cards td[data-label="Estado"] {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
  }

  /* La píldora de estado sube a 28px, la altura que le da `pill()` en móvil. */
  body.theme-ises .status-badge,
  body.theme-ises .type-badge { height: 28px; padding: 0 12px; }

  /* Los renglones de la cotización van aparte porque su reparto no es el
     mismo: el concepto lleva el buscador de producto y el botón de quitar va
     solo, así que esos dos ocupan la fila entera; pero cantidad, precio,
     stock y total son campos cortos —los steppers miden 64 y 96px— y se
     emparejan de dos en dos. Un renglón pasa de 376px a poco más de 200, que
     con cinco conceptos es media pantalla menos de arrastre por concepto. */
  body.theme-ises table.concepts-table tr {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  body.theme-ises table.concepts-table td.concept-cell,
  body.theme-ises table.concepts-table td.empty,
  body.theme-ises table.concepts-table td:not([data-label]) {
    grid-column: 1 / -1;
  }

  /* Cantidad y precio traen un ancho fijo (64 y 96px) heredado de la tabla de
     escritorio, donde la columna es estrecha. Aquí la media columna da 122 y
     el precio ya no queda pegado a las flechas del stepper. */
  body.theme-ises .concepts-table .stepper,
  body.theme-ises .concepts-table .col-qty input,
  body.theme-ises .concepts-table .col-price input { width: 100%; }
  body.theme-ises table.data-cards td::before {
    font-size: 10px;
    font-weight: 500;
    letter-spacing: 0.02em;
    text-transform: none;
    color: var(--i-txt-5);
  }
  /* El mensaje del lead deja de recortarse a una línea. */
  body.theme-ises .lead-mensaje,
  body.theme-ises .description-short { white-space: normal; overflow: visible; }

  /* --- Tipografía -------------------------------------------------------
     El diseño NO agranda los títulos en móvil: los deja como el escritorio
     original (26/14). Los 32px de la versión de escritorio son demasiado
     para 390px de ancho. */
  body.theme-ises h1 { font-size: 22px; }
  body.theme-ises h2 { font-size: 14px; }
  body.theme-ises h3 { font-size: 13px; }

  /* El párrafo bajo cada título SE QUEDA: el diseño lo pinta en móvil, debajo
     del h1 y a 12px. Estuvo oculto un tiempo para ganar altura; se repone
     porque la referencia manda. */
  body.theme-ises .section-header p { display: block; font-size: 12px; }

  /* `align-items: flex-start` es imprescindible: al pasar la cabecera a
     columna, el valor por defecto (stretch) estiraba los botones a todo el
     ancho. El diseño los deja a su tamaño natural. */
  body.theme-ises .section-header {
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
    margin-bottom: 14px;
  }
  /* Los dos envoltorios de acciones que existen en el HTML: `.section-actions`
     (Nueva cotización) y `.header-actions` (Productos). Los botones a su
     tamaño natural y envolviendo, nunca estirados. */
  body.theme-ises .section-actions,
  body.theme-ises .header-actions { width: auto; flex-wrap: wrap; gap: 8px; }
  body.theme-ises .section-actions .btn,
  body.theme-ises .header-actions .btn { flex: 0 0 auto; padding: 0 13px; font-size: 13px; }

  /* --- Acciones de "Productos y servicios" -------------------------------
     El diseño las parte en dos: los secundarios juntos en una caja con borde
     que ocupa la fila, y el botón azul solo, debajo, a todo el ancho. */
  body.theme-ises .header-actions { width: 100%; }
  body.theme-ises .ha-grupo {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    width: 100%;
    padding: 4px;
    border: 1px solid var(--i-brd-08);
    border-radius: 12px;
    background: var(--i-sup-2);
  }
  body.theme-ises .ha-grupo .btn {
    flex: 1 1 auto;
    min-width: 0;
    border: 0;
    background: transparent;
  }
  body.theme-ises .ha-grupo .btn:hover { background: var(--i-sup-4); }
  body.theme-ises .header-actions > .btn.primary { flex: 1 1 100%; }

  /* Las siete acciones de una cotización guardada (Ver, PDF, WhatsApp,
     Editar, Borrar, Aprobar, Rechazar) ocupaban 179px en cuatro filas, y la
     hoja base les pone `flex: 1 1 auto`, que estira al último de cada fila:
     "Rechazar" medía 271px y parecía el botón principal de la tarjeta.

     A tamaño natural caben en dos filas. La altura se queda en los 44px del
     piso táctil —lo que se recorta es el relleno y el cuerpo de letra, no el
     área que se toca— y ninguna acción se esconde detrás de un menú. */
  body.theme-ises .saved-actions { gap: 6px; }
  body.theme-ises table.data-cards td[data-label="Acciones"] .saved-actions .btn {
    flex: 0 0 auto;
    padding: 0 12px;
    font-size: 12px;
  }

  /* Ya no hay barra de navegación debajo que esquivar: baja al borde, con el
     margen de la barra de gestos.

     EL `:not(.movido)` ES LO QUE IMPIDE QUE SE ESTIRE, y no es un detalle de
     estilo. Cuando el usuario arrastra el botón, js/asistente-arrastrable.js le
     escribe `left`/`top` inline y css/asistente.css le pone `right: auto;
     bottom: auto` para que la caja se encoja al tamaño del botón. Pero esta
     hoja se enlaza DESPUÉS y con la misma especificidad ganaba por orden: con
     `top` y `bottom` puestos a la vez, un `position: fixed` se ESTIRA entre los
     dos. La caja pasaba a medir 56 × 675 px —invisible, sin fondo, pero
     capturando el puntero— y se comía los clics de toda la franja derecha:
     el zoom del mapa del Diseñador, los steppers de la Calculadora y el
     segmentado del catálogo dejaban de responder sin que nada se viera mal.

     Es la clase de encime que no se ve en una captura y solo aparece midiendo
     con elementFromPoint. */
  body.theme-ises .asistente-wrap:not(.movido) {
    bottom: calc(24px + env(safe-area-inset-bottom, 0px));
  }

  /* El Diseñador no está en el diseño móvil: es extrapolación. Su cabecera es
     un flex de cuatro bloques sin envolver, y a 390px el del título se
     comprimía a 0 de ancho: el h1 y su descripción salían partidos palabra
     por palabra, con los controles encima. En columna cada bloque recupera
     el ancho completo. */
  body.theme-ises .dz2-topbar {
    flex-direction: column;
    align-items: stretch;
    gap: 12px;
  }
  body.theme-ises .dz2-heading,
  body.theme-ises .dz2-search,
  body.theme-ises .dz2-project,
  body.theme-ises .dz2-topactions {
    flex: 0 0 auto;
    width: 100%;
    min-width: 0;
  }
  body.theme-ises .dz2-topactions { flex-wrap: wrap; }
  body.theme-ises #sectionDisenador { overflow-x: hidden; }
}

/* Por encima del umbral móvil pero en pantalla estrecha, el catálogo agradece
   una columna menos. Nada más: el diseño trata todo esto como escritorio. */
@media (min-width: 760px) and (max-width: 1100px) {
  body.theme-ises .cat-grid { grid-template-columns: 220px minmax(0, 1fr); }
}


/* ==========================================================================
   18. REDUCCIÓN DE MOVIMIENTO
   ==========================================================================
   No se apaga el fondo aquí: js/tema-ises.js ya lo usa como valor por defecto
   y así el usuario puede elegir otra cosa si quiere. Lo que se apaga es lo que
   no se puede desactivar desde la interfaz.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  body.theme-ises .ia-panel h2 { animation: none; }
  body.theme-ises .rail-logo,
  body.theme-ises .tab-btn,
  body.theme-ises .tab-btn svg,
  body.theme-ises .ises-pill-blob,
  body.theme-ises .ises-pill-txt,
  body.theme-ises .ises-pill-dot { transition: none; }
  body.theme-ises .rail-logo:hover { transform: none; }
}

/* El interruptor del configurador. Va aparte de la media query y es más
   ancho que ella: aquí el usuario lo pidió explícitamente, así que se apaga
   TODO, incluida la magnificación del dock, y no solo lo que no tiene control
   propio en la interfaz. El fondo lo apaga js/ajustes-ises.js, que además
   recuerda cuál estaba puesto para devolverlo al desactivar el interruptor.

   `!important` x2 (del presupuesto de 4 de esta hoja): las animaciones que
   se apagan están declaradas en selectores más específicos repartidos por
   cinco hojas, y perseguirlas una a una sería justo lo contrario de un
   interruptor. */
body.theme-ises.mov-reducido *,
body.theme-ises.mov-reducido *::before,
body.theme-ises.mov-reducido *::after {
  animation-duration: 0.001ms !important;
  transition-duration: 0.001ms !important;
  scroll-behavior: auto;
}


/* ==========================================================================
   19. VENTANAS EMERGENTES: CAPA Y ENCUADRE
   ==========================================================================
   Va al final de la hoja a propósito: aquí hay reglas que tienen que ganarle
   al bloque 17 (RESPONSIVE), y entre selectores de la misma especificidad
   manda el orden, no la media query.
   ========================================================================== */

/* --- 19.1 La capa ---------------------------------------------------------
   Un `z-index: 50` no significa "por encima de todo": significa "por encima
   de mis hermanos DENTRO del contexto apilado que me toque". El tema abre dos
   contextos —`.app-shell` y `.content`, los dos con `z-index: 1` para quedar
   sobre el canvas del fondo— y los modales del catálogo vivían dentro del
   segundo. Medido con elementsFromPoint sobre un modal abierto, encima del
   modal se pintaban: el dock, la barra superior y el botón del asistente.

   Los modales ya salieron al <body> (ver index.html). Falta la escala, porque
   el asistente flota FUERA del cascarón con z-index 950 y le ganaba incluso a
   los modales que ya colgaban del <body> con 50:

     950/960  asistente (botón / panel)  ← lo de fuera del cascarón
     1000     modales
     1100     toasts                      ← el aviso siempre se lee

   El alta de producto era la excepción: como cajón NO se podía sacar del
   cascarón (era la columna derecha de la rejilla del catálogo), y había que
   subir `.content` entero y apagar el asistente mientras estaba abierto. Desde
   que es #productoModal entra por la escala de arriba como cualquier otro
   modal y esas dos excepciones se borraron. */
body.theme-ises .modal { z-index: 1000; }
body.theme-ises .toast-stack { z-index: 1100; }

/* Al salir del <section>, estos dos modales dejaron de estar dentro del
   alcance de `#sectionProductos .catalogo-toolbar` de la hoja base. Se repone
   lo que esa regla les daba, ahora por su propia clase. */
body.theme-ises .clasificacion-toolbar { flex-wrap: wrap; row-gap: 8px; }
body.theme-ises .clasificacion-toolbar .search-field { flex: 1 1 200px; }


/* --- 19.2 El marco --------------------------------------------------------
   Antes el que se estiraba era el modal entero y el que se desplazaba era el
   VELO: un modal alto se salía de la pantalla por abajo, el botón de cerrar
   se iba con él y arriba se metía debajo de la barra. Ahora el marco se queda
   quieto, nunca más alto que la ventana, y lo que rueda es `.modal-cuerpo`.

   El botón de cerrar queda fuera de ese hijo, así que se queda clavado en la
   esquina pase lo que pase con el desplazamiento. */
body.theme-ises .modal-content:has(> .modal-cuerpo) {
  display: flex;
  flex-direction: column;
  padding: 0;
  /* Recorta el hijo que rueda contra el radio del marco. */
  overflow: hidden;
}

body.theme-ises .modal-cuerpo {
  /* Sin esto un hijo flex NUNCA se encoge por debajo de su contenido y el
     `overflow-y` no llega a activarse nunca. */
  min-height: 0;
  overflow-y: auto;
  /* La rueda del ratón deja de arrastrar la página de detrás al llegar al
     final del modal. */
  overscroll-behavior: contain;
  padding: 30px 34px;
}

body.theme-ises .modal-cuerpo > h2:first-child { margin-top: 0; }

body.theme-ises .modal-close { z-index: 2; }


/* --- 19.3 Escritorio ------------------------------------------------------ */
@media (min-width: 760px) {
  body.theme-ises .modal {
    align-items: center;
    justify-content: center;
    /* El hueco de la izquierda es el del dock (fijo en 14 + 64 = 78px) más el
       mismo aire que `.content` deja a su contenido. Sin él, el modal se
       centraba en la ventana y quedaba montado sobre el dock: encuadrado
       respecto de la pantalla, descuadrado respecto de la app. */
    padding: 28px 28px 28px 106px;
    /* Ya no se desplaza el velo: se desplaza el cuerpo del modal. */
    overflow: hidden;
  }

  /* `100%` es el alto del velo YA sin su relleno, así que el modal nunca se
     sale por arriba ni por abajo. Los anchos se recortan contra el mismo
     hueco: `96vw` de la hoja base ignoraba el relleno y pegaba el modal a los
     dos cantos de la pantalla. */
  body.theme-ises .modal-content { max-height: 100%; width: min(820px, 100%); }
  body.theme-ises .recetas-modal-content { width: min(1240px, 100%); }
  body.theme-ises .lexico-modal-content { width: min(1040px, 100%); }

  /* Recetas es de dos columnas y las dos crecen: la lista de familias por
     arriba y el detalle de una familia por abajo. Con el marco ya acotado, la
     rejilla ocupa el alto que sobra y cada columna se desplaza por su cuenta,
     en vez de empujar el modal fuera de la pantalla. */
  body.theme-ises .recetas-modal-content .modal-cuerpo {
    display: flex;
    flex-direction: column;
  }
  /* En columna flex los márgenes ya no se funden entre hermanos: el hueco
     entre el título y su pie de texto se sumaría dos veces. */
  body.theme-ises .recetas-modal-content .modal-cuerpo > .panel-hint { margin-top: 0; }

  body.theme-ises .recetas-layout {
    flex: 1;
    min-height: 0;
    align-items: stretch;
  }
  body.theme-ises .recetas-lista,
  body.theme-ises .recetas-detalle {
    min-height: 0;
    max-height: 100%;
    overflow-y: auto;
  }
}


/* --- 19.4 Móvil -----------------------------------------------------------
   El corte es el 759 del diseño, no el 720 de la hoja base: entre esos dos
   anchos el tema ya pinta la interfaz móvil (cabecera fija de 64px y dock
   abajo) y los modales seguían con el encuadre de escritorio.

   Los centrados (`.modal-center`: confirmar y buscador de conceptos) se
   quedan como están: son cortos y en el centro se leen mejor. */
@media (max-width: 759px) {
  body.theme-ises .modal:not(.modal-center) {
    align-items: flex-end;
    justify-content: center;
    padding: 0;
    overflow: hidden;
  }

  body.theme-ises .modal:not(.modal-center) .modal-content {
    width: 100%;
    max-width: none;
    /* dvh y no vh: la barra del navegador entra y sale. El 8% de arriba deja
       ver que hay app detrás y da dónde tocar para cerrar. */
    max-height: 92dvh;
    border-radius: var(--radius) var(--radius) 0 0;
  }

  body.theme-ises .modal-cuerpo {
    /* env() respeta la barra de gestos: sin ella el último botón del formulario
       queda debajo de la raya del iPhone. */
    padding: 24px 16px calc(28px + env(safe-area-inset-bottom, 0px));
  }

  /* Los centrados sí se quedan centrados, pero como TARJETA: la hoja base les
     daba ancho completo y solo las esquinas de arriba redondeadas —la forma de
     una hoja inferior— flotando en mitad de la pantalla. */
  body.theme-ises .modal.modal-center { padding: 16px; }
  body.theme-ises .modal.modal-center .modal-content {
    width: 100%;
    max-width: none;
    max-height: calc(100dvh - 32px);
    border-radius: var(--radius);
  }

  /* En una hoja de 92dvh no caben dos columnas ni tiene sentido meter un
     desplazamiento dentro de otro: se apila y rueda el cuerpo entero. */
  body.theme-ises .recetas-modal-content .modal-cuerpo { display: block; }
  body.theme-ises .recetas-layout { grid-template-columns: minmax(0, 1fr); }
  body.theme-ises .recetas-lista,
  body.theme-ises .recetas-detalle {
    max-height: none;
    overflow: visible;
  }
  body.theme-ises .clasificacion-card { max-height: none; }
}


/* ==========================================================================
   24. MODAL DE ALTA Y EDICIÓN DEL CATÁLOGO (#productoModal)
   ==========================================================================
   El formulario de alta era un cajón de 460px: cinco bloques en una tira
   vertical, y no se veía cómo iba quedando el registro hasta guardarlo.

   Aquí es un modal ancho de dos columnas. A la izquierda la captura por pasos
   numerados; a la derecha, FIJA mientras se rueda, la tarjeta tal como se verá
   en el catálogo, el margen y lo que falta para poder guardar.

   El <form> ES el `.modal-cuerpo` (el hijo que rueda), así que las dos cosas
   pegajosas de dentro —la cabecera y la columna derecha— se pegan contra él.
   ========================================================================== */

body.theme-ises .producto-modal-content {
  /* Alto de la cabecera pegajosa: es el `top` contra el que se pega la columna
     derecha, y así no se le mete por debajo al rodar. */
  --pm-cab-h: 74px;
}

@media (min-width: 760px) {
  body.theme-ises .producto-modal-content { width: min(1180px, 100%); }
}

/* Las columnas traen su propio relleno para que el filo que las separa llegue
   de arriba abajo; el del `.modal-cuerpo` partiría ese filo en dos. */
body.theme-ises .pm-cuerpo { padding: 0; }

body.theme-ises .pm-cab {
  position: sticky;
  top: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 16px;
  min-height: var(--pm-cab-h);
  padding: 14px 62px 14px 22px; /* a la derecha, el hueco del botón de cerrar */
  border-bottom: 1px solid var(--i-brd-08);
  background: var(--i-sup-1);
}

body.theme-ises .pm-cab-txt {
  display: flex;
  flex-direction: column;
  gap: 3px;
  flex: 1;
  min-width: 0;
}

body.theme-ises .pm-cab-txt h2 {
  margin: 0;
  font: 600 18px/1.2 var(--font-display);
  color: var(--ink-strong);
}

body.theme-ises .pm-eyebrow {
  font: 500 10px/1 var(--font-mono);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--faint);
}

/* El selector Producto/Servicio sube a la cabecera y deja de ser un bloque a
   todo el ancho del formulario. */
body.theme-ises .pm-tipo {
  width: 210px;
  margin-bottom: 0;
  flex: none;
}

/* --- Barra del alta por lote --------------------------------------------- */
body.theme-ises .pm-lote-barra {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 22px;
  border-bottom: 1px solid var(--accent-line);
  background: var(--accent-surface);
}

body.theme-ises .pm-lote-progreso {
  flex: 1;
  min-width: 0;
  font: 500 11.5px/1.3 var(--font-mono);
  letter-spacing: 0.04em;
  color: var(--i-azul-claro);
  /* El modelo lo teclea el usuario en el textarea del lote, sin tope de largo.
     `flex: none` en las acciones para que sea el texto el que ceda ancho, y no
     los botones de Saltar y Cancelar. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

body.theme-ises .pm-lote-acciones { display: flex; gap: 14px; flex: none; }
body.theme-ises .link-btn.danger { color: var(--danger); }
body.theme-ises .link-btn.danger:hover { color: var(--danger-hover); }

/* --- Las dos columnas ----------------------------------------------------- */
body.theme-ises .pm-cols {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
}

body.theme-ises .pm-main {
  display: flex;
  flex-direction: column;
  gap: 22px;
  min-width: 0;
  padding: 22px;
}

body.theme-ises .pm-lado {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
  padding: 22px;
  border-top: 1px solid var(--i-brd-08);
}

/* Dos columnas solo cuando caben las dos: por debajo de 980px la de la derecha
   quedaría en 250px y la tarjeta de vista previa no se leería. */
@media (min-width: 980px) {
  body.theme-ises .pm-cols { grid-template-columns: minmax(0, 1fr) 372px; }
  body.theme-ises .pm-lado {
    position: sticky;
    top: var(--pm-cab-h);
    align-self: start;
    border-top: 0;
    border-left: 1px solid var(--i-brd-08);
  }
}

/* --- Cabecera numerada de cada paso --------------------------------------- */
body.theme-ises .pm-sec {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

body.theme-ises .pm-sec-cab {
  display: flex;
  align-items: center;
  gap: 10px;
}

body.theme-ises .pm-sec-num {
  font: 500 10px/1 var(--font-mono);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--faint);
  white-space: nowrap;
}

body.theme-ises .pm-sec-linea {
  flex: 1;
  height: 1px;
  background: var(--i-brd-08);
}

body.theme-ises .pm-sec-nota {
  font: 400 11px/1 var(--font-body);
  color: var(--faint);
  white-space: nowrap;
}

/* El primer paso va sobre azul: es por donde se empieza y de ahí sale casi
   todo lo demás. */
body.theme-ises .pm-sec-ia {
  padding: 16px;
  border: 1px solid var(--accent-line);
  border-radius: var(--radius-sm);
  background: var(--accent-surface);
}

body.theme-ises .pm-sec-ia .pm-sec-num { color: var(--i-azul-claro); }
body.theme-ises .pm-sec-ia .pm-sec-linea { background: var(--accent-line); }


/* Las dos fotos, lado a lado: ocupan la mitad del alto que en el cajón, donde
   iban una encima de la otra. */
body.theme-ises .pm-fotos {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}
/* `min-width: 0` es el cinturón: un hijo de rejilla arranca en `min-width:auto`
   y se niega a encogerse por debajo de su contenido, así que basta con que algo
   ahí dentro mida de más para que la caja se salga de su columna. El tirantes es
   el tope de `.upload-box > img` en la hoja base. */
body.theme-ises .pm-fotos .upload-box {
  margin-bottom: 0;
  min-width: 0;
}

body.theme-ises .pm-lote-toggle { align-self: flex-start; }

body.theme-ises .pm-lote-panel {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
body.theme-ises .pm-lote-panel textarea {
  font: 400 12px/1.6 var(--font-mono);
  resize: vertical;
}
body.theme-ises .pm-lote-pie {
  display: flex;
  align-items: center;
  gap: 10px;
}
body.theme-ises .pm-lote-hint {
  font: 400 11.5px/1.4 var(--font-body);
  color: var(--faint);
}

/* --- Paso 03: precio ------------------------------------------------------ */
/* El precio especial va ARRIBA y aparte de los otros dos: es el dato que se
   copia de la ficha, no uno de los importes del producto (no se guarda). */
body.theme-ises .pm-especial-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 10px;
  align-items: end;
  padding-bottom: 14px;
  border-bottom: 1px dashed var(--i-campo-brd);
}
body.theme-ises .pm-especial-row .field { margin-bottom: 0; }
body.theme-ises .pm-especial-row .btn { height: var(--i-h-campo); }

body.theme-ises .pm-precio-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  gap: 10px;
  align-items: end;
}
body.theme-ises .pm-precio-row .field { margin-bottom: 0; }

body.theme-ises .pm-precio-flecha {
  padding-bottom: 9px;
  color: var(--i-txt-6);
  font-size: 15px;
}

body.theme-ises .pm-auto-btn {
  height: 20px;
  padding: 0 8px;
  border: 0;
  border-radius: var(--i-r-5);
  background: var(--i-sup-4);
  color: var(--i-txt-4);
  font: 500 9.5px/1 var(--font-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
}
body.theme-ises .pm-auto-btn.is-auto {
  background: var(--accent-surface);
  color: var(--i-azul-claro);
}

/* En "Auto" el precio no se teclea: se ve que es un valor calculado. */
body.theme-ises #productPrecioVenta[readonly] {
  background: var(--i-sup-1);
  color: var(--i-azul-claro);
  cursor: default;
}

body.theme-ises .pm-margen {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 13px;
  border: 1px solid var(--green-line);
  border-radius: var(--radius-sm);
  background: var(--green-surface);
}

body.theme-ises .pm-margen-cifra {
  font: 500 21px/1 var(--font-mono);
  letter-spacing: -0.02em;
  color: var(--green);
}

body.theme-ises .pm-margen-txt {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
body.theme-ises .pm-margen-tit { font: 400 12.5px/1.2 var(--font-body); color: var(--ink); }
body.theme-ises .pm-margen-nota { font: 400 11px/1.2 var(--font-body); color: var(--faint); }

body.theme-ises .pm-margen-obj {
  display: flex;
  align-items: center;
  gap: 5px;
  margin-left: auto;
  font: 400 11px/1 var(--font-body);
  color: var(--faint);
  text-transform: none;
  letter-spacing: normal;
}
body.theme-ises .pm-margen-obj input {
  width: 54px;
  height: 28px;
  padding: 0 6px;
  text-align: right;
  font: 500 12px/1 var(--font-mono);
}

body.theme-ises .pm-margen-util {
  font: 400 11.5px/1 var(--font-mono);
  color: var(--faint);
  white-space: nowrap;
}

/* --- Paso 05: avanzado ---------------------------------------------------- */
body.theme-ises .pm-avanzado {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px;
  border: 1px solid var(--i-brd-08);
  border-radius: var(--radius-sm);
  background: var(--i-sup-1);
}
body.theme-ises .pm-avanzado .product-specs { margin-bottom: 0; }
body.theme-ises .pm-avanzado .checkbox-row { margin-bottom: 0; }
body.theme-ises .pm-avanzado .field { margin-bottom: 0; }

/* --- Columna derecha: la tarjeta del catálogo ----------------------------- */
body.theme-ises .pm-card {
  border: 1px solid var(--i-brd-08);
  border-radius: var(--radius-sm);
  background: var(--i-sup-1);
  overflow: hidden;
}

/* 140px y no los 172 de la maqueta: con la tarjeta más alta, el botón de
   guardar se iba por debajo del pliegue en una pantalla de portátil (720px de
   alto útil), y la acción principal de la pantalla no puede exigir rueda. */
body.theme-ises .pm-card-foto {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 140px;
  background: var(--i-sup-3);
}
body.theme-ises .pm-card-foto img {
  max-width: calc(100% - 32px);
  max-height: 112px;
  object-fit: contain;
}
body.theme-ises .pm-card-sinfoto {
  font: 500 10px/1 var(--font-mono);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--i-txt-7);
}
body.theme-ises .pm-card-stock {
  position: absolute;
  top: 9px;
  right: 9px;
  padding: 3px 7px;
  border-radius: var(--i-r-5);
  background: var(--danger-surface);
  color: var(--danger);
  font: 500 9.5px/1.3 var(--font-mono);
  letter-spacing: 0.06em;
}
body.theme-ises .pm-card-stock.is-ok {
  background: var(--green-surface);
  color: var(--green);
}

body.theme-ises .pm-card-cuerpo {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 13px 14px 15px;
}
body.theme-ises .pm-card-linea {
  display: flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
}
body.theme-ises .pm-card-marca {
  font: 400 11.5px/1.2 var(--font-body);
  color: var(--faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  /* El `min-width: 0` va en el ÍTEM, no en el contenedor: sin él un ítem flex no
     baja de su ancho de contenido y la elipsis de arriba no llega a aplicarse
     nunca. */
  min-width: 0;
}
/* Los nombres de este catálogo son largos de verdad ("Bala TurboHD 2MP ColorVu
   Metálica HiLook…") y a veces llegan pegados de una ficha, sin espacios. Se
   corta a dos líneas, que es justo el `min-height` de abajo. */
body.theme-ises .pm-card-nombre {
  margin: 0;
  min-height: 38px;
  font: 500 14px/1.35 var(--font-body);
  color: var(--ink-strong);
  overflow-wrap: anywhere;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}
/* Un modelo de Syscom es un solo token sin espacios donde cortar
   ("DS-2CD2T87G2P-LSU/SL-BLACK-4MM"). */
body.theme-ises .pm-card-modelo {
  margin: 0;
  font: 400 11px/1.2 var(--font-mono);
  color: var(--faint);
  overflow-wrap: anywhere;
}
body.theme-ises .pm-card-precio {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding-top: 2px;
}
body.theme-ises .pm-card-precio > span:first-child {
  font: 600 18px/1 var(--font-display);
  letter-spacing: -0.01em;
  color: var(--i-azul-claro);
}
body.theme-ises .pm-card-margen {
  font: 400 11.5px/1 var(--font-mono);
  color: var(--faint);
}

/* --- Columna derecha: qué falta para guardar ------------------------------ */
body.theme-ises .pm-checklist-wrap {
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding-top: 14px;
  border-top: 1px solid var(--i-brd-08);
}

body.theme-ises .pm-checklist {
  display: flex;
  flex-direction: column;
  gap: 7px;
  margin: 0;
  padding: 0;
  list-style: none;
}
body.theme-ises .pm-checklist li {
  display: flex;
  align-items: center;
  gap: 9px;
  font: 400 12.5px/1.2 var(--font-body);
  color: var(--faint);
}
body.theme-ises .pm-checklist li.is-ok { color: var(--ink); }

body.theme-ises .pm-check-dot {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto; /* es un ítem flex de ancho fijo: no debe encogerse */
  width: 17px;
  height: 17px;
  border-radius: var(--i-r-pill);
  background: var(--i-sup-4);
  color: var(--i-txt-6);
  font-size: 10px;
}
body.theme-ises .pm-checklist li.is-ok .pm-check-dot {
  background: var(--green-surface);
  color: var(--green);
}

/* --- Columna derecha: botones --------------------------------------------- */
body.theme-ises .pm-acciones {
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding-top: 14px;
  border-top: 1px solid var(--i-brd-08);
}
body.theme-ises .pm-acciones-row { display: flex; gap: 9px; }
body.theme-ises .pm-acciones-row .btn { flex: 1; }
body.theme-ises .pm-otro { align-self: center; }

/* Atenuado mientras falte algo, pero NO deshabilitado: el clic tiene que
   disparar la validación del navegador, que sí dice qué campo falta. */
body.theme-ises .btn-full.is-off {
  background: var(--i-sup-4);
  color: var(--i-txt-6);
}

body.theme-ises .pm-lexico {
  border: 1px solid var(--i-brd-08);
  border-radius: var(--radius-sm);
  padding: 12px 13px;
}
body.theme-ises .pm-lexico summary {
  cursor: pointer;
  font: 500 13px/1.2 var(--font-body);
  color: var(--ink-strong);
  user-select: none;
}
body.theme-ises .pm-lexico[open] summary { margin-bottom: 11px; }
body.theme-ises .pm-lexico .side-hint { margin-top: 9px; }

/* El asterisco de campo obligatorio. */
body.theme-ises .field-label .req { color: var(--danger); }

/* --- Una sola columna ------------------------------------------------------ */
@media (max-width: 979px) {
  body.theme-ises .pm-cab { padding: 12px 56px 12px 16px; }
  body.theme-ises .pm-tipo { width: 168px; }
  body.theme-ises .pm-main,
  body.theme-ises .pm-lado { padding: 18px 16px; }
  body.theme-ises .pm-lote-barra { padding: 9px 16px; }

  /* En una hoja inferior de 92dvh, el último bloque tiene que respetar la barra
     de gestos igual que el resto de los modales. */
  body.theme-ises .pm-lado { padding-bottom: calc(24px + env(safe-area-inset-bottom, 0px)); }

  body.theme-ises .pm-margen { flex-wrap: wrap; }
  body.theme-ises .pm-margen-obj { margin-left: 0; }
}

/* Tres cajas en fila necesitan sitio: por debajo de 700px cada una bajaría de
   200px y su etiqueta se parte en tres renglones. */
@media (max-width: 700px) {
  body.theme-ises .pm-fotos { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 520px) {
  body.theme-ises .pm-fotos { grid-template-columns: minmax(0, 1fr); }
  body.theme-ises .pm-precio-row { grid-template-columns: minmax(0, 1fr); }
  body.theme-ises .pm-precio-flecha { display: none; }
  body.theme-ises .pm-cab { flex-wrap: wrap; }
  body.theme-ises .pm-tipo { width: 100%; }
}
