/* ═══════════════════════════════════════════════════════════════════════════
   Tema Vértice — colores y medidas centralizados de las pantallas estandarizadas.
   Cambia un valor acá y se actualiza en TODA pantalla que enlace este archivo
   (<link rel="stylesheet" href="vertice-theme.css">). Va junto a barra-vertice.js,
   que dibuja las figuras (Volver / Marca / Usuario) que usan estas clases.
   ─────────────────────────────────────────────────────────────────────────── */
:root {
  --topbar-bg:  #1B1340;   /* fondo de la barra superior */
  --topbar-h:   60px;      /* altura de la barra (desktop) — sube para la marca de 2 líneas */
  --thead-bg:   #1B1340;   /* fondo de la línea de títulos de columna (listados) */
  --brand-gold: #E8B84B;   /* dorado del recuadro de la inicial */
  --brand-sub:  #9fabd8;   /* azul apagado del nombre de Cliente (subtítulo) */
  --user-av:    #7C3AED;   /* fondo del avatar del usuario */
  /* Lote FM.3 — el acento pasa de violeta #6C3CE1 a AZUL. Un solo azul para todo el
     sistema: no hay "azul de retroceder" aparte (ver .vx-cf-btn más abajo). La portada
     (index.html) ya usaba este mismo #2563EB, así que además queda alineada.
     ⚠️ Para que este token gobierne de verdad hubo que sacar los `--accent` que cada
     pantalla redefinía en su propio :root — si alguna vuelve a declararlo, este valor
     deja de aplicarse en esa pantalla. */
  --accent:      #2563EB;   /* acento (botones/foco) */
  --accent-h:    #1D4ED8;   /* hover del acento */
  /* Versión suave del acento: fondos de estado ACTIVO / hover / filtro aplicado. Antes
     era el violeta claro #EDE9FE, que al pasar el acento a azul dejaba combinaciones
     absurdas (en pagos.html, `border-color:var(--accent)` azul con fondo violeta).
     ⚠️ NO se migraron los #EDE9FE que son CHIPS SEMÁNTICOS (est-iniciada, chip-pend,
     origen-cot, tipo AD, sello manual…): esos son una familia de color propia y siguen
     violeta a propósito — no acompañan al acento. */
  --accent-soft: #E9F0FE;

  /* Lote FM.3 — Aro de separación para superficies que flotan sobre fondo OSCURO
     (modales y ventanas flotantes). Es una capa de box-shadow completa, para
     anteponerla a la sombra que ya tenga cada superficie:
         box-shadow: var(--ring-flotante), 0 24px 60px …;
     Blanco al 22% con esparcido 0: aparece SOLO cuando hay algo oscuro detrás y
     queda invisible sobre pantalla clara. Existe porque la sombra sola no separa
     nada contra un hero navy — la barra de la ventana se fundía con el fondo. */
  --ring-flotante: 0 0 0 1px rgba(255, 255, 255, .22);

  /* ═══ Tipografía, contraste y espaciado (Lote FD) ═══════════════════════════
     Tokens comunes para formularios y fichas. Nacen de auditar la ventana
     "Actualizar tarea" contra las recomendaciones de tipografía; los ratios de
     contraste están MEDIDOS (WCAG), no estimados. Las pantallas se migran de a
     una: mientras una pantalla no use estos var(), no cambia en nada.

     ── Texto (contraste sobre los 3 fondos del sistema de capas: blanco #fff,
        crema #F7F4EE de campos bloqueados, y #FBFBFD de cabeceras) ───────────── */
  --text-soft:    #5F6B7A;  /* secundario (etiquetas meta, timestamps, vacíos).
                               AA: 5.43 blanco · 4.94 crema · 5.29 #FDFCF9.
                               ⚠️ NO usar --gray #6B7280 para esto: da 4.40 sobre
                               crema, bajo el mínimo 4.5 de WCAG AA. */
  --text-lock:    #7D7565;  /* candado 🔒 de campos bloqueados sobre crema.
                               4.15 — sobre el umbral 3:1 de iconos, sin volverse negro. */
  --text-eyebrow: #806A24;  /* rótulos dorados en mayúsculas (CONTEXTO, HISTORIAL…)
                               sobre crema: 4.78 · 5.11. El --brand-gold NO sirve
                               acá (es para fondo navy, no para texto sobre crema). */

  /* ── Colores SEMÁNTICOS para TEXTO (Lote FF) ──────────────────────────────
     Los tonos vivos de la paleta (#D97706 ámbar, #16A34A verde, #E74C3C rojo)
     rinden 3.19 / 3.30 / 3.82 sobre blanco: alcanzan para TEXTO GRANDE (umbral
     3.0: ≥18.66px en negrita o ≥24px) pero NO para texto chico, que exige 4.5.
     Por eso el mismo color es correcto en un número de KPI de 22px e insuficiente
     en una celda de tabla. Estos son las variantes para TEXTO CHICO — el tono más
     claro de cada familia que llega a 4.5, para alterar lo mínimo el aspecto.
     Para FONDOS y puntos de leyenda seguí usando los vivos: el 4.5 rige el texto. */
  --text-warn:   #B45309;   /* advertencia (era #D97706 3.19) → 5.02 */
  --text-ok:     #15803D;   /* ok / éxito  (era #16A34A 3.30) → 5.02 */
  --text-danger: #DC2626;   /* peligro     (era #E74C3C 3.82) → 4.83 */

  /* ── Tamaños. Piso 11px: bajo eso el texto deja de ser legible en pantalla.
     Lote FK — valores ESTANDARIZADOS. Antes eran 12px/12.5px, calibrados para la
     ventana densa de 3 columnas de "Actualizar tarea" y tomados por norma: pero esa
     ventana era la EXCEPCIÓN. El resto de las pantallas ya usaba 13px en etiquetas
     (7 de ellas) y 15px en controles (4; otras 2 en 14), que además es lo que pide
     la guía de tipografía para escritorio (13–14 etiquetas, 15–16 cuerpo).
     Se midió que "Actualizar tarea" entra a 15px sin truncar nada ni desbordar
     (su columna crece 11px), así que no hace falta una escala aparte para lo denso. */
  --fs-meta:       11px;    /* timestamps, badges, notas al pie */
  --fs-label:      13px;    /* etiquetas de campo y rótulos */
  --fs-body:       15px;    /* texto de inputs, valores y notas */
  --fs-card-title: 15px;    /* título de tarjeta/sección (se distingue por peso y color,
                               no por tamaño; bajo el cuerpo se leería como subtítulo) */

  /* ── Espaciado. El padding de la tarjeta va ≥ separación entre campos: si no,
        los inputs se ven pegados al borde. ──────────────────────────────────── */
  --gap-label:  6px;        /* etiqueta ↔ su caja */
  --gap-fields: 13px;       /* entre campos */
  --pad-card:   13px;       /* padding interno de la tarjeta */

  /* ── Controles (inputs/selects). El alto sale del padding + tipo. ─────────── */
  --pad-control:   8px 10px;
  --h-control-min: 36px;

  /* ── Modal de confirmación (Lote FM) ───────────────────────────────────────
     La cabecera del modal es la MISMA barra navy del resto del sistema: apunta a
     --topbar-bg en vez de repetir el valor, así que si mañana cambia el color de
     la barra, cambia también el modal sin tocar ninguna pantalla. */
  --modal-head-bg:   var(--topbar-bg);
  /* Lote FM.3 — se ELIMINÓ --action-revert. Existió unas horas, para que un retroceso
     no se leyera como el camino normal frente al violeta de "avanzar". Al pasar el
     acento a azul los dos quedaban del mismo color, así que la distinción por tono ya
     no decía nada: lo que un retroceso es lo dicen el rótulo del botón y el modal que
     lo explica, no un matiz de azul que nadie compara lado a lado. */
  /* Superficies de apoyo del modal (mismas familias que ya usan las fichas). */
  --surface-crema:   #F7F4EE;   /* nota al pie / campos bloqueados */
  --surface-warn:    #FEF6E7;   /* caja de advertencia, pareja de --text-warn */

  /* ═══ Lote FR · Colores de TIPO de tarea ══════════════════════════════════
     La paleta que identifica cada tipo (el chip de la lista, el ícono y el chip
     del selector de "Nueva tarea"). Estaba escrita en TRES lugares —dos reglas
     CSS del selector y una tercera a mano dentro de tipoChipHtml()— y no
     coincidían: AD salía VERDE en el selector y VIOLETA en la lista. Nadie lo
     notaba porque las dos pantallas nunca se ven juntas.
     Manda el violeta: el Lote FM.3 dejó AD dentro de la familia de chips
     semánticos a propósito (por eso no se migró al acento azul). */
  --tipo-np-bg: #EEF4FF;  --tipo-np-fg: #2563EB;   /* No Presupuestada */
  --tipo-cs-bg: #DBEAFE;  --tipo-cs-fg: #2563EB;   /* Con Servicio */
  --tipo-cp-bg: #DCFCE7;  --tipo-cp-fg: #15803D;   /* Con Presupuesto */
  --tipo-tr-bg: #FFEDD5;  --tipo-tr-fg: #C2410C;   /* Tarea Recurrente */
  --tipo-ad-bg: #EDE9FE;  --tipo-ad-fg: #6D28D9;   /* Administrativa */
  --tipo-gb-bg: #FFF7ED;  --tipo-gb-fg: #B45309;   /* Gasto con Boleta */

  /* ═══ Lote FR · Superficies y bordes ══════════════════════════════════════
     --border lo USABA este archivo seis veces —siempre como `var(--border,
     #E5E7EB)`— sin definirlo nunca: dependía de que cada pantalla lo declarara
     en su propio :root, y lo hacían diez, por separado. Ahora lo dueña el theme.
     ⚠️ Una pantalla usa #E6EAF2 en vez de #E5E7EB; su declaración local sigue
     ganando por orden de cascada (su <style> va después del <link>), así que no
     cambia de aspecto. */
  --border:        #E5E7EB;
  /* Fondo de cabecera de tarjeta / sección. Estaba escrito a mano 15 veces en 8
     archivos. NO confundir con --bg, que es el fondo de la pantalla: ese vale
     distinto en cada app (#FAFAF8, #F5F5F3, #F3F5F9) y por eso no se centraliza. */
  --surface-head:  #FBFBFD;
  /* Violeta del título de tarjeta ("📋 Datos de la tarea"). 10 usos a mano en 5
     archivos. Es de la familia semántica, no del acento: no acompaña al azul. */
  --card-title:    #534AB7;
}

/* ═══ Lote FR · Tarjeta de formulario ═════════════════════════════════════════
   La agrupación visual que estrenó "Actualizar tarea" (Lote EN) y que ahora usa
   también "Nueva tarea". Vive acá para que las dos —y las que vengan— se vean
   igual sin copiar reglas. Los tamaños salen de los tokens de tipografía y
   espaciado, así que respetan solos el régimen táctil de celular. */
.vx-card {
  background: #fff; border: 1.5px solid var(--border);
  border-radius: 10px; overflow: hidden;
}
.vx-card-h {
  padding: 8px var(--pad-card); border-bottom: 1px solid var(--border);
  background: var(--surface-head);
  font-size: var(--fs-card-title); font-weight: 700; color: var(--card-title);
  display: flex; align-items: center; gap: 6px;
}
.vx-card-b {
  padding: var(--pad-card); display: flex; flex-direction: column;
  gap: var(--gap-fields);
}

/* ═══ Lote FR · Esqueleto de una ventana con pie ══════════════════════════════
   Barra arriba (.vx-wintop), cuerpo que scrollea, pie siempre abajo. Reemplaza
   el truco de `position: sticky`, que sólo pega el pie cuando hay desborde: con
   el formulario corto quedaba flotando a media ventana.
   ⚠️ `min-height: 0` en el cuerpo es obligatorio — sin él un hijo flexible no se
   deja encoger bajo su contenido y el scroll se escapa a la ventana entera. */
.vx-win-cuerpo { flex: 1; min-height: 0; overflow: auto; }
.vx-win-pie {
  flex: none; display: flex; gap: 9px; justify-content: flex-end;
  padding: 11px 13px; background: #fff; border-top: 1px solid var(--border);
}

/* Dos columnas que se apilan en celular. El umbral es el 599px de siempre. */
.vx-cols-2 {
  display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr);
  gap: 9px; align-items: start;
}
.vx-col { display: flex; flex-direction: column; gap: 9px; min-width: 0; }
@media (max-width: 599px) {
  .vx-cols-2 { grid-template-columns: 1fr; }
}

/* ═══ Lote FR · La CAJA de una ventana flotante ═══════════════════════════════
   Radio, sombra y aro de toda ventana que flota sobre la pantalla. Estaba
   repetido: `tareas.html` lo tenía DOS VECES en el mismo archivo (una para
   .screen-overlay y otra para .tab-panel, valores idénticos), y cada pantalla
   con ventana propia lo vuelve a escribir.
   El aro va primero a propósito — ver --ring-flotante: es lo único que separa la
   ventana de un fondo oscuro, porque la sombra sola también es oscura.
   NO incluye tamaños ni posición: eso es de cada ventana. */
.vx-caja-flotante {
  border-radius: 12px;
  overflow: hidden;
  box-shadow: var(--ring-flotante),
              0 24px 60px -20px rgba(27, 19, 64, .42),
              0 4px 12px rgba(27, 19, 64, .12);
}

/* ═══ Gesto: anular el "pull-to-refresh" de Chrome Android ════════════════════
   Al deslizar el dedo hacia abajo estando arriba de todo, Chrome Android recarga
   la página. En Vértice eso no es una molestia menor: recargar es un arranque en
   FRÍO, y el arranque en frío pide login a propósito (Lote EP: solo se auto-entra
   con ?session=, nunca restaurando el token de localStorage). O sea, el gesto
   accidental te expulsa de la app y te obliga a entrar de nuevo.

   `contain` corta el encadenamiento del scroll hacia el viewport, que es lo que
   dispara el gesto; conserva el rebote/brillo interno. No es `none` porque no hace
   falta apagar también ese efecto visual.

   Va en html Y en body a propósito: la propagación al viewport la define el
   elemento raíz, pero la receta de Chrome documenta el body, y ponerlo en los dos
   es inocuo. Global (no opt-in como el régimen móvil de abajo): no altera ningún
   layout — en escritorio, donde no existe el gesto, la regla es inerte.
   ─────────────────────────────────────────────────────────────────────────── */
html, body { overscroll-behavior-y: contain; }

/* ═══ Régimen MÓVIL de los tokens — OPT-IN (Lote FD) ══════════════════════════
   En móvil los formularios colapsan a 1 columna, así que desaparece la
   restricción de ancho que obliga a tipos compactos en escritorio, y manda la
   ergonomía táctil:
     · 16px en los controles es OBLIGATORIO — con menos, iOS hace zoom
       automático al enfocar el campo.
     · 44px de alto mínimo = área táctil (Apple 44pt / Material 48dp).

   ⚠️ Es OPT-IN por `<body class="vx-responsive">`, NO global. Varias pantallas
   (pagos.html, por ejemplo) son SOLO-DESKTOP: no tienen ninguna media query y
   muestran tablas de 20+ columnas. Si este régimen fuera global, al angostar el
   navegador les saltaría el tipo a 16px y les rompería la tabla. Así, una
   pantalla solo-desktop simplemente NO pone la clase y sus tokens se quedan en
   los valores de escritorio siempre.
   Los tokens se redefinen sobre `body` (no `:root`) porque las custom properties
   se heredan: puestas en body cascadean a todo el documento igual que en :root,
   pero permiten condicionarlas a una clase.

   UMBRAL 599px — es la convención del proyecto: lo usan las media queries de
   todas las pantallas y el gate `esViewportMobile()` (matchMedia
   '(max-width: 599px)'), que es el chequeo autoritativo de "es móvil" y el que
   apaga el arrastre/resize de las ventanas flotantes. Mantener JS y CSS en el
   MISMO número: si discrepan en 1px queda un ancho exacto donde el JS dice
   "móvil" y el CSS dice "escritorio" (o al revés). */
@media (max-width: 599px) {
  body.vx-responsive {
    /* --fs-label NO se redefine: tras la estandarización (Lote FK) ya vale 13px en
       escritorio, que es justo lo que pide la guía para móvil. Repetirlo sería ruido. */
    --fs-body:       16px;   /* 15 → 16: bajo 16px iOS hace zoom al enfocar el campo */
    --fs-card-title: 16px;   /* acompaña al cuerpo; por debajo se leería como subtítulo */
    --gap-fields:    18px;
    --pad-card:      16px;
    --pad-control:   11px 12px;
    --h-control-min: 44px;
  }
}

/* ── Barra superior ─────────────────────────────────────────────────────── */
.vx-topbar { background: var(--topbar-bg); height: var(--topbar-h); }

/* ── Figura: Volver (←) — forma/color/alto inertes; el onClick lo pone cada pantalla ── */
.vx-back {
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; background: rgba(255,255,255,0.08); border: none;
  border-radius: 8px; color: #fff; cursor: pointer; padding: 0; flex-shrink: 0;
}
.vx-back:hover { background: rgba(255,255,255,0.16); }
.vx-back svg { width: 20px; height: 20px; }

/* ── Figura: Marca (estilo portada) — recuadro con inicial + 2 nombres apilados.
   marca.js llena inicial / App / Cliente por data-marca-inicial|-nombre|-cliente. ── */
.vx-brand {
  display: inline-flex; align-items: center; gap: 12px; background: transparent;
  padding: 0; white-space: nowrap;
}
.vx-brand-mark {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; background: var(--brand-gold); color: var(--topbar-bg);
  border-radius: 10px; font-family: 'DM Serif Display', serif; font-size: 23px; line-height: 1; flex-shrink: 0;
}
.vx-brand-stack { display: inline-flex; flex-direction: column; justify-content: center; }
.vx-brand-name {
  font-family: 'DM Serif Display', serif; font-size: 19px; color: #fff; line-height: 1;
}
.vx-brand-cliente {
  font-size: 11px; font-weight: 300; color: var(--brand-sub); line-height: 1; margin-top: 4px;
}

/* ── Figura: Usuario (avatar + nombre) ─────────────────────────────────────── */
.vx-user { color: #fff; font-size: 13px; display: flex; align-items: center; gap: 10px; }
.vx-user-av {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; background: var(--user-av); color: #fff;
  border-radius: 50%; font-size: 12px; font-weight: 700;
}

/* ── Barra superior de VENTANA flotante (Actualizar tarea, editar contrato, etc.) ──
   Mismo COLOR y ALTO que la barra de listados (viven acá, en el paquete) y reúne las
   MISMAS figuras Volver/Marca/Usuario (barra-vertice.js). El ← es "Cerrar": cierra la
   ventana o vuelve a donde fue llamada — la FUNCIÓN la engancha cada ventana sobre
   .vx-back, igual que los listados enganchan su Volver. En medio va el contexto propio
   de la ventana (ID · descripción), que cada pantalla llena. */
.vx-wintop {
  background: var(--topbar-bg); height: var(--topbar-h);
  display: flex; align-items: center; gap: 12px; padding: 0 14px; flex-shrink: 0;
}
.vx-win-ctx { display: flex; align-items: baseline; justify-content: center; gap: 8px; flex: 1; min-width: 0; }
.vx-win-id { color: #fff; font-size: 14px; font-weight: 700; white-space: nowrap; flex-shrink: 0; }
.vx-win-sep { color: rgba(255,255,255,.4); flex-shrink: 0; }
.vx-win-desc {
  color: rgba(255,255,255,.72); font-size: 13px; font-weight: 400;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* ── Lote FP · Píldora de paso ("Paso 2 de 3") ───────────────────────────────
   Solo la usan las ventanas que son ASISTENTES (cotizaciones, adjudicación).
   Existe porque en ellas el ← tiene doble papel: retrocede de paso, y sólo en el
   paso 1 cierra. Sin decir en qué paso vas, la flecha es una ruleta. La ventana
   la muestra sólo cuando el hijo manda `paso` en su vxv:meta.
   `flex: none` para que no la aplaste el contexto, que sí es elástico. */
.vx-win-paso {
  font-size: var(--fs-meta, 11px); font-weight: 600;
  letter-spacing: .05em; text-transform: uppercase;
  color: var(--brand-sub); background: rgba(255, 255, 255, .10);
  border-radius: 20px; padding: 4px 10px; flex: none; white-space: nowrap;
}
.vx-win-paso[hidden] { display: none; }

/* En celular se oculta junto con la marca y el usuario: el contexto (qué tarea
   estás mirando) manda sobre el número de paso, que igual se ve en el cuerpo. */
@media (max-width: 599px) {
  .vx-wintop .vx-win-paso { display: none; }
}

/* ── Lote FH · La barra de ventana en CELULAR ────────────────────────────────
   En 375px no caben las 4 piezas (Cerrar + Marca + contexto + Usuario): la marca
   y el usuario se comen el ancho y el contexto —que es lo ÚNICO que identifica
   qué estás mirando— queda aplastado y truncado ("PR-1003 Comer…").
   La marca y el usuario son redundantes acá: ya venías de una pantalla que las
   muestra, y no aportan al identificar la ficha abierta. Se ocultan y el contexto
   se queda con todo el ancho.
   Vive en el THEME y no en cada pantalla porque las 4 ventanas flotantes comparten
   .vx-wintop: así se comportan igual sin repetir la regla (antes solo
   "Actualizar tarea" lo hacía, y las otras tres se veían apretadas).
   Se conserva el ellipsis (nowrap) a propósito: la barra tiene alto FIJO
   (--topbar-h), así que dejar envolver la descripción la desbordaría. Con el ancho
   liberado el corte ocurre mucho más tarde. */
@media (max-width: 599px) {
  .vx-wintop [id^="slot-brand-"], .vx-wintop [id^="slot-user-"] { display: none; }
}

/* ═══ Lote FM · Modal de confirmación (.vx-cf) ════════════════════════════════
   Reemplaza confirm()/alert() del navegador cuando la decisión necesita mostrar
   QUÉ cambia y QUÉ no se deshace: el cuadro nativo pone todos los párrafos con el
   mismo peso, y acá lo que decide es el del medio. Lo dibujan vxConfirm()/vxAviso().
   Vive en el THEME por lo mismo que .vx-wintop: la cabecera es la barra navy del
   sistema (var(--modal-head-bg) → var(--topbar-bg)), así que el día que cambie el
   color de la barra este modal lo sigue solo, y cualquier pantalla que enlace el
   theme puede usarlo sin copiar estilos.
   z-index 1300: por encima de los modales de pantalla (1200, ver ficha-prov/mt-overlay). */
.vx-cf-ov {
  position: fixed; inset: 0; z-index: 1300;
  background: rgba(27, 19, 64, .55); backdrop-filter: blur(2px);
  display: flex; align-items: center; justify-content: center; padding: 20px;
}
.vx-cf-ov[hidden] { display: none; }
.vx-cf {
  background: #fff; border-radius: 14px; width: 100%; max-width: 410px;
  /* Lote FM.3 — el aro (ver --ring-flotante) es lo que separa al modal de un fondo
     OSCURO: la sombra sola es oscura y contra el hero navy no se ve. */
  box-shadow: var(--ring-flotante), 0 18px 45px rgba(17, 10, 50, .45);
  overflow: hidden;
}

/* ── Cabecera: ícono de la acción + rótulo + tarea identificada ── */
/* Lote FM.2: el alto sale de --topbar-h, igual que .vx-topbar y .vx-wintop. Antes salía
   del padding (15+32+15 = 62px) y coincidía con la barra por casualidad: si --topbar-h
   cambiaba, el modal se quedaba donde estaba. */
.vx-cf-head {
  background: var(--modal-head-bg); color: #fff;
  height: var(--topbar-h); padding: 0 20px;
  display: flex; align-items: center; gap: 11px;
}
.vx-cf-ico {
  width: 32px; height: 32px; border-radius: 9px; flex: none;
  background: rgba(232, 184, 75, .18); color: var(--brand-gold);
  display: grid; place-items: center; font-size: 16px;
}
.vx-cf-rot {
  font-size: var(--fs-meta, 11px); color: var(--brand-sub);
  letter-spacing: .05em; text-transform: uppercase; font-weight: 500;
}
.vx-cf-tarea {
  font-size: 15px; font-weight: 600; line-height: 1.25; margin-top: 1px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* ── Cuerpo ── */
.vx-cf-body { padding: 20px 22px 18px; }
.vx-cf-title {
  font-size: 19px; font-weight: 600; margin: 0 0 16px; line-height: 1.3; color: #111827;
}

/* Antes / Después: el estado completo, no solo el dato que cambia. */
.vx-cf-diff { display: grid; grid-template-columns: 1fr auto 1fr; gap: 11px; margin-bottom: 16px; }
.vx-cf-box {
  border: 1.5px solid var(--border, #E5E7EB); border-radius: 11px; padding: 12px; text-align: center;
}
.vx-cf-box.ahora { background: #FBFBFD; }
.vx-cf-box.queda { border-color: var(--text-warn); background: var(--surface-warn); }
.vx-cf-cap {
  font-size: var(--fs-meta, 11px); color: var(--text-soft);
  letter-spacing: .05em; text-transform: uppercase; margin-bottom: 6px;
}
.vx-cf-val { font-size: 15px; font-weight: 600; color: #111827; }
.vx-cf-val small { display: block; font-size: 12px; font-weight: 500; color: var(--text-soft); margin-top: 3px; }
.vx-cf-mid { display: grid; place-items: center; color: var(--text-warn); font-size: 17px; }

/* Avisos: lo que NO se deshace. Van en segundo plano a propósito — se leen, no gritan. */
.vx-cf-nota {
  background: var(--surface-crema); border-radius: 10px; padding: 11px 13px;
  font-size: 13px; color: #4A5462; line-height: 1.5;
  display: flex; gap: 9px; margin-bottom: 9px;
}
.vx-cf-nota:last-of-type { margin-bottom: 18px; }
.vx-cf-nota b { color: var(--text-warn); }

/* ── Pie ── */
.vx-cf-btns { display: flex; gap: 9px; justify-content: flex-end; }
.vx-cf-btn {
  font-family: inherit; font-size: 14px; font-weight: 600; border-radius: 9px;
  padding: 10px 16px; cursor: pointer; border: 1.5px solid transparent; transition: .14s;
}
.vx-cf-btn.ghost { background: #fff; border-color: var(--border, #E5E7EB); color: #374151; }
.vx-cf-btn.ghost:hover { background: #F9FAFB; }
/* Primario: el acento del sistema. Lote FM.3 — se retiró el modificador .rev (un azul
   distinto para los retrocesos): con el acento ya en azul no aportaba nada. */
.vx-cf-btn.pri { background: var(--accent); color: #fff; }
.vx-cf-btn.pri:hover { background: var(--accent-h); }
.vx-cf-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* En celular el modal usa el ancho disponible y los botones se estiran (área táctil). */
@media (max-width: 599px) {
  .vx-cf-ov { padding: 14px; }
  .vx-cf-btns .vx-cf-btn { flex: 1; padding: 12px 14px; min-height: 44px; }
}

/* ═══ Lote FM.2 · Campos y resultado dentro del modal (.vx-cf) ════════════════
   El modal de confirmación no tenía campos: solo mostraba y preguntaba. El diálogo de
   "Generar tareas" sí los necesita (mes + contrato), y después muestra un resultado con
   una línea por contrato. Ambas piezas viven acá y no en monitor.html para que cualquier
   pantalla que enlace el theme las tenga — y para que los controles hereden los tokens
   (--pad-control / --h-control-min / --fs-body), incluido el régimen táctil de móvil. */
.vx-cf-intro { font-size: var(--fs-body); color: #4A5462; line-height: 1.5; margin: 0 0 16px; }
.vx-cf-field { margin-bottom: 14px; }
.vx-cf-lbl {
  display: block; font-size: var(--fs-label); font-weight: 600; color: var(--text-soft);
  text-transform: uppercase; letter-spacing: .05em; margin-bottom: var(--gap-label, 6px);
}
.vx-cf-input {
  width: 100%; padding: var(--pad-control); min-height: var(--h-control-min);
  border: 1.5px solid var(--border, #E5E7EB); border-radius: 8px;
  font-family: inherit; font-size: var(--fs-body); color: #111827; background: #fff;
}
.vx-cf-input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(108, 60, 225, .15); }
.vx-cf-hint { font-size: 12px; color: var(--text-soft); margin-top: 5px; line-height: 1.45; }

/* ── Resultado: KPIs + grupos plegables ──
   Con 70+ contratos una lista plana es una pared de texto, así que manda el resumen y el
   detalle se despliega. El grupo "recreadas" se abre solo: es la única acción de la
   corrida que pisa una decisión previa de alguien. */
.vx-cf-kpis { display: flex; gap: 8px; margin-bottom: 14px; flex-wrap: wrap; }
.vx-cf-kpi {
  flex: 1; min-width: 84px; border: 1px solid var(--border, #E5E7EB);
  border-radius: 10px; padding: 9px 10px; text-align: center;
}
.vx-cf-kpi .n { font-size: 21px; font-weight: 700; line-height: 1.1; }
.vx-cf-kpi .l {
  font-size: var(--fs-meta); color: var(--text-soft);
  text-transform: uppercase; letter-spacing: .04em; margin-top: 3px;
}
.vx-cf-kpi.ok { background: #F1F9F3; border-color: #CDE8D4; }
.vx-cf-kpi.ok .n { color: var(--text-ok); }
.vx-cf-kpi.re { background: var(--surface-warn); border-color: #F2DFB8; }
.vx-cf-kpi.re .n { color: var(--text-warn); }
.vx-cf-kpi.sk .n { color: var(--text-soft); }

.vx-cf-grupo { border: 1px solid var(--border, #E5E7EB); border-radius: 10px; overflow: hidden; margin-bottom: 9px; }
.vx-cf-grupo-h {
  background: #FBFBFD; border-bottom: 1px solid var(--border, #E5E7EB); padding: 9px 12px;
  font-size: var(--fs-label); font-weight: 600; color: var(--text-soft);
  text-transform: uppercase; letter-spacing: .05em;
  display: flex; justify-content: space-between; align-items: center; cursor: pointer;
}
.vx-cf-grupo-h .tw { font-size: 11px; font-weight: 500; text-transform: none; letter-spacing: 0; }
.vx-cf-grupo.cerrado .vx-cf-grupo-h { border-bottom: 0; }
.vx-cf-grupo.cerrado .vx-cf-lista { display: none; }
/* Alto acotado con scroll propio: "Creadas · 58" desplegado no debe estirar el modal. */
.vx-cf-lista { max-height: 180px; overflow-y: auto; }
.vx-cf-it {
  display: flex; gap: 10px; padding: 8px 12px; font-size: 13px; line-height: 1.4;
  border-bottom: 1px solid #F3F4F6; align-items: flex-start;
}
.vx-cf-it:last-child { border-bottom: 0; }
.vx-cf-it .ic { flex: none; width: 15px; text-align: center; font-size: 12px; margin-top: 1px; }
.vx-cf-it .k { font-weight: 600; flex: none; }
.vx-cf-it .v { color: var(--text-soft); }
.vx-cf-it.ok .k { color: var(--text-ok); }
.vx-cf-it.re .k { color: var(--text-warn); }
.vx-cf-it.sk .k { color: var(--text-soft); }
.vx-cf-it.bad .k { color: var(--text-danger); }
/* El resultado de una corrida completa necesita más ancho que una pregunta. */
.vx-cf.ancho { max-width: 520px; }
