/*
 * gx-base — Overrides y utilidades sobre el bundle compilado de TailAdmin.
 * Cargar SIEMPRE después de style.css en layouts/app.php.
 */

/*
 * Tipografía del sistema — Inter, self-host.
 *
 * Por qué vive acá y no en style.css: el bundle compilado declara
 * `--font-outfit: Outfit, sans-serif` (línea 267, dentro de `@layer theme`) y la
 * aplica con `body { font-family: var(--font-outfit) }`. Las reglas de ESTE
 * archivo van FUERA de `@layer`, así que ganan sobre cualquier capa sin
 * `!important` y sin tocar el compilado. Cambiar la fuente del sistema = cambiar
 * el valor de la variable de abajo; revertir = borrar este bloque.
 *
 * El `@import` de Outfit sigue en la línea 4 del bundle (decisión: no alterar el
 * compilado). Ya no la usa el `body`, pero sí los mapas de jsvectormap, que la
 * tienen escrita a mano en bundle.js.
 *
 * Self-host y no Google Fonts: los .woff2 viven en public/assets/fonts/ y el
 * .htaccess ya los comprime y los sirve con `Cache-Control: immutable` a un año
 * ⇒ solo pesan en la primera visita. Además no dependen de un tercero durante el
 * render ni obligan a abrir `fontSrc` si algún día se prende el CSP
 * (`Config\App::$CSPEnabled`, hoy false).
 *
 * ⚠️ CDN: con CloudFront prendido este CSS se sirve desde el CDN y las URL
 * relativas de abajo resuelven contra ESE host ⇒ los .woff2 se piden
 * cross-origin y el navegador los DESCARTA EN SILENCIO si la respuesta no trae
 * CORS (el texto cae al fallback y nada lo grita en consola). La distribución
 * debe responder `Access-Control-Allow-Origin` en /assets/*.
 *
 * `font-weight: 100 900` = archivo variable: un solo .woff2 cubre todos los
 * pesos que usa el bundle (font-medium, font-semibold, font-bold…). Sin itálica
 * a propósito: la UI no la usa y el navegador la sintetiza. Los `unicode-range`
 * son los de Google Fonts ⇒ el subset latin-ext solo se descarga si aparece un
 * carácter de ese rango (en la práctica, casi nunca).
 *
 * Licencia: Inter es SIL OFL 1.1 — el texto vive junto a las fuentes en
 * public/assets/fonts/OFL.txt y debe viajar con ellas.
 */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('../fonts/inter-latin-var.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('../fonts/inter-latin-ext-var.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  /*
   * Punto ÚNICO de la tipografía. Conserva el nombre `--font-outfit` porque es
   * el que consume el bundle: renombrarla exigiría tocar el compilado.
   */
  --font-outfit: 'Inter', ui-sans-serif, system-ui, -apple-system, 'Segoe UI',
    Roboto, 'Helvetica Neue', Arial, sans-serif;
  /*
   * El bundle deja `--default-font-family: var(--font-sans)` con `--font-sans`
   * SIN definir ⇒ <html> caía al stack del sistema. Se alinea para que nada
   * quede fuera (utilidad `font-sans`, elementos que no heredan de body).
   */
  --font-sans: var(--font-outfit);
}

/*
 * AG Grid no hereda la fuente del documento: el tema quartz trae su propio
 * `--ag-font-family: -apple-system, …`, y sin esto la rejilla queda con OTRA
 * tipografía dentro de la misma pantalla (Auditoría Móvil, Ventas vs Utilidad).
 *
 * Se declara el stack y NO `inherit`: en una custom property `inherit` es
 * palabra clave del cascade (hereda la VARIABLE del padre), no "hereda el
 * font-family" — el resultado dependería de dónde quedó declarada.
 *
 * No hace falta `!important`: AG Grid inyecta su <style> ANTES del primer <link>
 * del <head> (`insertBefore(…, ':not(title, meta)')`) justo para poder ser
 * sobreescrito, así que con la misma especificidad ganamos por orden.
 */
.ag-root-wrapper,
.ag-theme-quartz,
.ag-theme-quartz-dark {
  --ag-font-family: var(--font-outfit);
}

/*
 * .gx-select — select con chevron consistente en cualquier fondo.
 *
 * Por qué: reemplaza la flecha por una del color del design system, con variante
 * dark. OJO (medido 2026-09-03): el bundle YA trae una regla base
 * `select { background-image: <chevron> }`, así que ningún select se queda sin
 * flecha — esta clase la SUSTITUYE, no la agrega. Corolario: agregar además un
 * `<svg>` absoluto en un wrapper `relative` pinta DOS flechas (pasaba en 18
 * selects). Lo único que apaga la del bundle es `bg-none`, no `appearance-none`. El bundle TailAdmin usa el patrón
 * "wrapper <div class='relative'> + <svg> chevron absoluto" pero replicarlo
 * en cada filtro es ruidoso. Esta clase encapsula appearance-none + chevron
 * como background-image y deja el padding-right necesario para no chocar.
 *
 * Uso: <select class="gx-select h-11 rounded-lg border ..."> — agregar junto
 * a las clases de tamaño/borde habituales. NO combinar con `pr-*` arbitrario:
 * la clase ya reserva espacio para el icono.
 */
:root {
  --color-brand-50: #f4f9ed;
  --color-brand-100: #e7f2d8;
  --color-brand-200: #cfe5b2;
  --color-brand-300: #b4d686;
  --color-brand-400: #95c45d;
  --color-brand-500: #71a83b;
  --color-brand-600: #5f8e31;
  --color-brand-700: #4d7428;
  --color-brand-800: #3c5b1f;
  --color-brand-900: #2d4518;
  --color-brand-950: #17240c;
}

.gx-select {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  /* Data-URI URL-encoded (%3C/%3E) y sin el parámetro no estándar `;utf8`: Chrome/Safari
     NO parsean `data:image/svg+xml;utf8,<svg…>` con `<`/`>` crudos (Firefox sí lo tolera),
     así el chevron no cargaba en Chrome y —con appearance:none— el select quedaba sin flecha. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M3.8335 5.9165L8.00016 10.0832L12.1668 5.9165' stroke='%236B7280' stroke-width='1.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.75rem center;
  background-size: 16px 16px;
  padding-right: 2.25rem;
}

.dark .gx-select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M3.8335 5.9165L8.00016 10.0832L12.1668 5.9165' stroke='%239CA3AF' stroke-width='1.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

/*
 * .gx-tooltip-* — ayuda junto al label sin `title` nativo.
 *
 * Por qué: el tooltip del browser tiene delay, estilo inconsistente y a veces
 * no aparece si el pointer “cruza” mal la zona. Este panel es CSS puro
 * (hover + focus-within), sin JS, aparece en el acto y respeta tema claro/oscuro.
 *
 * Uso: el trigger debe ser <button type="button"> para no enviar formularios.
 * El panel va después del botón, mismo .gx-tooltip-wrap. Conectá id +
 * aria-describedby para lectores de pantalla.
 *
 * Ojo: un ancestro con overflow:hidden puede recortar el panel; en ese caso
 * mové el wrap fuera del contenedor recortado o abrí el panel hacia adentro
 * (cambiá top/left en una variante local si hace falta).
 */
.gx-tooltip-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
  vertical-align: middle;
}

.gx-tooltip-trigger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  line-height: 1;
  border-radius: 9999px;
  cursor: help;
  color: rgb(156 163 175); /* gray-400 */
}

.gx-tooltip-trigger:hover,
.gx-tooltip-trigger:focus-visible {
  color: rgb(107 114 128); /* gray-500 */
  outline: none;
}

.gx-tooltip-trigger:focus-visible {
  box-shadow:
    0 0 0 2px rgb(255 255 255),
    0 0 0 4px rgb(60 80 224); /* ring brand-ish */
}

.dark .gx-tooltip-trigger {
  color: rgb(156 163 175);
}

.dark .gx-tooltip-trigger:hover,
.dark .gx-tooltip-trigger:focus-visible {
  color: rgb(209 213 219); /* gray-300 */
}

.dark .gx-tooltip-trigger:focus-visible {
  box-shadow:
    0 0 0 2px rgb(17 24 39),
    0 0 0 4px rgb(96 125 239);
}

.gx-tooltip-panel {
  position: absolute;
  left: 50%;
  top: 100%;
  z-index: 50;
  margin-top: 0.25rem;
  width: max-content;
  max-width: min(18rem, calc(100vw - 2rem));
  /* Explícito, y no confiando en el default: `white-space` SE HEREDA, así que un
   * ancestro con `whitespace-nowrap` (p. ej. un <th> que no quiere partir su
   * etiqueta en dos renglones) convierte el texto del panel en una sola línea
   * larguísima. El `max-width` recorta la caja pero NO el contenido, y ese
   * desborde se propaga al contenedor con overflow: aparece una barra de scroll
   * horizontal sobre una tabla que se ve completa. Medido: un solo tooltip así
   * le sumó 432 px de área scrolleable a una tabla que cabía exacta. */
  white-space: normal;
  transform: translateX(-50%);
  padding: 0.5rem 0.625rem;
  font-size: 0.75rem;
  line-height: 1.35;
  font-weight: 400;
  text-align: left;
  color: rgb(55 65 81); /* gray-700 */
  background-color: rgb(255 255 255);
  border: 1px solid rgb(229 231 235); /* gray-200 */
  border-radius: 0.5rem;
  box-shadow:
    0 4px 6px -1px rgb(0 0 0 / 0.08),
    0 2px 4px -2px rgb(0 0 0 / 0.06);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

/* Puente invisible para no perder :hover al cruzar el margen entre ícono y panel. */
.gx-tooltip-panel::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 100%;
  height: 0.375rem;
}

.gx-tooltip-wrap:hover .gx-tooltip-panel,
.gx-tooltip-wrap:focus-within .gx-tooltip-panel {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}

.dark .gx-tooltip-panel {
  color: rgb(229 231 235);
  background-color: rgb(31 41 55); /* gray-800 */
  border-color: rgb(55 65 81);
  box-shadow:
    0 10px 15px -3px rgb(0 0 0 / 0.35),
    0 4px 6px -4px rgb(0 0 0 / 0.25);
}

/* Variante: panel alineado al borde izquierdo del trigger (abre "hacia adentro").
 * Para wraps dentro de cards con overflow:hidden (ej. tablas de KPIs), donde el
 * panel centrado se recortaría por el borde izquierdo del contenedor. */
.gx-tooltip-start .gx-tooltip-panel {
  left: 0;
  transform: none;
}

/* Variante espejo: panel alineado al borde derecho del trigger (abre "hacia la
 * izquierda"). Para wraps pegados al borde derecho de un contenedor con overflow
 * (ej. la última columna de un grid dentro de un modal scrolleable), donde el
 * panel centrado o alineado a la izquierda se recortaría por el borde derecho. */
.gx-tooltip-end .gx-tooltip-panel {
  left: auto;
  right: 0;
  transform: none;
}

/* Variante: panel ARRIBA del trigger. Para campos al final de un modal scrolleable,
 * donde el panel hacia abajo se recorta contra el borde del cuerpo (o queda debajo del
 * footer fijo) y no hay más scroll disponible para revelarlo. Compone con -start/-end:
 * esta variante solo toca el eje vertical. */
.gx-tooltip-up .gx-tooltip-panel {
  top: auto;
  bottom: 100%;
  margin-top: 0;
  margin-bottom: 0.25rem;
}

/* El puente invisible también se voltea: si no, al bajar el pointer hacia el panel se
 * pierde el :hover en el hueco del margen. */
.gx-tooltip-up .gx-tooltip-panel::before {
  bottom: auto;
  top: 100%;
}

/* Chrome/Safari: ocultar el chevron nativo si el browser lo intenta dibujar igual. */
.gx-select::-ms-expand {
  display: none;
}

/* Checkboxes/radios: anillo brand sutil en lugar del azul nativo del browser.
 * Los inputs/textarea/select normales se cubren más abajo (sección "[type='text']:focus...")
 * con un outline brand consistente que NO se recorta en modales scrolleables. */
input[type="checkbox"]:focus-visible,
input[type="radio"]:focus-visible {
  outline: 2px solid var(--color-brand-500);
  outline-offset: 2px;
}

/* Color de marca en el relleno del check. El bundle NO setea accent-color, así
 * que el navegador pinta su azul nativo; y las utilities text-brand-* NO tiñen
 * un control nativo (eso solo aplica con appearance:none + check custom). Regla
 * global → todos los checkboxes/radios usan el verde de marca, sin inline. */
input[type="checkbox"],
input[type="radio"] {
  accent-color: var(--color-brand-500);
}

/* ============================================================
 * Utilities ausentes en el bundle precompilado de TailAdmin.
 * El bundle es estático (no hay tailwind.config + build local), así que
 * agregamos acá las clases verificadamente necesarias y de uso transversal.
 *
 * Convención: antes de SUMAR una utility nueva, verificar que no exista en
 * el bundle:
 *     grep -F '.la-clase' public/assets/css/style.css
 * Y si solo se usa una vez en todo el proyecto, mejor inline-style en lugar
 * de polucionar este archivo.
 * ============================================================ */

/* min-w-0 — sin esto `truncate` no recorta dentro de flex/grid items y el
 * contenido se desborda sobre la celda vecina. Crítico para data-tables. */
.min-w-0 {
  min-width: 0;
}
.min-h-0 {
  min-height: 0;
}

/* lg:w-[400px] — ancho fijo del sidebar de tickets en lg+. En mobile la
 * lista ocupa w-full (default) y se oculta con `hidden lg:flex` cuando hay
 * detalle abierto. El bundle solo trae lg:w-[634px] y lg:w-auto; ninguno
 * sirve para la lista densa de Support. */
@media (min-width: 1024px) {
  .lg\:w-\[400px\] {
    width: 400px;
  }
}

/* sm:items-end / lg:items-end — el bundle trae `items-end` e `items-center`
 * (y `sm:items-center`) pero NO las variantes `sm:items-end` ni `lg:items-end`.
 * Sin ellas, las toolbars con `sm:flex-row sm:items-end` caen a align-items:stretch
 * en desktop y los botones sin label (Buscar/Publicar) se estiran y quedan arriba.
 * Usadas en toolbars de filtros (scheduling, reporte de cumplimiento, finanzas). */
@media (min-width: 640px) {
  .sm\:items-end {
    align-items: flex-end;
  }
}
@media (min-width: 1024px) {
  .lg\:items-end {
    align-items: flex-end;
  }
}

/* sm:flex-wrap — el bundle trae `flex-wrap` base pero NO la variante `sm:`.
 * Necesaria en toolbars de filtros con muchos campos: en móvil la fila es
 * `flex-col` (apilada) y NO debe envolver; al pasar a `sm:flex-row` recién ahí
 * los campos deben poder envolver a una segunda línea. Aplicar `flex-wrap` base
 * rompería el apilado móvil, por eso se restringe al breakpoint sm. */
@media (min-width: 640px) {
  .sm\:flex-wrap {
    flex-wrap: wrap;
  }
}

/* col-span-5..11 — el bundle solo trae 1, 2, 3, 4 y 12 sin breakpoint.
 * Necesario para grids 12-col asimétricos (ej. nombre col-span-7 + acción
 * col-span-1 en data-tables con columna primaria amplia). */
.col-span-5 {
  grid-column: span 5 / span 5;
}
.col-span-6 {
  grid-column: span 6 / span 6;
}
.col-span-7 {
  grid-column: span 7 / span 7;
}
.col-span-8 {
  grid-column: span 8 / span 8;
}
.col-span-9 {
  grid-column: span 9 / span 9;
}
.col-span-10 {
  grid-column: span 10 / span 10;
}
.col-span-11 {
  grid-column: span 11 / span 11;
}

/* md:col-span-2 — el bundle solo trae md:col-span-1. Lo usa Integraciones ·
 * Meli para que el campo Redirect URI ocupe el ancho completo del grid de
 * credenciales (md:grid-cols-2). */
@media (min-width: 768px) {
  .md\:col-span-2 {
    grid-column: span 2 / span 2;
  }
}

/* xl:col-span-2 — no compilado en el bundle. Lo usa la pestaña Analítica de
 * Meli para las gráficas de ancho completo dentro de su grid xl:grid-cols-2. */
@media (min-width: 1280px) {
  .xl\:col-span-2 {
    grid-column: span 2 / span 2;
  }
}

/* grid-cols-3/5/6/7 — el bundle trae 1, 2, 4, 11, 12. Útil para KPI strips
 * de 3 o 6 elementos iguales sin tener que ir al patrón 12+col-span, y para
 * calendarios mensuales (7 = días de la semana). */
.grid-cols-3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
.grid-cols-5 {
  grid-template-columns: repeat(5, minmax(0, 1fr));
}
.grid-cols-6 {
  grid-template-columns: repeat(6, minmax(0, 1fr));
}
.grid-cols-7 {
  grid-template-columns: repeat(7, minmax(0, 1fr));
}

/* xl:grid-cols-{5,6} — el bundle solo trae xl:grid-cols-{1,2,3,4}. Los usan las barras
 * de filtros: Empleados (5) y Registro de vacaciones (6: empleado, estado, zona, puesto,
 * desde, hasta) — todos los filtros en una sola línea en pantallas anchas. */
@media (min-width: 1280px) {
  .xl\:grid-cols-5 {
    grid-template-columns: repeat(5, minmax(0, 1fr));
  }
  .xl\:grid-cols-6 {
    grid-template-columns: repeat(6, minmax(0, 1fr));
  }
}

/* grid-cols-14 + col-span-14 — para data-tables que necesitan más de 6
 * columnas visibles sin asfixiar los spans (ej. Usuarios y Roles). */
.grid-cols-14 {
  grid-template-columns: repeat(14, minmax(0, 1fr));
}
.col-span-14 {
  grid-column: span 14 / span 14;
}

/* grid-cols-15 + col-span-15 — bandeja de Candidaturas al agregar la columna
 * "Interés". El bundle tope en 12 y el header de data-table-03 emite la clase
 * de forma dinámica, así que sin estas dos el grid colapsa a una sola columna
 * SIN error visible (el navegador ignora la clase desconocida). */
.grid-cols-15 {
  grid-template-columns: repeat(15, minmax(0, 1fr));
}
.col-span-15 {
  grid-column: span 15 / span 15;
}

/* grid-cols-16 + col-span-16 — Órdenes de Meli (Integraciones) al agregar la
 * cuarta columna de estado, "Dinero". Mismo motivo que la de 15: el header de
 * data-table-03 emite `grid-cols-<n>` de forma dinámica y una clase que el
 * bundle no trae deja el grid en UNA sola columna sin error visible. */
.grid-cols-16 {
  grid-template-columns: repeat(16, minmax(0, 1fr));
}
.col-span-16 {
  grid-column: span 16 / span 16;
}

/* col-span-17 — la columna Dinero de Órdenes de Meli (17 × 10 px = 170).
 * Su rejilla `grid-cols-17` y el `min-w-[1700px]` que la acompañaban se
 * retiraron al pasar la tabla a unidades de 10 px: quedaron sin consumidor. */
.col-span-17 {
  grid-column: span 17 / span 17;
}

/* grid-cols-167 + col-span-167 y col-span-{13,18,19} — Órdenes de Meli.
 * Unidad de 10 px (167 × 10 = 1670), y no de 100 como antes, porque con spans
 * de 100 px la única reducción posible era del 50% y ahí las píldoras se
 * parten en dos renglones. Los anchos salen de MEDIR en el navegador el
 * contenido más largo de cada columna (+32 px del padding px-4), no de
 * estimarlos por número de caracteres:
 *   Orden 177 → 180 · Creada 185 → 190 · importes 122 → 130 · Acción 80 → 90.
 * Pipeline, Entrega y Dinero van con padding px-2 en vez de px-4 (16 px en vez
 * de 32), que es lo que les permite bajar a 140/170/150 sin partir la píldora:
 *   Pipeline 119+16=135 → 140 · Entrega 145+16=161 → 170 · Dinero 126+16=142 → 150.
 * En Entrega mandan DOS cosas, la píldora "Histórico sin consultar" (145) y el
 * aviso "Sin intento de entrega" (142), por eso es la que menos cede.
 * Entrega es la que menos cede: su píldora más larga es "Histórico sin
 * consultar" (145 px), que es justo la de las órdenes bajo el piso operativo,
 * o sea la mayoría del histórico.
 * ⚠️ Los importes SUBEN de 100 a 130: el 59.6% de las 235,905 órdenes pasa de
 * $1,000 y el máximo es $33,599.04, así que "$1,199.72" (90 px + 32) no cabía
 * en los 100 px que tenían — y se desbordaba sin dar error.
 * Viven aquí por lo de siempre: el header de data-table-03 emite la clase de
 * forma dinámica y una que el bundle no traiga deja el grid en UNA columna. */
.grid-cols-167 {
  grid-template-columns: repeat(167, minmax(0, 1fr));
}
.col-span-167 {
  grid-column: span 167 / span 167;
}
.col-span-13 {
  grid-column: span 13 / span 13;
}
.col-span-18 {
  grid-column: span 18 / span 18;
}
.col-span-19 {
  grid-column: span 19 / span 19;
}

/* min-w-[1670px] — el scroll horizontal de esa misma tabla: 167 × 10 px. */
.min-w-\[1670px\] {
  min-width: 1670px;
}

/* min-w-[1200px] — ancho mínimo del scroll horizontal de la tabla de
 * Usuarios y Roles (14 columnas). El bundle solo precompila 1102px. */
.min-w-\[1200px\] {
  min-width: 1200px;
}

/* min-w-[1500px] — bandeja de Candidaturas (Reclutamiento) al pasar a 14
 * columnas: mantiene el ancho por span que tenía con 12 columnas en 1300px
 * (~108px), así ninguna columna existente se angosta al agregar la nueva. */
.min-w-\[1500px\] {
  min-width: 1500px;
}

/* min-w-[1600px] — la misma bandeja con 15 columnas (se agregó "Interés").
 * Conserva el ancho por span de 1500px/14. */
.min-w-\[1600px\] {
  min-width: 1600px;
}

/* Tinte de fila por banda de interés (bandeja de Candidaturas). Va aquí y no
 * inline porque hacen falta las variantes hover Y dark de cada tramo, y el
 * bundle solo precompila bg-{success,error}-50 sin su hover.
 *
 * Solo se tiñen los EXTREMOS: si los cuatro tramos pintaran, ninguno resaltaría
 * y se perdería justo el efecto que se busca (que el ojo encuentre la fila sin
 * ir a buscar la columna). El tramo medio y el "sin datos" usan el fondo neutro
 * de siempre. */
.gx-engagement-row-high {
  background-color: var(--color-success-50, #ecfdf3);
}
.gx-engagement-row-high:hover {
  background-color: var(--color-success-100, #d1fadf);
}
.gx-engagement-row-low {
  background-color: var(--color-error-50, #fef3f2);
}
/* error-100 no está en el bundle (success-100 sí): el literal viene de la misma
 * escala de TailAdmin, no es un color inventado. */
.gx-engagement-row-low:hover {
  background-color: #fee4e2;
}
.dark .gx-engagement-row-high {
  background-color: color-mix(in srgb, var(--color-success-500, #12b76a) 10%, transparent);
}
.dark .gx-engagement-row-high:hover {
  background-color: color-mix(in srgb, var(--color-success-500, #12b76a) 18%, transparent);
}
.dark .gx-engagement-row-low {
  background-color: color-mix(in srgb, var(--color-error-500, #f04438) 12%, transparent);
}
.dark .gx-engagement-row-low:hover {
  background-color: color-mix(in srgb, var(--color-error-500, #f04438) 20%, transparent);
}

/* min-w-[900px] — ancho mínimo del scroll horizontal de tablas data-table-03
 * medianas (usado en múltiples listados; el bundle no lo precompila). */
.min-w-\[900px\] {
  min-width: 900px;
}

/* min-w-[1100px] — tablas data-table-03 de 14 columnas (historial de
 * asignaciones del empleado). Tampoco está en el bundle. */
.min-w-\[1100px\] {
  min-width: 1100px;
}

/* gap-x-3 / gap-x-4 / gap-y-1.5 — el bundle compila solo las gaps que algún
 * componente original de TailAdmin usó (gap-x-1, gap-x-3.5, gap-x-6, gap-x-8,
 * gap-y-2, gap-y-5). Sin estas, layouts con muchos items en celda quedan sin
 * separación efectiva — caso típico: matriz de permisos, columna "Acciones
 * extra" cuyo checkbox parecía pegado al label del siguiente item. */
.gap-x-3 {
  -webkit-column-gap: calc(var(--spacing) * 3);
  -moz-column-gap: calc(var(--spacing) * 3);
  column-gap: calc(var(--spacing) * 3);
}
.gap-x-4 {
  -webkit-column-gap: calc(var(--spacing) * 4);
  -moz-column-gap: calc(var(--spacing) * 4);
  column-gap: calc(var(--spacing) * 4);
}
.gap-y-1\.5 {
  row-gap: calc(var(--spacing) * 1.5);
}
/* gap-x-24 (96px) — separación entre las dos parejas de datos de la ficha de /me
 * (correo/celular y nacimiento/edad). El bundle solo llega a gap-8 y ninguna
 * variante responsiva pasa de sm:gap-9. Va como gap-x (solo column-gap) a
 * propósito: la tira es `flex-col` en móvil, donde el column-gap no aplica, así
 * que no necesita media query — el `gap-2` de al lado sigue poniendo el row-gap. */
.gap-x-24 {
  -webkit-column-gap: calc(var(--spacing) * 24);
  -moz-column-gap: calc(var(--spacing) * 24);
  column-gap: calc(var(--spacing) * 24);
}

/* py-10 / py-12 / py-16 — el bundle compila solo hasta py-7 (los componentes
 * originales de TailAdmin no usaban paddings verticales mayores). Sin estas, los
 * empty states (px-6 py-16) colapsan a la altura del contenido y el icono/texto
 * quedan pegados a los bordes de la tarjeta — caso típico: estado "Selecciona los
 * filtros y presiona Buscar" de los reportes.
 *
 * py-10 se agregó después (2026-08-20): lo escriben 52 vistas y 95 veces —casi
 * siempre la celda "Sin resultados" de una tabla— y en ninguna aplicaba. El fallo
 * es del tipo que nadie reporta: la fila se ve apretada, no rota. */
.py-10 {
  padding-top: calc(var(--spacing) * 10);
  padding-bottom: calc(var(--spacing) * 10);
}
.py-12 {
  padding-top: calc(var(--spacing) * 12);
  padding-bottom: calc(var(--spacing) * 12);
}
.py-16 {
  padding-top: calc(var(--spacing) * 16);
  padding-bottom: calc(var(--spacing) * 16);
}
/* py-8 — mismo hueco que py-12/py-16 (el bundle llega a py-7) y se usa en ~36
 * vistas del repo para el padding de estados vacíos dentro de un modal, donde
 * py-12 es demasiado. Sin esta regla el texto queda pegado a los bordes. */
.py-8 {
  padding-top: calc(var(--spacing) * 8);
  padding-bottom: calc(var(--spacing) * 8);
}

/* space-y-0.5 — el bundle compila space-y-* solo en enteros. Se usa para listas
 * densas (opciones de un reactivo, líneas de detalle) donde space-y-1 separa
 * demasiado; sin la regla las líneas quedan sin ningún aire.
 *
 * Se replica la forma del bundle (Tailwind 4: margen LÓGICO de bloque sobre
 * `> :not(:last-child)`), no el patrón v3 con `~` y `margin-top`, para que
 * convivan sin sorpresas en el mismo elemento. Se omite --tw-space-y-reverse:
 * ninguna vista del repo usa space-y-reverse. */
.space-y-0\.5 > :not(:last-child) {
  margin-block-end: calc(var(--spacing) * 0.5);
}

/* w-72 (18rem) — el bundle solo compila anchos sueltos hasta w-56 y las variantes
 * responsivas sm:w-* hasta sm:w-11. Ancho ESTÁNDAR del filtro de rango de fechas
 * de los reportes (Ventas y Utilidad / Desglose de Ingresos / Gráfico de Ventas):
 * 18rem es lo mínimo que muestra completo el valor con sufijo de semana
 * "YYYY-MM-DD al YYYY-MM-DD (Sem. NN)". */
.w-72 {
  width: calc(var(--spacing) * 72);
}

/* dark:bg-white/[0.02] y [0.04] — el bundle solo precompila las opacidades de
 * blanco que algún componente original de TailAdmin usó: /[0.03], /[0.05] y
 * /[0.07]. Las variantes 0.02 y 0.04 (más sutiles) fallan en silencio, así que
 * en modo oscuro el fondo claro de `bg-gray-50/100` se queda visible y la UI se
 * ve "rota" — caso típico: matriz de permisos (encabezados de módulo, thead y
 * hover de fila), editor bulk, schedule-admin, tickets y spotlight. Se replica
 * el patrón Tailwind v4 (selector `:is(.dark *)`) en CSS plano. */
.dark\:bg-white\/\[0\.02\]:is(.dark *) {
  background-color: rgba(255, 255, 255, 0.02);
}
.dark\:bg-white\/\[0\.04\]:is(.dark *) {
  background-color: rgba(255, 255, 255, 0.04);
}
@media (hover: hover) {
  .dark\:hover\:bg-white\/\[0\.02\]:is(.dark *):hover {
    background-color: rgba(255, 255, 255, 0.02);
  }
  .dark\:hover\:bg-white\/\[0\.04\]:is(.dark *):hover {
    background-color: rgba(255, 255, 255, 0.04);
  }
}

/* dark:bg-brand-500/[0.06] — el bundle precompila 0.08 (base) y 0.12 (dark) de
 * brand-500, pero no 0.06. Lo usan las barras de pie con acento de marca de la
 * vista de permisos (`bg-brand-50 dark:bg-brand-500/[0.06]`); sin esto el fondo
 * claro `bg-brand-50` queda visible en modo oscuro. */
.dark\:bg-brand-500\/\[0\.06\]:is(.dark *) {
  background-color: color-mix(in oklab, var(--color-brand-500) 6%, transparent);
}

/* rotate-90 — el bundle trae 0, 45, 180 y -45 pero no 90. Necesario para
 * carets de treegrid (indicador de "rama expandida"). */
.rotate-90 {
  transform: rotate(90deg);
}

/* gx-tl-dim — atenúa una línea de tiempo (barra + track) para que las filas hijas
 * (compañeros de zona en "Mis horarios") recedan frente al registro principal. El
 * bundle solo trae opacity-40/50/75; 0.6 es el punto justo (apagado pero legible). */
.gx-tl-dim {
  opacity: 0.6;
}

/* gx-evt-line / gx-evt-dash — marcador de checada real (4px) sobre la línea de tiempo
 * ("Mis horarios" y Reporte de cumplimiento). El color sale de `currentColor` (clase
 * text-*): dirección → color (entrada/regreso oscuro, salida/salida-comida rojo). El
 * estilo distingue categoría: sólida = trabajo (entrada/salida), punteada = comida
 * (salida/regreso). El bundle no trae estos anchos ni border-l-*, por eso van acá. */
.gx-evt-line {
  width: 4px;
  background-color: currentColor;
}
.gx-evt-dash {
  width: 4px;
  background-image: repeating-linear-gradient(
    to bottom,
    currentColor 0 3px,
    transparent 3px 6px
  );
}

/* bottom-4 / top-4 (top-4 ya existe en el bundle, bottom-4 no) — necesarias
 * para anclar contenedores fijos a 1rem del borde inferior. El bundle trae
 * bottom-0/5/6/10/full pero no bottom-4 (la escala más usada). Lo usa el
 * contenedor global de toasts y cualquier overlay flotante. */
.bottom-4 {
  bottom: calc(var(--spacing) * 4);
}

/* pointer-events-{none,auto} — none existe pero auto no. Necesario para
 * "contenedor sin captura + items con captura" (toast stack: el wrapper deja
 * pasar clicks al fondo, cada toast los captura). */
.pointer-events-auto {
  pointer-events: auto;
}

/* left-1/2 + translate-y-{0,2,-2} — para centrar contenedores horizontalmente
 * (top-center / bottom-center del toast stack) y para animaciones de slide-in
 * verticales (x-transition de Alpine). El bundle trae -translate-x-1/2 y
 * translate-x-0 pero no los hermanos. */
.left-1\/2 {
  left: 50%;
}
.translate-y-0 {
  --tw-translate-y: 0;
  transform: translate3d(var(--tw-translate-x, 0), var(--tw-translate-y, 0), 0);
}
.translate-y-2 {
  --tw-translate-y: calc(var(--spacing) * 2);
  transform: translate3d(var(--tw-translate-x, 0), var(--tw-translate-y, 0), 0);
}
.-translate-y-2 {
  --tw-translate-y: calc(var(--spacing) * -2);
  transform: translate3d(var(--tw-translate-x, 0), var(--tw-translate-y, 0), 0);
}

/* ============================================================
 * Toasts globales — utility para el botón CTA dentro del toast.
 *
 * Por qué un utility custom y no clases Tailwind: el bundle no incluye
 * `hover:underline` ni hovers de paleta extendida (success-800, error-800,
 * blue-light-800, warning-800). Encapsulamos el patrón "link-like sobre toast"
 * en una sola clase que se combina con el color base que sí está en el bundle
 * (text-success-700, text-error-700, etc.). Tipografía y peso vienen del
 * markup (text-sm font-semibold).
 * ============================================================ */
.gx-toast-cta {
  text-underline-offset: 2px;
}
.gx-toast-cta:hover {
  text-decoration: underline;
  filter: brightness(0.85);
}

/* Barra de progreso del auto-dismiss.
 *
 * Se anima vía CSS (no JS) para que el navegador la pinte en su capa de
 * compositor — el thread JS puede estar ocupado y la barra sigue fluida.
 * `transform-origin: left` + `scaleX` (no width) evita reflows.
 *
 * Pause: cuando el usuario hace hover, el store seta `_paused: true` en el
 * item y nosotros congelamos la animación con `animation-play-state`. Al salir,
 * vuelve a `running` y la animación retoma desde el progreso exacto que tenía
 * (esto lo maneja el browser, no requiere recalcular tiempo restante). El JS
 * de pausa/resume del timer va en paralelo y queda en sync porque ambos
 * eventos (mouseenter/leave) disparan en el mismo instante.
 *
 * La duración se pasa por inline-style (`animation-duration`) para que cada
 * toast use su propio timeoutMs sin generar N reglas CSS. */
.gx-toast-progress {
  position: absolute;
  left: 0;
  bottom: 0;
  height: 3px;
  width: 100%;
  transform-origin: left;
  animation: gx-toast-shrink linear forwards;
  /* Opacidad sobre el color del border del toast — funciona en claro y oscuro. */
  background-color: currentColor;
  opacity: 0.45;
  pointer-events: none;
}
.gx-toast-progress.is-paused {
  animation-play-state: paused;
}
@keyframes gx-toast-shrink {
  from {
    transform: scaleX(1);
  }
  to {
    transform: scaleX(0);
  }
}

/* ============================================================
 * Flatpickr — fix de alineación de la columna "Sem" (week numbers).
 *
 * Problema: TailAdmin override (style.css ~7382) le da margin-top:24px y
 * margin-bottom:16px a `.flatpickr-weekdays` (la fila Lun-Dom de la derecha),
 * pero no aplica el mismo margen al header "Sem" del lado izquierdo
 * (`.flatpickr-weekwrapper > span.flatpickr-weekday`), porque está fuera
 * del contenedor `.flatpickr-weekdays`.
 *
 * Resultado visible: la columna entera de números de semana queda corrida
 * una fila hacia arriba — "Sem 18" aparece a la altura de "Lun Mar Mié...".
 *
 * Fix: replicar el mismo margin en el header del wrapper de semanas.
 * Si en el futuro se reemplaza el bundle y este override desaparece, esta
 * regla se vuelve no-op y se puede limpiar.
 * ============================================================ */
.flatpickr-weekwrapper .flatpickr-weekday {
  margin-top: calc(var(--spacing) * 6);
  margin-bottom: calc(var(--spacing) * 4);
}

/* ============================================================
 * Flatpickr — barra de presets de rango (gx.datepicker en mode:'range').
 *
 * gx.datepicker envuelve el contenido nativo de flatpickr en un <div
 * class="gx-fp-body"> e inyecta un <div class="gx-fp-presets"> como primer hijo
 * del .flatpickr-calendar, marcándolo con .gx-has-presets. Layout: flex de 2
 * columnas (barra | calendario). Solo se activa con .open para no mostrar el
 * popup cuando está cerrado. El wrapper lleva position:relative para re-anclar
 * las flechas prev/next (position:absolute) a la columna del calendario y no al
 * popup completo (si no, la flecha izquierda cae sobre la barra de presets).
 * ============================================================ */
.flatpickr-calendar.gx-has-presets.open {
  display: flex;
  align-items: flex-start;
  width: auto !important; /* flatpickr fija un width inline para multiMonth; aquí dejamos que el contenido mande */
}
.flatpickr-calendar.gx-has-presets > .gx-fp-body {
  position: relative;
  flex: 0 0 auto;
}

.gx-fp-presets {
  flex: 0 0 150px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  align-self: stretch;
  padding: 10px 8px;
  border-right: 1px solid var(--color-gray-200, #e5e7eb);
  background: var(--color-gray-50, #f9fafb);
  overflow-y: auto;
}
.gx-fp-preset {
  display: block;
  width: 100%;
  text-align: left;
  padding: 7px 10px;
  font-size: 13px;
  line-height: 1.2;
  color: var(--color-gray-700, #344054);
  border-radius: 6px;
  background: transparent;
  cursor: pointer;
  white-space: nowrap;
  transition: background-color 0.12s ease;
}
.gx-fp-preset:hover {
  background: var(--color-gray-100, #f2f4f7);
}
.gx-fp-preset.is-active {
  background: var(--color-brand-500, #71a83b);
  color: #fff;
}

.dark .gx-fp-presets {
  border-right-color: var(--color-gray-800, #1d2939);
  background: var(--color-gray-900, #101828);
}
.dark .gx-fp-preset {
  color: var(--color-gray-300, #d0d5dd);
}
.dark .gx-fp-preset:hover {
  background: rgba(255, 255, 255, 0.06);
}
.dark .gx-fp-preset.is-active {
  background: var(--color-brand-500, #71a83b);
  color: #fff;
}

/* Responsive: en pantallas angostas la barra pasa arriba como fila scrollable
   (gx.datepicker ya baja a 1 mes en móvil). */
@media (max-width: 639px) {
  .flatpickr-calendar.gx-has-presets.open {
    flex-direction: column;
  }
  .flatpickr-calendar.gx-has-presets > .gx-fp-presets {
    flex: 0 0 auto;
    flex-direction: row;
    width: auto;
    align-self: stretch;
    border-right: 0;
    border-bottom: 1px solid var(--color-gray-200, #e5e7eb);
    overflow-x: auto;
  }
  .dark .flatpickr-calendar.gx-has-presets > .gx-fp-presets {
    border-bottom-color: var(--color-gray-800, #1d2939);
  }
}

/* ============================================================
 * Form controls — focus ring que no se recorta dentro de overflow:auto.
 *
 * Problema: TailAdmin (style.css ~L8133) aplica el ring de focus a inputs/
 * textareas/selects vía `box-shadow` (1px ring). Cuando el control está
 * dentro de un contenedor con `overflow-y-auto` (modales con header/footer
 * fijos + body scrolleable), el box-shadow se recorta al edge del clip
 * rectangle del scroll container → el ring aparece incompleto en el borde
 * superior o inferior según dónde quedó pegado el input.
 *
 * Fix: reemplazar el ring de box-shadow por `outline`. Los outlines se
 * pintan en una capa separada y NO se recortan por overflow del padre.
 * Mantenemos el cambio de border-color (es el border del propio control,
 * no se recorta) y suprimimos el outline transparente que TailAdmin usa
 * como spacer.
 *
 * Aplicamos solo a los tipos de control que TailAdmin estilea en su capa
 * @layer base (mismo selector que el bundle), para no afectar al picker
 * de fechas (flatpickr renderiza un <input> que ya tiene su propio focus).
 * ============================================================ */
[type="text"]:focus,
input:where(:not([type])):focus,
[type="email"]:focus,
[type="url"]:focus,
[type="password"]:focus,
[type="number"]:focus,
[type="search"]:focus,
[type="tel"]:focus,
[type="datetime-local"]:focus,
[type="month"]:focus,
[type="time"]:focus,
[type="week"]:focus,
[multiple]:focus,
textarea:focus,
select:focus {
  /* Brand verde del proyecto (override de --color-brand-500 = #71a83b).
     * Usamos outline en lugar de box-shadow para que el ring no se recorte
     * por el overflow:auto de modales scrolleables (issue conocido en flatpickr-
     * embedded forms y modales con header/footer fijos). */
  outline: 1px solid var(--color-brand-500);
  outline-offset: 0;
  border-color: var(--color-brand-500);
  box-shadow: var(--tw-inset-shadow, 0 0 #0000), var(--tw-shadow, 0 0 #0000);
}

/* ============================================================
 * flatpickr dentro de modales teleportados (x-teleport="body").
 *
 * El popup de flatpickr se monta en <body> con z-index:99999 (default del
 * bundle). Nuestros modales se teleportan al body con z-999999 (para tapar el
 * header), así que el calendario quedaba DETRÁS del modal. Lo subimos por
 * encima. Solo el popup flotante (no el inline de vacaciones).
 * ============================================================ */
.flatpickr-calendar.open:not(.inline) {
  z-index: 1000000;
}

/* ============================================================
 * gxa-* — componentes Alpine-driven (gx + alpine).
 *
 * Distintos del bundle estático (TailAdmin compilado) y de los helpers gx.*
 * (estado puro, sin UI). Estos se construyen en tiempo de render con HTML
 * dinámico generado por Alpine. Convención: prefijo `.gxa-<componente>__*`
 * BEM-ish para evitar choques con el bundle.
 *
 * Componentes:
 *   - .gxa-combobox  — select rico (píldora + label + chip secundario + badges)
 * ============================================================ */

/* ---- gxa-combobox ---- */

.gxa-combobox {
  position: relative;
}

.gxa-combobox__trigger {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  min-width: 0;
  min-height: 2.75rem;
  padding: 0.5rem 0.75rem;
  border: 1px solid rgb(209 213 219); /* gray-300 — match h-11 selects */
  border-radius: 0.5rem;
  background: transparent;
  font-size: 0.875rem;
  text-align: left;
  cursor: pointer;
  transition: border-color 0.15s;
}
.dark .gxa-combobox__trigger {
  border-color: rgb(55 65 81); /* gray-700 */
}
/* Variante compacta opt-in: trigger ~20% más bajo (2.75rem → 2.2rem). */
.gxa-combobox--compact .gxa-combobox__trigger {
  min-height: 2.2rem;
  padding-top: 0.375rem;
  padding-bottom: 0.375rem;
}
.gxa-combobox__trigger:hover {
  border-color: rgb(156 163 175); /* gray-400 */
}
.gxa-combobox__trigger--open {
  border-color: var(--color-brand-500);
}
.gxa-combobox__trigger--error {
  border-color: rgb(240 68 56); /* error-500 */
}

.gxa-combobox__trigger-content {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex: 1;
  min-width: 0;
  flex-wrap: nowrap;
  /* Clipa solo el área del texto (el label trunca con elipsis); los iconos
     * —flecha y limpiar— son hermanos y conservan su espacio (shrink-0). */
  overflow: hidden;
}
/* El wrapper interno (single: píldora + label + chip) también en una sola
   línea; sin esto la píldora de clave y el nombre se apilan a dos renglones. */
.gxa-combobox__trigger-content > span.contents {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
  flex: 1;
}

.gxa-combobox__trigger-label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: rgb(31 41 55); /* gray-800 */
}
.dark .gxa-combobox__trigger-label {
  color: rgb(255 255 255 / 0.9);
}

.gxa-combobox__placeholder {
  color: rgb(31 41 55); /* gray-800 — match native <select> text */
}
.dark .gxa-combobox__placeholder {
  color: rgb(255 255 255 / 0.9);
}

/* Modo chips (multi): píldoras seleccionadas visibles en el trigger, con × para
   quitar. El color de cada chip lo aplica badgeClasses() (Tailwind); aquí solo
   layout. El trigger crece en alto al envolver (min-height del trigger). */
.gxa-combobox__chips {
  display: flex;
  flex: 1;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem;
  min-width: 0;
}
.gxa-combobox__chip {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  max-width: 100%;
  border-radius: 9999px;
  padding: 0.125rem 0.25rem 0.125rem 0.5rem;
  font-size: 0.75rem;
  font-weight: 500;
  line-height: 1.25rem;
}
.gxa-combobox__chip-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.gxa-combobox__chip-x {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 1rem;
  width: 1rem;
  border-radius: 9999px;
  opacity: 0.7;
  cursor: pointer;
  flex-shrink: 0;
}
.gxa-combobox__chip-x:hover {
  opacity: 1;
  background: rgb(0 0 0 / 0.08);
}
.dark .gxa-combobox__chip-x:hover {
  background: rgb(255 255 255 / 0.15);
}

.gxa-combobox__trigger-icons {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  flex-shrink: 0;
  color: rgb(107 114 128); /* gray-500 */
}

.gxa-combobox__clear {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.25rem;
  height: 1.25rem;
  border-radius: 9999px;
  background: transparent;
  color: rgb(107 114 128);
  cursor: pointer;
  border: 0;
  padding: 0;
  line-height: 1;
}
.gxa-combobox__clear:hover {
  background: rgb(243 244 246);
  color: rgb(31 41 55);
}
.dark .gxa-combobox__clear:hover {
  background: rgb(31 41 55);
  color: rgb(255 255 255 / 0.9);
}

.gxa-combobox__chevron {
  width: 16px;
  height: 16px;
  transition: transform 0.15s;
}

.gxa-combobox__menu {
  /* position: fixed + coords por JS (gxa-combobox.js _positionMenu): así el
     * menú escapa el overflow-hidden de la tarjeta data-table-03, que lo
     * recortaba cuando la tabla se achicaba al filtrar. Del inline :style
     * vienen left/width, el ancla vertical (top si abre hacia abajo, bottom si
     * se voltea hacia arriba por falta de espacio) y el max-height calculado
     * contra el viewport; aquí solo el resto. El max-height de abajo es el
     * valor previo a abrir — al abrir siempre lo pisa el inline. */
  position: fixed;
  z-index: 60;
  /* El menú nunca es más angosto que ~24rem aunque el trigger sea chico
     * (ej. filtros estrechos): así las opciones con código + label largo +
     * chip de tipo entran cómodas. min() evita desborde en pantallas chicas. */
  min-width: min(24rem, calc(100vw - 2rem));
  max-height: min(40rem, 80vh);
  display: flex;
  flex-direction: column;
  border: 1px solid rgb(229 231 235);
  border-radius: 0.5rem;
  background: rgb(255 255 255);
  box-shadow:
    0 10px 15px -3px rgb(0 0 0 / 0.1),
    0 4px 6px -4px rgb(0 0 0 / 0.1);
  overflow: hidden;
}
.dark .gxa-combobox__menu {
  border-color: rgb(31 41 55);
  background: rgb(17 24 39);
}

/* Píldoras de filtro rápido (ej. tipo de zona) debajo del buscador. Click =
 * selección masiva. El color "activo" lo aporta badgeClasses() inline; el
 * estado --off es el reposo (outline gris). */
.gxa-combobox__kinds {
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
  padding: 0.5rem;
  border-bottom: 1px solid rgb(229 231 235); /* gray-200 */
}
.dark .gxa-combobox__kinds {
  border-bottom-color: rgb(31 41 55);
}
.gxa-combobox__kind {
  display: inline-flex;
  align-items: center;
  border-radius: 9999px;
  padding: 0.125rem 0.625rem;
  font-size: 0.75rem;
  font-weight: 500;
  line-height: 1.25rem;
  border: 1px solid transparent;
  cursor: pointer;
  transition:
    background-color 0.1s,
    color 0.1s,
    border-color 0.1s;
}
.gxa-combobox__kind--off {
  background: transparent;
  color: rgb(107 114 128); /* gray-500 */
  border-color: rgb(209 213 219); /* gray-300 */
}
.dark .gxa-combobox__kind--off {
  color: rgb(156 163 175); /* gray-400 */
  border-color: rgb(55 65 81); /* gray-700 */
}

/* Control de estado (Activas/Inactivas/Todas) debajo del buscador. */
.gxa-combobox__status {
  display: flex;
  gap: 0.25rem;
  padding: 0.5rem 0.5rem 0;
}
.gxa-combobox__status button {
  flex: 1;
  padding: 0.25rem 0.5rem;
  font-size: 0.6875rem;
  font-weight: 500;
  border-radius: 0.375rem;
  border: 1px solid rgb(229 231 235); /* gray-200 */
  background: transparent;
  color: rgb(107 114 128); /* gray-500 */
  cursor: pointer;
  transition:
    background-color 0.1s,
    color 0.1s,
    border-color 0.1s;
}
.dark .gxa-combobox__status button {
  border-color: rgb(55 65 81);
  color: rgb(156 163 175);
}
.gxa-combobox__status button.is-active {
  background: var(--color-brand-500, rgb(70 95 255));
  border-color: var(--color-brand-500, rgb(70 95 255));
  color: #fff;
}

/* Foco/estado por opción (getDot). Punto de 8px; color por modificador. */
.gxa-combobox__dot {
  flex-shrink: 0;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 9999px;
}
.gxa-combobox__dot--success {
  background: rgb(18 183 106);
}
.gxa-combobox__dot--gray {
  background: rgb(152 162 179);
}
.gxa-combobox__dot--error {
  background: rgb(240 68 56);
}
.gxa-combobox__dot--warning {
  background: rgb(247 144 9);
}
.gxa-combobox__dot--brand {
  background: rgb(70 95 255);
}
.gxa-combobox__dot--info {
  background: rgb(54 191 250);
}

.gxa-combobox__search {
  padding: 0.5rem;
  border-bottom: 1px solid rgb(243 244 246);
}
.dark .gxa-combobox__search {
  border-bottom-color: rgb(31 41 55);
}

.gxa-combobox__search input {
  width: 100%;
  padding: 0.5rem 0.75rem;
  border: 1px solid rgb(229 231 235);
  border-radius: 0.375rem;
  background: transparent;
  font-size: 0.875rem;
  color: inherit;
}
.dark .gxa-combobox__search input {
  border-color: rgb(31 41 55);
}
.gxa-combobox__search input:focus {
  outline: none;
  border-color: rgb(70 95 255);
  box-shadow: 0 0 0 3px rgb(70 95 255 / 0.1);
}

.gxa-combobox__list {
  flex: 1;
  overflow-y: auto;
  margin: 0;
  padding: 0.25rem;
  list-style: none;
}

/* Agrupación (groupBy): bloque sin estilo propio + encabezado sticky. La
 * lista anidada resetea márgenes/padding para que las opciones se alineen
 * igual que en modo plano. */
.gxa-combobox__group-block {
  list-style: none;
}
.gxa-combobox__group-list {
  margin: 0;
  padding: 0;
  list-style: none;
}
.gxa-combobox__group {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0.375rem 0.625rem 0.25rem;
  font-size: 0.6875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: rgb(107 114 128); /* gray-500 */
  background: #fff;
}
.dark .gxa-combobox__group {
  color: rgb(156 163 175); /* gray-400 */
  background: rgb(17 24 39); /* gray-900 — match menu */
}
/* Encabezado de grupo como selector masivo (multi + groupSelectable). */
.gxa-combobox__group--selectable {
  cursor: pointer;
  border-radius: 0.375rem;
  transition: background-color 0.1s;
}
.gxa-combobox__group--selectable:hover {
  background: rgb(245 247 255); /* brand-50 */
}
.dark .gxa-combobox__group--selectable:hover {
  background: rgb(70 95 255 / 0.12);
}

.gxa-combobox__option {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.625rem;
  border-radius: 0.375rem;
  cursor: pointer;
  transition: background-color 0.1s;
}
.gxa-combobox__option--active {
  background-color: rgb(245 247 255); /* brand-50 */
}
.dark .gxa-combobox__option--active {
  background-color: rgb(70 95 255 / 0.12);
}
.gxa-combobox__option--disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.gxa-combobox__option--selected {
  background-color: rgb(
    245 247 255
  ); /* brand-50 — sutil para el seleccionado */
}
.dark .gxa-combobox__option--selected {
  background-color: rgb(70 95 255 / 0.08);
}

/* Checkbox visual para modo multi-select. Caja 16x16 con borde gris;
 * cuando --checked, fondo brand y SVG check visible. */
.gxa-combobox__check {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  border: 1px solid rgb(209 213 219); /* gray-300 */
  border-radius: 0.25rem;
  background: #fff;
  color: transparent;
  transition:
    background-color 0.1s,
    border-color 0.1s,
    color 0.1s;
}
.dark .gxa-combobox__check {
  border-color: rgb(55 65 81); /* gray-700 */
  background: rgb(17 24 39); /* gray-900 */
}
.gxa-combobox__check--checked {
  background: var(--color-brand-500, rgb(70 95 255));
  border-color: var(--color-brand-500, rgb(70 95 255));
  color: #fff;
}
.dark .gxa-combobox__check--checked {
  background: var(--color-brand-500, rgb(70 95 255));
  border-color: var(--color-brand-500, rgb(70 95 255));
  color: #fff;
}

/* Cada opción en dos renglones: el nombre ocupa el primer renglón completo
 * (order:-1 + flex-basis:100%); la clave (pill), el tipo (chip) y los badges
 * envuelven juntos en el segundo. row-gap separa las dos líneas, column-gap
 * separa los chips entre sí. El nombre no se trunca: envuelve si fuera enorme. */
.gxa-combobox__option-main {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem 0.375rem;
  flex: 1 1 auto;
  min-width: 0;
}

.gxa-combobox__option-label {
  order: -1;
  flex-basis: 100%;
  white-space: normal;
  overflow-wrap: anywhere;
  font-size: 0.875rem;
  font-weight: 400;
  color: rgb(31 41 55);
}
.dark .gxa-combobox__option-label {
  color: rgb(255 255 255 / 0.9);
}

.gxa-combobox__option-secondary {
  font-size: 0.75rem;
  color: rgb(107 114 128);
}
.dark .gxa-combobox__option-secondary {
  color: rgb(156 163 175);
}

.gxa-combobox__empty {
  padding: 1rem;
  text-align: center;
  color: rgb(107 114 128);
  font-size: 0.875rem;
}

/* ============================================================
 * Vacation calendar — color-coded blocked days (flatpickr inline + mode:multiple)
 * Usado en app/Modules/Rh/Views/vacations/my_requests.php para que el usuario
 * marque uno o más días de descanso. Las clases las agrega `onDayCreate` del
 * datepicker leyendo el calendar API (/rh/vacation-requests/calendar).
 * Receta: docs/VACATION-CALENDAR-RECIPE.md.
 * ============================================================ */
/* Chip de leyenda: replica el tratamiento de las celdas (rounded-rect)
 * para que el usuario asocie el chip con el día real en el calendario. */
.gx-legend-chip {
  display: inline-block;
  width: 14px;
  height: 14px;
  border-radius: 4px;
  flex-shrink: 0;
}
.gx-legend-chip.gx-day-rest {
  background: repeating-linear-gradient(
    135deg,
    rgba(107, 114, 128, 0.35) 0 3px,
    rgba(107, 114, 128, 0.1) 3px 6px
  );
}
.gx-legend-chip.gx-day-holiday {
  background: rgba(245, 158, 11, 0.45);
}
/* holiday-off = festivo que además es NO laborable en el horario publicado del empleado.
 * Ámbar (festivo) + rayado diagonal (el mismo recurso visual que `rest` usa para "no
 * laborable"), porque a diferencia de `holiday` este día NO se puede pedir. */
.gx-legend-chip.gx-day-holiday-off {
  background: repeating-linear-gradient(
    135deg,
    rgba(245, 158, 11, 0.5) 0 3px,
    rgba(245, 158, 11, 0.15) 3px 6px
  );
}
.gx-legend-chip.gx-day-pending {
  background: rgba(245, 158, 11, 0.3);
  border: 1px dashed rgb(217 119 6);
}
.gx-legend-chip.gx-day-approved {
  background: rgba(34, 197, 94, 0.55);
}
.gx-legend-chip.gx-day-rejected {
  background: rgba(239, 68, 68, 0.45);
}
.gx-legend-chip.gx-day-peer-blocked {
  background: rgba(59, 130, 246, 0.45);
}
.gx-legend-chip.gx-day-min-notice {
  background: rgba(100, 116, 139, 0.2);
  border: 1px dashed rgb(100 116 139);
}
.gx-legend-chip.gx-day-blocked-admin {
  background: rgba(168, 85, 247, 0.45);
}
.gx-legend-chip.gx-day-incapacity {
  background: rgba(244, 63, 94, 0.45);
}
.gx-legend-chip.gx-day-period-locked {
  background: rgba(100, 116, 139, 0.4);
}
/* vacation-taken: el propio empleado ya tiene vacaciones ese día (pendientes o
 * aprobadas). Solo aparece en el calendario de CUMPLEAÑOS y necesita color propio:
 * pintarlo como `approved` lo confundiría con "mi día de cumpleaños ya aprobado",
 * que es justo la otra cosa que ese calendario puede decir. */
.gx-legend-chip.gx-day-vacation-taken {
  background: rgba(20, 184, 166, 0.45);
}

/* Estado base: días no seleccionables del calendario de vacaciones.
 * Diferenciación visual respecto a la selección:
 *   - estados (rest/holiday/pending/approved/rejected) usan rounded-rect (radius 6px)
 *   - selección del usuario y "hoy" mantienen el círculo brand de flatpickr
 * Esto evita que un sábado de descanso se confunda con un día ya seleccionado. */
.flatpickr-day.gx-day-rest,
.flatpickr-day.gx-day-holiday,
.flatpickr-day.gx-day-holiday-off,
.flatpickr-day.gx-day-pending,
.flatpickr-day.gx-day-approved,
.flatpickr-day.gx-day-rejected,
.flatpickr-day.gx-day-peer-blocked,
.flatpickr-day.gx-day-min-notice,
.flatpickr-day.gx-day-blocked-admin,
.flatpickr-day.gx-day-incapacity,
.flatpickr-day.gx-day-vacation-taken,
.flatpickr-day.gx-day-period-locked {
  border-radius: 6px;
  border-color: transparent;
  font-weight: 500;
}
/* peer-blocked, min-notice y blocked-admin son seleccionables para RH (override con
 * confirmación), por eso no fuerzan cursor:not-allowed; cuando flatpickr los deshabilita
 * (usuario normal) aplica su propio .flatpickr-disabled. */
/* 'holiday' NO va aquí: es marca solo visual y el día sigue siendo seleccionable.
 * 'holiday-off' SÍ va: es un día no laborable del horario publicado (el server lo
 * rechaza con NOT_A_WORK_DAY), así que se comporta como 'rest'. */
.flatpickr-day.gx-day-rest,
.flatpickr-day.gx-day-holiday-off,
.flatpickr-day.gx-day-pending,
.flatpickr-day.gx-day-approved,
.flatpickr-day.gx-day-rejected,
.flatpickr-day.gx-day-incapacity,
.flatpickr-day.gx-day-vacation-taken,
.flatpickr-day.gx-day-period-locked {
  cursor: not-allowed;
}

.flatpickr-day.gx-day-rest {
  background: repeating-linear-gradient(
    135deg,
    rgba(107, 114, 128, 0.1) 0 4px,
    rgba(107, 114, 128, 0.04) 4px 8px
  );
  color: rgb(75 85 99);
}
.flatpickr-day.gx-day-holiday {
  background: rgba(245, 158, 11, 0.18);
  color: rgb(146 64 14);
}
.flatpickr-day.gx-day-holiday-off {
  background: repeating-linear-gradient(
    135deg,
    rgba(245, 158, 11, 0.28) 0 4px,
    rgba(245, 158, 11, 0.08) 4px 8px
  );
  color: rgb(146 64 14);
}
.flatpickr-day.gx-day-pending {
  background: rgba(245, 158, 11, 0.3);
  color: rgb(146 64 14);
  border: 1px dashed rgb(217 119 6);
}
.flatpickr-day.gx-day-approved {
  background: rgba(34, 197, 94, 0.28);
  color: rgb(21 128 61);
}
.flatpickr-day.gx-day-rejected {
  background: rgba(239, 68, 68, 0.2);
  color: rgb(185 28 28);
  text-decoration: line-through;
}
.flatpickr-day.gx-day-peer-blocked {
  background: rgba(59, 130, 246, 0.22);
  color: rgb(29 78 216);
}
.flatpickr-day.gx-day-min-notice {
  background: rgba(100, 116, 139, 0.12);
  color: rgb(100 116 139);
  border: 1px dashed rgba(100, 116, 139, 0.55);
}
.flatpickr-day.gx-day-blocked-admin {
  background: rgba(168, 85, 247, 0.18);
  color: rgb(126 34 206);
}
.flatpickr-day.gx-day-incapacity {
  background: rgba(244, 63, 94, 0.2);
  color: rgb(190 24 93);
}
/* vacation-taken: solo en el calendario de cumpleaños. Teal, para no confundirse ni
 * con el verde de "aprobado" ni con el rosa de incapacidad. */
.flatpickr-day.gx-day-vacation-taken {
  background: rgba(20, 184, 166, 0.2);
  color: rgb(15 118 110);
}
/* period-locked: período aún no abierto (arranca a más de la ventana de anticipación).
 * Bloqueo duro para todos (no overridable), por eso va con cursor:not-allowed. Tono
 * slate SÓLIDO para distinguirlo de min-notice (slate punteado). */
.flatpickr-day.gx-day-period-locked {
  background: rgba(100, 116, 139, 0.16);
  color: rgb(71 85 105);
}

/* Mantener color al hover (sobreescribe el hover gris default de flatpickr) */
.flatpickr-day.gx-day-rest:hover,
.flatpickr-day.gx-day-holiday:hover,
.flatpickr-day.gx-day-holiday-off:hover,
.flatpickr-day.gx-day-pending:hover,
.flatpickr-day.gx-day-approved:hover,
.flatpickr-day.gx-day-rejected:hover,
.flatpickr-day.gx-day-peer-blocked:hover,
.flatpickr-day.gx-day-min-notice:hover,
.flatpickr-day.gx-day-blocked-admin:hover,
.flatpickr-day.gx-day-incapacity:hover,
.flatpickr-day.gx-day-vacation-taken:hover,
.flatpickr-day.gx-day-period-locked:hover {
  background-color: inherit;
  border-color: transparent;
}
.flatpickr-day.gx-day-min-notice:hover {
  border: 1px dashed rgba(100, 116, 139, 0.55);
}
.flatpickr-day.gx-day-pending:hover {
  border: 1px dashed rgb(217 119 6);
}

/* Día peer-blocked seleccionado por RH (override): conserva el fill verde de
 * "seleccionado" pero le suma un anillo azul para señalar que es un conflicto
 * forzado. El fill viene del tema (!important), por eso solo tocamos el box-shadow. */
.flatpickr-day.gx-day-peer-blocked.selected,
.flatpickr-day.gx-day-peer-blocked.selected:hover,
.flatpickr-day.gx-day-peer-blocked.selected:focus {
  box-shadow: inset 0 0 0 2px rgb(59 130 246) !important;
}

/* Día min-notice seleccionado por RH (override): mismo principio que peer-blocked,
 * con anillo slate para señalar que se forzó la anticipación mínima. */
.flatpickr-day.gx-day-min-notice.selected,
.flatpickr-day.gx-day-min-notice.selected:hover,
.flatpickr-day.gx-day-min-notice.selected:focus {
  box-shadow: inset 0 0 0 2px rgb(100 116 139) !important;
}

/* Día blocked-admin seleccionado por RH (override): anillo violeta para señalar que se
 * forzó un período bloqueado (temporada alta / política). */
.flatpickr-day.gx-day-blocked-admin.selected,
.flatpickr-day.gx-day-blocked-admin.selected:hover,
.flatpickr-day.gx-day-blocked-admin.selected:focus {
  box-shadow: inset 0 0 0 2px rgb(168 85 247) !important;
}

/* Ancho fijo de la columna de compañeros en el modal de solicitud (no está en el bundle). */
@media (min-width: 1024px) {
  .lg\:w-72 {
    width: 18rem;
  }
}

/* "Hoy" — borde brand para diferenciarlo del estado de descanso (gris).
 * flatpickr aplica .today por defecto pero con borde gris poco legible. */
.flatpickr-day.today:not(.selected) {
  border: 1.5px solid var(--color-brand-500);
  color: var(--color-brand-500);
  background: transparent;
}
.flatpickr-day.today.gx-day-rest,
.flatpickr-day.today.gx-day-holiday,
.flatpickr-day.today.gx-day-holiday-off,
.flatpickr-day.today.gx-day-pending,
.flatpickr-day.today.gx-day-approved,
.flatpickr-day.today.gx-day-rejected,
.flatpickr-day.today.gx-day-min-notice,
.flatpickr-day.today.gx-day-blocked-admin,
.flatpickr-day.today.gx-day-incapacity,
.flatpickr-day.today.gx-day-vacation-taken {
  box-shadow: inset 0 0 0 1.5px var(--color-brand-500);
  border-color: transparent;
}

/* En modo inline (calendario de vacaciones), el calendario ocupa todo el ancho del
 * contenedor padre. Reemplazamos el layout flex/space-around default de flatpickr por
 * un grid explícito de 7 columnas para que:
 *   - Cada día ocupe exactamente 1/7 del ancho (sin huecos producidos por max-width 39px).
 *   - La altura de fila sea uniforme y coincida con la columna "Sem" → ambas grillas
 *     alineadas visualmente fila por fila.
 *   - Los nombres de día (Lun, Mar...) se distribuyan parejos arriba.
 * Receta: docs/VACATION-CALENDAR-RECIPE.md. */
.flatpickr-calendar.inline {
  box-shadow: none;
  border: 0;
  width: 100%;
  max-width: 100%;
}
.flatpickr-calendar.inline .flatpickr-innerContainer {
  display: flex;
  width: 100%;
  align-items: stretch;
}
.flatpickr-calendar.inline .flatpickr-rContainer {
  flex: 1 1 auto;
  min-width: 0;
  display: block;
}
.flatpickr-calendar.inline .flatpickr-days {
  width: 100%;
}
.flatpickr-calendar.inline .dayContainer {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  grid-auto-rows: 40px;
  width: 100%;
  min-width: 0;
  max-width: none;
  box-shadow: none;
}
/* Cada día se renderiza como cuadro 40×40 centrado dentro de su columna 1fr del grid.
 * Mantener tamaño fijo es lo que permite que el border-radius (sea pill para selected
 * o rounded-rect para estados) produzca formas correctas en lugar de óvalos estirados. */
.flatpickr-calendar.inline .flatpickr-day {
  width: 40px;
  max-width: 40px;
  flex-basis: auto;
  height: 40px;
  line-height: 38px;
  margin: 0;
  place-self: center;
}
.flatpickr-calendar.inline .flatpickr-day.gx-day-rest,
.flatpickr-calendar.inline .flatpickr-day.gx-day-holiday,
.flatpickr-calendar.inline .flatpickr-day.gx-day-holiday-off,
.flatpickr-calendar.inline .flatpickr-day.gx-day-pending,
.flatpickr-calendar.inline .flatpickr-day.gx-day-approved,
.flatpickr-calendar.inline .flatpickr-day.gx-day-rejected,
.flatpickr-calendar.inline .flatpickr-day.gx-day-min-notice,
.flatpickr-calendar.inline .flatpickr-day.gx-day-blocked-admin,
.flatpickr-calendar.inline .flatpickr-day.gx-day-incapacity {
  width: 36px;
  height: 36px;
  line-height: 34px;
}
/* Encabezado de días (Lun-Dom) con la misma grilla 7-col para que alineen con cada columna del día */
.flatpickr-calendar.inline .flatpickr-weekdays {
  width: 100%;
}
.flatpickr-calendar.inline .flatpickr-weekdaycontainer {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  width: 100%;
  flex: 1 1 auto;
}
.flatpickr-calendar.inline .flatpickr-weekday {
  flex: none;
  width: 100%;
}
/* Columna "Sem": misma altura de fila (40px) y padding compacto para que los números
 * de semana queden centrados verticalmente con la fila de días correspondiente. */
.flatpickr-calendar.inline .flatpickr-weekwrapper {
  flex: 0 0 auto;
  float: none;
}
.flatpickr-calendar.inline .flatpickr-weekwrapper .flatpickr-weeks {
  padding: 0 10px;
  box-shadow: 1px 0 0 rgb(229 231 235);
}
.dark .flatpickr-calendar.inline .flatpickr-weekwrapper .flatpickr-weeks {
  box-shadow: 1px 0 0 rgb(55 65 81);
}
.flatpickr-calendar.inline .flatpickr-weekwrapper span.flatpickr-day {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 40px;
  line-height: 1;
  max-width: none;
  margin: 0;
}

/* Dark mode: oscurecer los días bloqueados sin cambiar la semántica */
.dark .flatpickr-day.gx-day-rest {
  background: repeating-linear-gradient(
    135deg,
    rgba(156, 163, 175, 0.16) 0 4px,
    rgba(156, 163, 175, 0.06) 4px 8px
  );
  color: rgb(209 213 219);
}
.dark .flatpickr-day.gx-day-holiday {
  background: rgba(252, 211, 77, 0.18);
  color: rgb(252 211 77);
}
.dark .flatpickr-day.gx-day-holiday-off {
  background: repeating-linear-gradient(
    135deg,
    rgba(252, 211, 77, 0.26) 0 4px,
    rgba(252, 211, 77, 0.08) 4px 8px
  );
  color: rgb(252 211 77);
}
.dark .flatpickr-day.gx-day-pending {
  background: rgba(252, 211, 77, 0.24);
  color: rgb(252 211 77);
  border: 1px dashed rgb(252 211 77);
}
.dark .flatpickr-day.gx-day-approved {
  background: rgba(74, 222, 128, 0.24);
  color: rgb(134 239 172);
}
.dark .flatpickr-day.gx-day-rejected {
  background: rgba(248, 113, 113, 0.26);
  color: rgb(252 165 165);
}
.dark .flatpickr-day.gx-day-peer-blocked {
  background: rgba(96, 165, 250, 0.22);
  color: rgb(147 197 253);
}
.dark .flatpickr-day.gx-day-blocked-admin {
  background: rgba(192, 132, 252, 0.22);
  color: rgb(216 180 254);
}
.dark .flatpickr-day.gx-day-incapacity {
  background: rgba(251, 113, 133, 0.24);
  color: rgb(253 164 175);
}
.dark .flatpickr-day.gx-day-period-locked {
  background: rgba(148, 163, 184, 0.2);
  color: rgb(203 213 225);
}

/* Días fuera del rango permitido (minDate/maxDate) en el datepicker POPUP.
 * El bundle los deja en opacity ~0.1 (casi invisibles); para que el usuario note
 * que esas fechas no están permitidas (ej. la ventana de retroceso de la
 * Singularidad Tiendas & Distritales) las pintamos en un gris legible con fondo.
 * Acotado a `.flatpickr-calendar:not(.inline)` para NO tocar el calendario inline
 * de vacaciones, que también usa `.flatpickr-disabled` con su propio color-coding. */
.flatpickr-calendar:not(.inline) .flatpickr-day.flatpickr-disabled,
.flatpickr-calendar:not(.inline) .flatpickr-day.flatpickr-disabled:hover {
  color: #9ca3af;            /* gray-400 */
  background: #f3f4f6;       /* gray-100 */
  cursor: not-allowed;
}
.dark .flatpickr-calendar:not(.inline) .flatpickr-day.flatpickr-disabled,
.dark .flatpickr-calendar:not(.inline) .flatpickr-day.flatpickr-disabled:hover {
  color: #4b5563;            /* gray-600 */
  background: rgba(255, 255, 255, 0.04);
}

/* whitespace-pre-line — el bundle no la trae. Preserva `\n` como salto visual
 * y colapsa runs de espacios. Necesaria para renderizar replies/descripciones
 * de tickets con saltos de línea capturados desde un <textarea>. */
.whitespace-pre-line {
  white-space: pre-line;
}

/* max-h-* — el bundle solo trae `max-h-full`. Estas escalas son necesarias para
 * cap-ear dropdowns/menus de pickers async (combobox de soporte, etc.) y dejar
 * que el scroll interno funcione sin desbordar el modal contenedor. */
.max-h-40 {
  max-height: 10rem;
}
.max-h-48 {
  max-height: 12rem;
}
.max-h-56 {
  max-height: 14rem;
}
.max-h-60 {
  max-height: 15rem;
}
.max-h-64 {
  max-height: 16rem;
}
.max-h-72 {
  max-height: 18rem;
}
.max-h-80 {
  max-height: 20rem;
}
.max-h-96 {
  max-height: 24rem;
}

/* pr-7 / pr-9 — el bundle TailAdmin sólo trae 2..5, 8, 10..14. Estos dos faltan
 * y se usan en selects con chevron absoluto (picker de reasignar / agregar
 * colaboradores) para que el ícono no se solape con el texto. */
.pr-7 {
  padding-right: 1.75rem;
}
.pr-9 {
  padding-right: 2.25rem;
}

/* bg-ticket-thread — tono crema cálido específico para el fondo del hilo de
 * conversación del detalle de ticket. Imita el "papel" de un chat estilo
 * mensajería y diferencia visualmente la sección del resto de la UI. No vive
 * en el bundle de TailAdmin porque es un color de uso muy puntual. */
.bg-ticket-thread {
  background-color: #f7f3e8;
}
.dark .bg-ticket-thread {
  background-color: #2a2620;
}

/* Chat — mismo tratamiento visual que el thread de tickets (mismo bundle,
 * misma paleta) pero con nombres genéricos para que los módulos próximos
 * puedan reusar sin alias. */
.bg-chat-thread {
  background-color: #f7f3e8;
}
.dark .bg-chat-thread {
  background-color: #2a2620;
}

.chat-thread-scroll {
  overflow-y: auto;
}
.chat-thread-scroll::-webkit-scrollbar {
  width: 6px;
}
.chat-thread-scroll::-webkit-scrollbar-thumb {
  background: rgb(209 213 219);
  border-radius: 3px;
}
.dark .chat-thread-scroll::-webkit-scrollbar-thumb {
  background: rgb(55 65 81);
}

/* gx-ticket-event-pill — pastilla de eventos audit (creó, reasignó, status…) en
 * el hilo de conversación del detalle del ticket. Va en tonos crema que
 * armonizan con `.bg-ticket-thread`, y a 30% de opacidad por default para que
 * no compita con las burbujas de mensajes. Al hacer hover sube a 100% por
 * legibilidad cuando el usuario quiere leerlo. */
.gx-ticket-event-pill {
  background-color: #e8e0cc;
  color: #6b6149;
  opacity: 0.3;
  transition: opacity 0.15s ease-in-out;
}
.gx-ticket-event-pill:hover {
  opacity: 1;
}
.dark .gx-ticket-event-pill {
  background-color: #3a342a;
  color: #b8ad94;
}

/* ticket-replies-scroll — scroll interno del hilo de mensajes del detalle de
 * ticket. El alto lo define el contenedor padre (layout 2-col con
 * h-[calc(100vh-186px)]); este selector solo activa el scroll y el styling del
 * scrollbar (espejo de .chat-thread-scroll). */
.ticket-replies-scroll {
  overflow-y: auto;
}
.ticket-replies-scroll::-webkit-scrollbar {
  width: 6px;
}
.ticket-replies-scroll::-webkit-scrollbar-thumb {
  background: rgb(209 213 219);
  border-radius: 3px;
}
.dark .ticket-replies-scroll::-webkit-scrollbar-thumb {
  background: rgb(55 65 81);
}

/* ---------------------------------------------------------------------------
 * Support · Tickets — Reacciones estilo WhatsApp/Slack
 * Controla la afordancia hover del botón "…" sobre cada burbuja, los chips
 * agrupados debajo, y la integración del web-component <emoji-picker>.
 * ------------------------------------------------------------------------- */

/* Botón de reacción 🙂 + quick-strip: aparece SOLO al hover del bubble (o
 * cuando el quick-strip está abierto/focus). Mantengo pointer-events:none en
 * el reposo para que no interfiera con clicks accidentales en el bubble. */
.gx-bubble-actions {
  opacity: 0;
  transition: opacity 0.12s ease-in-out;
  pointer-events: none;
}
.group\/bubble:hover .gx-bubble-actions,
.gx-bubble-actions:focus-within,
.gx-bubble-actions:hover {
  opacity: 1;
  pointer-events: auto;
}

/* Chip de reacción: sin fondo ni borde (estilo WhatsApp). Solo emoji +
 * contador (cuando count>=2). Hover con scale para feedback táctil. */
.gx-reaction-chip {
  line-height: 1.1;
  background: transparent;
  border: 0;
  padding: 0;
  transition: transform 0.08s ease-in-out;
}
.gx-reaction-chip:hover {
  transform: scale(1.12);
}
.gx-reaction-chip:active {
  transform: scale(1);
}

/* Fila de reacciones: ligeramente superpuesta al borde inferior del bubble
 * (overlap ~10px). z-index la deja sobre la sombra del bubble. */
.gx-reactions-row {
  margin-top: -10px;
  position: relative;
  z-index: 1;
}

/* <emoji-picker>: el web-component expone CSS vars para custom theming.
 * Lo afinamos al look TailAdmin (radio, sombra, fuente). El modo oscuro lo
 * habilita la clase `.dark` propagada desde el binding `:class` en la vista. */
.gx-emoji-picker {
  --border-radius: 12px;
  --background: #ffffff;
  --border-color: rgb(229 231 235);
  --indicator-color: rgb(70 95 255); /* brand-500 aprox */
  --input-border-color: rgb(229 231 235);
  --input-padding: 0.5rem 0.75rem;
  --num-columns: 8;
  width: 320px;
  height: 360px;
  box-shadow:
    0 10px 25px -5px rgba(0, 0, 0, 0.1),
    0 8px 10px -6px rgba(0, 0, 0, 0.08);
  border: 1px solid rgb(229 231 235);
  border-radius: 12px;
}
.gx-emoji-picker.dark {
  --background: rgb(17 24 39);
  --border-color: rgb(55 65 81);
  --input-border-color: rgb(55 65 81);
  --input-font-color: rgb(229 231 235);
  --category-emoji-padding: 0.25rem;
  --button-active-background: rgb(31 41 55);
  --button-hover-background: rgb(31 41 55);
  color-scheme: dark;
  border-color: rgb(55 65 81);
}

/* ---------------------------------------------------------------------------
 * Support · Tickets — Quote / Responder (estilo WhatsApp)
 * ------------------------------------------------------------------------- */

/* Bloque de cita: borde lateral brand-500 y fondo sutil. Truncate a 2 líneas. */
.gx-quote-block {
  border-left: 3px solid rgb(70 95 255); /* brand-500 */
  transition: background-color 0.12s ease-in-out;
}

/* mt-auto — empuja un hijo flex al fondo; no está compilado en el bundle. */
.mt-auto {
  margin-top: auto;
}

/* object-contain — ajusta la imagen sin recortar (lightbox); no está en el bundle. */
.object-contain {
  object-fit: contain;
}

/* line-clamp-1/2 (truncate multilinea) — no está compilado en el bundle. */
.line-clamp-1 {
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.line-clamp-2 {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Flash visual al hacer scroll a una reply citada (click sobre el quote). */
@keyframes gx-quote-flash {
  0% {
    box-shadow: 0 0 0 0 rgba(70, 95, 255, 0);
  }
  20% {
    box-shadow: 0 0 0 4px rgba(70, 95, 255, 0.35);
  }
  100% {
    box-shadow: 0 0 0 0 rgba(70, 95, 255, 0);
  }
}
.gx-quote-flash {
  animation: gx-quote-flash 1.2s ease-out;
}

/* Compose textarea (estilo WhatsApp): altura mínima de 1 línea, crece hasta
 * el cap del autoGrowCompose() en JS. Sin esto el rows=1 inicial rinde 1.5x
 * en algunos browsers por line-height. */
.gx-compose-textarea {
  min-height: 40px;
  max-height: 160px;
  line-height: 1.4;
}

/* Utilities transversales ausentes en el bundle compilado. Agregadas aquí
 * (en vez de reemplazarlas en HTML) porque tienen sentido semántico. */
.w-52 {
  width: 13rem;
} /* 208px — dropdowns medianos (ej. menú [+] del compose) */
.w-80 {
  width: 20rem;
} /* 320px — dropdown de plantillas (canned responses) */
/* w-3.5: el bundle trae h-3.5 pero NO w-3.5; SVGs con `h-3.5 w-3.5`
 * quedaban con ancho 0 en silencio. Pareo con la formula de h-3.5. */
.w-3\.5 {
  width: calc(var(--spacing) * 3.5);
}
.gap-y-1 {
  row-gap: 0.25rem;
}
.leading-none {
  line-height: 1;
}

/* Shades 600 (hover de send button) — el bundle solo trae 500. Usamos las
 * vars brand-* del :root + un warning-600 calculado a mano. */
.bg-brand-600 {
  background-color: var(--color-brand-600);
}
.bg-warning-600 {
  background-color: #b45309;
} /* warning más oscuro que el 500 */

/* hover:* equivalentes para las shades 600 (Tailwind no genera estas reglas
 * porque la utility base no existe en el bundle). */
.hover\:bg-brand-600:hover {
  background-color: var(--color-brand-600);
}
.hover\:bg-warning-600:hover {
  background-color: #b45309;
}

/* text-brand-700/800 — texto verde oscuro para el item activo del sidebar
 * (píldora bg-brand-200). El bundle solo trae text-brand-500. */
.text-brand-700 {
  color: var(--color-brand-700);
}
.text-brand-800 {
  color: var(--color-brand-800);
}

/* gx-sub-indent — sangría de una hoja que cuelga de un subgrupo (tercer nivel
 * del sidebar). El bundle no pasa de pl-12: con 48px contra los 36px del nivel
 * de arriba la diferencia se lee como ruido y los dos niveles parecen el mismo.
 * 60px repone los 24px de separación que ya usaba el diseño anterior.
 * En unidades de --spacing para que el modo .compact lo re-escale. */
.gx-sub-indent {
  padding-left: calc(var(--spacing) * 15);
}

/* Sidebar mini (xl + data-collapsed): la fila de módulo queda como un riel de
 * iconos centrados, y al hacer hover vuelve a alinearse a la izquierda para que
 * se lean los nombres (`.sidebar:hover .menu-item-text` del bundle).
 *
 * Va acá y no como utility porque `xl:justify-center` NO está compilada en el
 * bundle (solo xl:justify-between|end|normal) y el fallo sería silencioso.
 * `padding-inline` en unidades de --spacing para que el modo .compact lo
 * re-escale igual que px-3. Mismo patrón que style.css § aside[data-collapsed]. */
@media (min-width: 1280px) {
  aside[data-collapsed="true"] .gx-mod-row {
    justify-content: center;
    padding-inline: 0;
  }
  aside[data-collapsed="true"]:hover .gx-mod-row {
    justify-content: flex-start;
    padding-inline: calc(var(--spacing) * 3);
  }
}

/* tracking-wide — el badge "Interna" en el compose lo usa. */
.tracking-wide {
  letter-spacing: 0.025em;
}

/* Paleta meta-3 ausente del bundle. Solo agregamos los tones que efectivamente
 * usamos (verde tipo "foto/imagen" en el menú [+]). */
.bg-meta-3\/10 {
  background-color: rgb(16 185 129 / 0.1);
} /* emerald-500 @ 10% */
.text-meta-3 {
  color: rgb(16 185 129);
}

/* bg-warning-50/10 — fondo sutil del textarea cuando hay nota interna. */
.bg-warning-50\/10 {
  background-color: rgb(252 233 215 / 0.1);
}

/* h-[598px] — usado por el modal de administrar favoritos (alto fijo).
 * El bundle trae max-h-[598px] pero no h-[598px]; sin esto el modal
 * colapsa al tamaño del contenido cuando hay pocos items. */
.h-\[598px\] {
  height: 598px;
}

/* h-[490px] — usado por los modales "Nuevo grupo" y "Reenviar mensaje".
 * Mismo motivo que h-[598px]: el bundle no compila esta altura específica
 * y sin ella los modales colapsan al contenido. */
.h-\[490px\] {
  height: 490px;
}

/* max-h-[90vh] — cap de altura de modales scrollables (header/footer shrink-0 +
 * body flex-1 overflow-y-auto). El bundle no la compila; sin ella el body no tiene
 * padre acotado y el overflow-y-auto no se activa. La usan scheduling/detail y los
 * modales de incapacidades. */
.max-h-\[90vh\] {
  max-height: 90vh;
}

/* Utilities de posición/spacing pequeñas que el bundle NO trae compiladas
 * (solo trae top-0/3/4/5/6/8/10/16 y right-0/2/3/4/5/6/10). Sin estas, los
 * `absolute top-1 right-1` fallan en silencio y el elemento cae al origen
 * del padre (default top:0; right:auto;) → aparece a la izquierda. */
.top-1 {
  top: 0.25rem;
}
.top-2 {
  top: 0.5rem;
}
.right-1 {
  right: 0.25rem;
}

/* gx-bubble-menu-trigger — chevron del menú anclado en el top-right interno
 * del bubble. Visibilidad por hover del grupo (.group/bubble) o focus-within,
 * mismo patrón que .gx-bubble-actions. Sin esto el chevron se ve permanente
 * y rompe la vibra "se asoma al pasar por encima". */
.gx-bubble-menu-trigger {
  opacity: 0;
  transition: opacity 0.12s ease-in-out;
  pointer-events: none;
}
.group\/bubble:hover .gx-bubble-menu-trigger,
.gx-bubble-menu-trigger:focus-within,
.gx-bubble-menu-trigger:focus {
  opacity: 1;
  pointer-events: auto;
}

/* text-receipt-read — azul WhatsApp clásico para las palomitas de "leído"
 * (doble check). El bundle no trae paleta sky/cyan/blue; brand-500 (#3C50E0)
 * es demasiado violáceo. WhatsApp usa #53BDEB en sus apps oficiales. */
.text-receipt-read {
  color: #53bdeb;
}

/* Búsqueda en el thread:
 * - gx-search-mark: highlight inline de matches dentro del body del mensaje.
 *   El bundle no incluye estilos para <mark>; sin esto sale negro sobre amarillo
 *   navegador-default y rompe el contraste en dark mode.
 * - gx-search-flash: flash temporal cuando se navega a un match (jumpToSearchMatch).
 *   Hereda el patrón de scrollToReply existente, pero con tono ámbar para reforzar
 *   que viene del buscador, no de un quote. */
.gx-search-mark {
  background-color: #fef08a; /* amber-200 */
  color: #1f2937; /* gray-800 */
  border-radius: 2px;
  padding: 0 1px;
}
.dark .gx-search-mark {
  background-color: #facc15; /* amber-400 — más punchy en dark */
  color: #111827; /* gray-900 */
}
.gx-search-flash {
  animation: gx-search-flash-kf 1.4s ease-out;
  border-radius: 12px;
}
@keyframes gx-search-flash-kf {
  0% {
    background-color: rgba(250, 204, 21, 0.45);
  } /* amber-400 @ 45% */
  100% {
    background-color: rgba(250, 204, 21, 0);
  }
}

/* Grillas pivote de asistencia (scheduling/index + attendance_report/index) —
 * ancho mínimo de las columnas de día. El bundle solo compila min-w-[260px]
 * (col Empleado) pero NO los anchos chicos de las columnas de fecha; sin esto
 * colapsan al contenido y se desalinean. Programación usa 90px (1 línea);
 * el Reporte usa 104px (2 líneas: horario + slot de cumplimiento). */
.min-w-\[38px\] {
  min-width: 38px;
}
.min-w-\[90px\] {
  min-width: 90px;
}
.min-w-\[104px\] {
  min-width: 104px;
}
/* Ancho mínimo de la tabla de días bloqueados (vacaciones) para que las columnas
 * de la grilla data-table-03 no colapsen al contenido en pantallas chicas. */
.min-w-\[900px\] {
  min-width: 900px;
}
/* KPIs Distritales — la matriz de tiendas (whitespace-nowrap) aplasta las columnas
 * de identidad: ancho mínimo para "Horas en zonas" (la col "Distrital" usa el
 * min-w-[260px] que el bundle sí compila). */
.min-w-\[130px\] {
  min-width: 130px;
}

/* vertical-align: el bundle no trae ninguna utility .align-*. La grilla del
 * reporte necesita top para que las celdas de 1 y 2 líneas queden parejas
 * arriba. */
.align-top {
  vertical-align: top;
}
.align-middle {
  vertical-align: middle;
}

/* text-2xs — escalón por debajo de text-xs (0.75rem/12px). El bundle no baja
 * de text-xs; este es el 10px del sistema para metadatos ultra-compactos
 * (claves de empleado, chips densos, sub-labels). line-height proporcional a
 * la fórmula de TailAdmin (--text-xs--line-height = calc(1/0.75)) para que el
 * interlineado acompañe el tamaño: 0.625rem * (0.875/0.625) = 0.875rem/14px. */
.text-2xs {
  font-size: 0.625rem; /* 10px */
  line-height: 0.875rem; /* 14px */
}
/* Variantes responsive de text-2xs. El bundle no las genera (no hay build de
 * Tailwind), así que las declaramos a mano con los breakpoints estándar y la
 * misma sintaxis `@media (width >= …)` que usa el bundle compilado. */
@media (width >= 640px) {
  .sm\:text-2xs {
    font-size: 0.625rem;
    line-height: 0.875rem;
  }
}
@media (width >= 768px) {
  .md\:text-2xs {
    font-size: 0.625rem;
    line-height: 0.875rem;
  }
}
@media (width >= 1024px) {
  .lg\:text-2xs {
    font-size: 0.625rem;
    line-height: 0.875rem;
  }
}
@media (width >= 1280px) {
  .xl\:text-2xs {
    font-size: 0.625rem;
    line-height: 0.875rem;
  }
}
@media (width >= 1536px) {
  .\32xl\:text-2xs {
    font-size: 0.625rem;
    line-height: 0.875rem;
  }
}
/* Variantes de estado hover/focus (misma dinámica que el resto de utilities
 * de estado del archivo, ej. .hover\:bg-brand-600). El bundle envuelve sus
 * hover en `@media (hover:hover)`; acá mantenemos la forma plana ya usada en
 * custom.css por simplicidad y consistencia interna. */
.hover\:text-2xs:hover {
  font-size: 0.625rem;
  line-height: 0.875rem;
}
.focus\:text-2xs:focus {
  font-size: 0.625rem;
  line-height: 0.875rem;
}

/* ── Programación de horarios: indicadores de festivo y vacaciones ──────────
 * Espejo de los colores del calendario de solicitud de vacaciones, pero como
 * utilities del bundle estático (las variantes blue-light dark/* no están
 * compiladas). Festivo = solo indicador (franja ámbar) sobre el día, que
 * conserva su horario. Vacaciones = celda azul, sin horario (como descanso).
 * Azul rgb(59 130 246) = mismo tono que .gx-day-peer-blocked del calendario. */
.gx-sched-holiday-accent {
  box-shadow: inset 4px 0 0 0 rgb(245 158 11);
}
.dark .gx-sched-holiday-accent {
  box-shadow: inset 4px 0 0 0 rgb(251 191 36);
}
.gx-sched-holiday-cell {
  background: rgba(245, 158, 11, 0.16);
}
.dark .gx-sched-holiday-cell {
  background: rgba(245, 158, 11, 0.24);
}
.gx-sched-holiday-text {
  color: rgb(180 83 9);
}
.dark .gx-sched-holiday-text {
  color: rgb(251 191 36);
}
.gx-sched-vacation {
  background: rgba(59, 130, 246, 0.12);
}
.dark .gx-sched-vacation {
  background: rgba(59, 130, 246, 0.22);
}
.gx-sched-vacation-text {
  color: rgb(37 99 235);
}
.dark .gx-sched-vacation-text {
  color: rgb(147 197 253);
}
/* Incapacidad (rosa) — distinto del azul de vacaciones. */
.gx-sched-incapacity {
  background: rgba(244, 63, 94, 0.12);
}
.dark .gx-sched-incapacity {
  background: rgba(244, 63, 94, 0.22);
}
.gx-sched-incapacity-text {
  color: rgb(190 24 93);
}
.dark .gx-sched-incapacity-text {
  color: rgb(251 207 232);
}

/* Día anterior al ingreso de un alta PENDIENTE: no programable. Rayado diagonal en vez de
 * un color plano, porque cualquier fondo sólido se confundiría con un estado del día
 * (descanso/vacación/incapacidad) y aquí justamente NO hay día que mostrar. */
.gx-sched-prehire {
  background-image: repeating-linear-gradient(
    45deg,
    transparent,
    transparent 4px,
    rgba(148, 163, 184, 0.18) 4px,
    rgba(148, 163, 184, 0.18) 8px
  );
}
.dark .gx-sched-prehire {
  background-image: repeating-linear-gradient(
    45deg,
    transparent,
    transparent 4px,
    rgba(148, 163, 184, 0.14) 4px,
    rgba(148, 163, 184, 0.14) 8px
  );
}

/* ============================================================
 * Modo compacto / densidad — toggle por usuario (clase .compact en <body>)
 * --------------------------------------------------------------
 * TailAdmin (Tailwind v4) compila TODO el espaciado contra una sola variable
 * (--spacing) y las tipografías contra --text-*. Sobrescribirlas dentro de
 * .compact re-escala toda la UI de golpe — sin recompilar ni tocar style.css.
 * El toggle vive en el header (espejo de darkMode) y persiste en localStorage.
 * Como las custom properties heredan, llega a todo lo que cuelga de <body>,
 * incluidos los modales con x-teleport="body".
 *
 * Rollback total: borrar este bloque. Por usuario: apagar el toggle.
 * Intensidad: MEDIA. Calibrar subiendo/bajando los valores de abajo.
 * ============================================================ */
.compact {
  /* Perilla principal: re-escala TODO el espaciado (p/px/py/gap/h/w/space-y/m/inset…) */
  --spacing: 0.19rem; /* 0.25rem → ~24% más compacto */

  /* Texto −1 escalón (rem) + interlineado más junto */
  --text-xs: 0.6875rem; /* 11px (era 12px) */
  --text-sm: 0.8125rem; /* 13px (era 14px) */
  --text-base: 0.875rem; /* 14px (era 16px) */
  --text-xs--line-height: 0.95rem;
  --text-sm--line-height: 1.1rem;
  --text-base--line-height: 1.25rem;

  /* Texto de TABLAS (theme vars en px) −1 escalón */
  --text-theme-xs: 11px;
  --text-theme-xs--line-height: 15px; /* era 12/18 */
  --text-theme-sm: 13px;
  --text-theme-sm--line-height: 17px; /* era 14/20 */
}

/*
 * <select> nativo en modo compacto.
 * Al bajar --spacing, un h-9 pasa de 2.25rem a ~1.71rem. A un <div> no le afecta,
 * pero Chrome RECORTA verticalmente el texto del valor mostrado de un <select>
 * nativo cuando la caja queda más baja de lo que necesita (se veía "25" cortado y
 * "Pendientes por aprobar" recortado). Es el problema inverso al gxa-combobox de abajo:
 * el nativo sí re-escala con --spacing, pero se encoge de más. Un piso de altura
 * evita el recorte; min-height solo sube los que están por debajo (los h-11 →
 * ~2.09rem no se tocan) y no colisiona con el chevron (padding-right propio).
 *
 * OJO: aplica a CUALQUIER `select` (no solo `.gx-select`) — el selector "Mostrar N
 * registros" usa el patrón wrapper+SVG (appearance-none, sin `.gx-select`) y también
 * se recortaba. `.compact select` cubre ambos.
 */
.compact select {
  min-height: 2rem;
  line-height: 1.25rem;
}

/*
 * gxa-combobox en modo compacto.
 * El combobox rico (familia gxa-*, incluido el selector estándar de zonas) se
 * estiliza con valores rem/px FIJOS arriba (no usa --spacing/--text-*), así que
 * NO se re-escala solo con .compact y quedaba más alto/holgado que los inputs
 * vecinos (un <select> h-11 baja a ~2.09rem; el trigger seguía en 2.75rem).
 * Estos overrides bajan trigger, menú, buscador y opciones a la misma densidad.
 * Los chips internos (px/py + text-theme-xs) ya escalan solos vía variables.
 */
.compact .gxa-combobox__trigger {
  min-height: 2.25rem;
  padding: 0.375rem 0.625rem;
  gap: 0.375rem;
  font-size: 0.8125rem;
}
/* Variante --compact dentro del modo .compact: baja el trigger a la altura de
 * los inputs py-1.5 ya re-escalados (~1.8rem). Especificidad (0,3,0) gana sobre
 * la regla general .compact .gxa-combobox__trigger. */
.compact .gxa-combobox--compact .gxa-combobox__trigger {
  min-height: 1.8rem;
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
}
.compact .gxa-combobox__trigger-content {
  gap: 0.375rem;
}
.compact .gxa-combobox__clear {
  width: 1.125rem;
  height: 1.125rem;
}

.compact .gxa-combobox__search {
  padding: 0.375rem;
}
.compact .gxa-combobox__search input {
  padding: 0.375rem 0.5rem;
  font-size: 0.8125rem;
}

.compact .gxa-combobox__status {
  padding: 0.375rem 0.375rem 0;
}
.compact .gxa-combobox__status button {
  padding: 0.1875rem 0.375rem;
  font-size: 0.625rem;
}

.compact .gxa-combobox__kinds {
  padding: 0.375rem;
  gap: 0.25rem;
}
.compact .gxa-combobox__kind {
  padding: 0.0625rem 0.5rem;
  font-size: 0.6875rem;
  line-height: 1.1rem;
}

.compact .gxa-combobox__list {
  padding: 0.1875rem;
}
.compact .gxa-combobox__group {
  padding: 0.25rem 0.5rem 0.1875rem;
}
.compact .gxa-combobox__option {
  padding: 0.3125rem 0.5rem;
  gap: 0.375rem;
}
.compact .gxa-combobox__option-label {
  font-size: 0.8125rem;
}
.compact .gxa-combobox__option-secondary {
  font-size: 0.6875rem;
}
.compact .gxa-combobox__check {
  width: 14px;
  height: 14px;
}
.compact .gxa-combobox__empty {
  padding: 0.625rem;
  font-size: 0.8125rem;
}

/*
 * .font-mono — utilidad monoespaciada ausente del bundle compilado.
 * Usada por el tag de debug de permisos (partials/debug-permission-tag) para
 * los strings de permiso. Stack mono estándar.
 */
.font-mono {
  font-family:
    ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
    "Courier New", monospace;
}

/*
 * .gx-doc — tipografía para Markdown renderizado (modal de documentación por
 * sección, partials/section-help.php). El bundle no trae `prose`; estos estilos
 * dan jerarquía legible al HTML que produce CommonMark. Paleta alineada a
 * TailAdmin (gray-* / brand-*) con variante dark.
 */
.gx-doc {
  font-size: 0.9375rem;
  line-height: 1.65;
  color: #344054; /* gray-700 */
}
.dark .gx-doc {
  color: #d0d5dd;
} /* gray-300 */

.gx-doc > :first-child {
  margin-top: 0;
}
.gx-doc > :last-child {
  margin-bottom: 0;
}

.gx-doc h1 {
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1.25;
  margin: 1.5rem 0 0.75rem;
  color: #1d2939;
}
.gx-doc h2 {
  font-size: 1.25rem;
  font-weight: 600;
  line-height: 1.3;
  margin: 1.5rem 0 0.5rem;
  color: #1d2939;
  padding-bottom: 0.3rem;
  border-bottom: 1px solid #eaecf0;
}
.gx-doc h3 {
  font-size: 1.05rem;
  font-weight: 600;
  line-height: 1.35;
  margin: 1.25rem 0 0.4rem;
  color: #1d2939;
}
.gx-doc h4 {
  font-size: 0.95rem;
  font-weight: 600;
  margin: 1rem 0 0.3rem;
  color: #1d2939;
}
.dark .gx-doc h1,
.dark .gx-doc h2,
.dark .gx-doc h3,
.dark .gx-doc h4 {
  color: #f2f4f7;
}
.dark .gx-doc h2 {
  border-bottom-color: #1d2939;
}

.gx-doc p {
  margin: 0.6rem 0;
}
.gx-doc ul,
.gx-doc ol {
  margin: 0.6rem 0;
  padding-left: 1.4rem;
}
.gx-doc ul {
  list-style: disc;
}
.gx-doc ol {
  list-style: decimal;
}
.gx-doc li {
  margin: 0.25rem 0;
}
.gx-doc li > ul,
.gx-doc li > ol {
  margin: 0.25rem 0;
}

.gx-doc a {
  color: var(--color-brand-500);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.gx-doc a:hover {
  color: var(--color-brand-600);
}
.dark .gx-doc a {
  color: var(--color-brand-400);
}

.gx-doc strong {
  font-weight: 600;
  color: #1d2939;
}
.dark .gx-doc strong {
  color: #f2f4f7;
}
.gx-doc em {
  font-style: italic;
}

.gx-doc code {
  font-family:
    ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
    "Courier New", monospace;
  font-size: 0.85em;
  background: #f2f4f7;
  color: #344054;
  padding: 0.15em 0.4em;
  border-radius: 0.3rem;
}
.dark .gx-doc code {
  background: #1d2939;
  color: #d0d5dd;
}

.gx-doc pre {
  font-family:
    ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
    "Courier New", monospace;
  font-size: 0.85rem;
  line-height: 1.5;
  background: #1d2939;
  color: #e4e7ec;
  padding: 1rem;
  border-radius: 0.6rem;
  overflow-x: auto;
  margin: 0.9rem 0;
}
.gx-doc pre code {
  background: transparent;
  color: inherit;
  padding: 0;
  font-size: inherit;
}

.gx-doc blockquote {
  border-left: 3px solid var(--color-brand-500);
  background: #f9fafb;
  padding: 0.5rem 1rem;
  margin: 0.9rem 0;
  color: #475467;
  border-radius: 0 0.4rem 0.4rem 0;
}
.dark .gx-doc blockquote {
  background: #ffffff08;
  color: #98a2b3;
}

.gx-doc hr {
  border: 0;
  border-top: 1px solid #eaecf0;
  margin: 1.5rem 0;
}
.dark .gx-doc hr {
  border-top-color: #1d2939;
}

.gx-doc table {
  width: 100%;
  border-collapse: collapse;
  margin: 0.9rem 0;
  font-size: 0.875rem;
}
.gx-doc th,
.gx-doc td {
  border: 1px solid #eaecf0;
  padding: 0.5rem 0.75rem;
  text-align: left;
  vertical-align: top;
}
.gx-doc th {
  background: #f9fafb;
  font-weight: 600;
  color: #1d2939;
}
.dark .gx-doc th,
.dark .gx-doc td {
  border-color: #1d2939;
}
.dark .gx-doc th {
  background: #ffffff08;
  color: #f2f4f7;
}
/* .gx-doc-inline — markdown INLINE (una sola línea, sin párrafos ni bloques):
   las notas del changelog en el modal de documentación. Reusa la tipografía de
   .gx-doc para negritas/código/ligas sin arrastrar márgenes de bloque. */
.gx-doc-inline strong {
  font-weight: 600;
  color: #1d2939;
}
.dark .gx-doc-inline strong {
  color: #f2f4f7;
}
.gx-doc-inline em {
  font-style: italic;
}
.gx-doc-inline code {
  font-family:
    ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
    "Courier New", monospace;
  font-size: 0.85em;
  background: #f2f4f7;
  color: #344054;
  padding: 0.1em 0.35em;
  border-radius: 0.3rem;
}
.dark .gx-doc-inline code {
  background: #1d2939;
  color: #d0d5dd;
}
.gx-doc-inline a {
  color: #465fff;
  text-decoration: underline;
}
.dark .gx-doc-inline a {
  color: #7592ff;
}

/* Preview de documentos generables (contrato/constancia): las celdas vacías
   (espacio de firma, líneas de llenado) se muestran con alto usable, igual que en
   el PDF. Solo aplica a .gx-doc-form (no a la documentación por sección). */
.gx-doc-form td.fill {
  height: 48px;
}
/* Tabla de firmas ([[FIRMA]] → class="sig"): sin bordes, centrada, línea de firma
   en el primer renglón (celdas .sigline). Espeja el PDF (document_pdf.php); las
   clases .fill/.sigline las pone DocumentTemplateService porque mPDF no soporta
   :empty ni tr:first-child. */
.gx-doc-form table.sig {
  border-collapse: separate;
  border-spacing: 24px 0;
}
.gx-doc-form table.sig th,
.gx-doc-form table.sig td {
  border: none;
  text-align: center;
  padding: 2px 10px;
}
.gx-doc-form table.sig td.sigline {
  height: 78px;
  border-bottom: 1px solid #374151;
}
.dark .gx-doc-form table.sig td.sigline {
  border-bottom-color: #98a2b3;
}

.gx-doc img {
  max-width: 100%;
  height: auto;
  border-radius: 0.5rem;
  margin: 0.6rem 0;
}

.gx-doc input[type="checkbox"] {
  margin-right: 0.4rem;
}

/* Utilities faltantes en el bundle, usadas por bloques de salida/código
   (Herramientas de mantenimiento → diagnóstico HEIC). */
.whitespace-pre-wrap {
  white-space: pre-wrap;
}
.break-all {
  word-break: break-all;
}

/* Anchos/flex responsivos faltantes en el bundle (filtros del reporte
   Desglose de Ingresos Global: Periodo + Zona fijos, Conceptos toma el resto;
   y filtros compactos del Reporte Comercial: w-28 para caber todos en una línea). */
@media (min-width: 640px) {
  .sm\:w-28 {
    width: 7rem;
  }
  .sm\:w-32 {
    width: 8rem;
  }
  .sm\:w-40 {
    width: 10rem;
  }
  .sm\:w-44 {
    width: 11rem;
  }
  .sm\:w-56 {
    width: 14rem;
  }
  .sm\:w-60 {
    width: 15rem;
  }
  .sm\:w-72 {
    width: 18rem;
  }
  .sm\:flex-1 {
    flex: 1 1 0%;
  }
}

/* Ancho fijo del botón Buscar para que el cambio de texto
   ("Buscar" ↔ "Buscando…") no redimensione ni desborde los conceptos. */
@media (min-width: 1024px) {
  .lg\:w-32 {
    width: 8rem;
  }
}

/* Utilities faltantes en el bundle, usadas por la tabla del reporte
   Desglose de Ingresos Global (columnas dinámicas, headers multilínea, totales). */
.whitespace-pre {
  white-space: pre;
}
.tabular-nums {
  font-variant-numeric: tabular-nums;
}
.border-t-2 {
  border-top-width: 2px;
}
.last\:border-r-0:last-child {
  border-right-width: 0;
}
/* Línea sutil bajo el thead sticky (el bundle no trae este arbitrary value). */
.shadow-\[0_1px_0_0_rgba\(0\2c 0\2c 0\2c 0\.05\)\] {
  box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.05);
}

/* Tintes de fondo por grupo de columna (reporte Desglose de Ingresos Global):
   base = Costo Prom. + Total c/IVA · total = Total por forma de pago ·
   flow = Ingreso/Egreso. Replica bg_base/bg_total/bg_otros del legacy.
   En dark mode, tinte oscuro tenue equivalente (no el pastel claro). */
.gx-cell-base {
  background-color: rgb(225, 236, 249);
}
.gx-cell-total {
  background-color: rgb(222, 246, 236);
}
.gx-cell-flow {
  background-color: rgb(235, 233, 249);
}
/* Amarillo pastel para la columna "Cant. etiquetas a imprimir"
   (Cambio de Precios / Productos en Exhibición). */
.gx-cell-print {
  background-color: rgb(254, 249, 219);
}
.dark .gx-cell-print {
  background-color: rgba(250, 204, 21, 0.12);
}
.dark .gx-cell-base {
  background-color: rgba(96, 165, 250, 0.12);
}
.dark .gx-cell-total {
  background-color: rgba(52, 211, 153, 0.12);
}
.dark .gx-cell-flow {
  background-color: rgba(167, 139, 250, 0.12);
}

/* Hover de fila: las celdas tinteadas (y la sticky con bg propio) tapan el
   hover:bg-gray-50 del <tr>, así que aquí se oscurece cada una un escalón al
   pasar el cursor por su fila — incluida la columna fija (ID Tienda). */
.gx-desglose-table tbody tr:hover .gx-cell-base {
  background-color: rgb(205, 222, 244);
}
.gx-desglose-table tbody tr:hover .gx-cell-total {
  background-color: rgb(202, 238, 224);
}
.gx-desglose-table tbody tr:hover .gx-cell-flow {
  background-color: rgb(223, 219, 245);
}
.gx-desglose-table tbody tr:hover .gx-cell-print {
  background-color: rgb(250, 243, 199);
}
.dark .gx-desglose-table tbody tr:hover .gx-cell-print {
  background-color: rgba(250, 204, 21, 0.2);
}
.gx-desglose-table tbody tr:hover td.sticky {
  background-color: rgb(249, 250, 251);
}
.dark .gx-desglose-table tbody tr:hover .gx-cell-base {
  background-color: rgba(96, 165, 250, 0.2);
}
.dark .gx-desglose-table tbody tr:hover .gx-cell-total {
  background-color: rgba(52, 211, 153, 0.2);
}
.dark .gx-desglose-table tbody tr:hover .gx-cell-flow {
  background-color: rgba(167, 139, 250, 0.2);
}
.dark .gx-desglose-table tbody tr:hover td.sticky {
  background-color: rgb(24, 31, 42);
}

/* Modal del Ticket (detalle de tienda): un poco más ancho que max-w-3xl y altura
   fija al 90% del viewport desde el montaje, para que no haya saltos visuales
   entre el estado "cargando" y el ticket cargado. El bundle no trae max-w-4xl
   ni h-[90vh]. El scroll vive en el body general (no en tablas internas). */
.gx-ticket-modal {
  width: 100%;
  max-width: 56rem;
  height: 90vh;
}

/* Modal de detalle de producto ERP. Ancho mayor a max-w-3xl porque la tarjeta de
   variante trae un grid de 3 columnas que no cabe; el bundle no tiene max-w-4xl+.
   Alto acotado por ARRIBA y por ABAJO en vez de fijo: con 90vh fijo, un producto de
   una sola variante dejaba el modal 80% vacío; con solo max-height, cambiar de
   pestaña movía el alto (y los botones) en cada clic. El piso lo estabiliza. */
.gx-erp-product-modal {
  width: 100%;
  max-width: 72rem;
  max-height: 90vh;
  min-height: 34rem;
}

/* Modal de designación de deuda de Auditoría Móvil: tabla de empleados con montos,
   más ancho que el max-w-3xl del bundle (mismo caso que los dos de arriba). */
.gx-am-deuda-modal {
  width: 100%;
  max-width: 72rem;
  max-height: 90vh;
  min-height: 30rem;
}

/* Modal de la Evaluación Operativa de Auditoría Móvil: cada renglón es pregunta +
   niveles + observación, y con el max-w-3xl del bundle la pregunta se parte en cuatro
   líneas. Mismo criterio que .gx-am-deuda-modal, un poco más angosto (no hay tabla). */
.gx-am-evaluacion-modal {
  width: 100%;
  max-width: 60rem;
  max-height: 90vh;
  min-height: 28rem;
}

/* Utilities estándar de Tailwind ausentes del bundle compilado. */
.table-fixed {
  table-layout: fixed;
}
.min-w-0 {
  min-width: 0;
}

/* Grid de 5 KPIs (Resumen de tienda): el bundle no trae grid-cols-5. 2 cols en
   móvil → 5 en lg. Mobile-first, sin tocar el bundle. */
.gx-grid-kpi {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1rem;
}
@media (min-width: 1024px) {
  .gx-grid-kpi {
    grid-template-columns: repeat(5, minmax(0, 1fr));
  }
}

/* Badges pastel genéricos por color (reutilizables): texto + fondo tenue,
   con variante dark. El bundle no trae purple-50 ni los bg-*-500/10 dark de
   varias paletas, así que se definen aquí (norma: utilities faltantes →
   custom.css). Pensados para píldoras `rounded-full px-2 py-0.5 text-xs`. */
.gx-badge-gray {
  background-color: rgb(243, 244, 246);
  color: rgb(75, 85, 99);
}
.gx-badge-green {
  background-color: rgb(220, 252, 231);
  color: rgb(21, 128, 61);
}
.gx-badge-red {
  background-color: rgb(254, 226, 226);
  color: rgb(185, 28, 28);
}
.gx-badge-yellow {
  background-color: rgb(254, 243, 199);
  color: rgb(161, 98, 7);
}
.gx-badge-purple {
  background-color: rgb(237, 233, 254);
  color: rgb(109, 40, 217);
}
.gx-badge-blue {
  background-color: rgb(224, 242, 254);
  color: rgb(2, 132, 199);
}
.gx-badge-orange {
  background-color: rgb(255, 237, 213);
  color: rgb(194, 65, 12);
}
.dark .gx-badge-gray {
  background-color: rgba(156, 163, 175, 0.15);
  color: rgb(209, 213, 219);
}
.dark .gx-badge-green {
  background-color: rgba(34, 197, 94, 0.15);
  color: rgb(74, 222, 128);
}
.dark .gx-badge-red {
  background-color: rgba(239, 68, 68, 0.15);
  color: rgb(248, 113, 113);
}
.dark .gx-badge-yellow {
  background-color: rgba(234, 179, 8, 0.15);
  color: rgb(250, 204, 21);
}
.dark .gx-badge-purple {
  background-color: rgba(124, 90, 248, 0.18);
  color: rgb(196, 181, 253);
}
.dark .gx-badge-blue {
  background-color: rgba(11, 165, 236, 0.15);
  color: rgb(125, 211, 252);
}
.dark .gx-badge-orange {
  background-color: rgba(249, 115, 22, 0.15);
  color: rgb(253, 186, 116);
}

/* ---------------------------------------------------------------------------
 * Portal de clientes (/customer/*) — utilities ausentes del bundle.
 * `h-dvh` no está precompilado (solo min-h-dvh); el portal lo usa como alto
 * del documento para que el scroll viva adentro de la tarjeta (correcto en
 * iframe y en mobile con barra de URL dinámica). Fallback 100vh para
 * navegadores sin dvh.
 * ------------------------------------------------------------------------- */
.h-dvh {
  height: 100vh;
  height: 100dvh;
}

/* Utilities del portal de clientes ausentes del bundle (verificadas con
 * grep -F antes de agregar — norma del proyecto). */
.self-end {
  align-self: flex-end;
}
.hover\:bg-white\/10:hover {
  background-color: rgb(255 255 255 / 0.1);
}
.max-w-\[85\%\] {
  max-width: 85%;
}
@media (min-width: 640px) {
  .sm\:max-w-\[75\%\] {
    max-width: 75%;
  }
}
@media (min-width: 1024px) {
  .lg\:border-r {
    border-right-style: var(--tw-border-style, solid);
    border-right-width: 1px;
  }
}

/* ---------------------------------------------------------------------------
 * AG Grid — headers compactos + tooltip "?" tipo fórmula (gx.agGrid).
 * Norma del proyecto: reportes con AG Grid usan gx.agGrid.gridOptions(), que
 * inyecta el header component con soporte de tooltip por columna. Ver
 * public/assets/js/gx-aggrid.js y docs/AG-GRID-RECIPE.md.
 * ------------------------------------------------------------------------- */

/* Header: letra reducida + 2 filas. Aplica al label del header default y al
 * custom (TooltipHeader reusa la clase .ag-header-cell-text). */
.ag-theme-quartz .ag-header-cell-text,
.ag-theme-quartz-dark .ag-header-cell-text {
  font-size: 11px;
  line-height: 1.25;
  white-space: normal;
}

.gx-aggrid-header {
  display: flex;
  align-items: center;
  gap: 4px;
  width: 100%;
  min-width: 0;
}
.gx-aggrid-header__label {
  min-width: 0;
  flex: 0 1 auto;
}
.gx-aggrid-header__sort {
  font-size: 9px;
  color: #6b7280;
  flex: none;
}
.gx-aggrid-header__help {
  flex: none;
  display: inline-flex;
  align-items: center;
  color: #9ca3af;
  cursor: help;
  transition: color 0.12s ease;
}
.gx-aggrid-header__help:hover {
  color: #3b82f6;
}
.ag-theme-quartz-dark .gx-aggrid-header__help {
  color: #6b7280;
}
.ag-theme-quartz-dark .gx-aggrid-header__help:hover {
  color: #60a5fa;
}

/* Botón de menú/filtro reconstruido (el headerComponent custom no hereda el del
   header default). Siempre visible (fijo); se resalta en azul cuando la columna
   tiene filtro activo — paridad con el embudo del header default. */
.gx-aggrid-header__menu {
  flex: none;
  display: inline-flex;
  align-items: center;
  margin-left: auto;
  color: #9ca3af;
  cursor: pointer;
  transition: color 0.12s ease;
}
.gx-aggrid-header__menu:hover {
  color: #3b82f6;
}
.gx-aggrid-header--filtered .gx-aggrid-header__menu {
  color: #3b82f6;
}
.ag-theme-quartz-dark .gx-aggrid-header__menu {
  color: #6b7280;
}
.ag-theme-quartz-dark .gx-aggrid-header__menu:hover,
.ag-theme-quartz-dark .gx-aggrid-header--filtered .gx-aggrid-header__menu {
  color: #60a5fa;
}

/* Popover (teleportado a <body>) ------------------------------------------- */
.gx-aggrid-tooltip {
  position: fixed;
  z-index: 10000;
  max-width: 320px;
  padding: 12px 16px;
  background: #fff;
  border: 1.5px solid #3b82f6;
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.14);
  color: #1f2937;
  font-size: 13px;
}
.dark .gx-aggrid-tooltip,
html.dark .gx-aggrid-tooltip {
  background: #111827;
  border-color: #3b82f6;
  color: #e5e7eb;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
}

/* Fórmula (título + nombre = fracción × algo) ------------------------------ */
.gx-formula__title {
  color: #2563eb;
  font-weight: 600;
  font-size: 12.5px;
  margin-bottom: 10px;
}
.dark .gx-formula__title {
  color: #60a5fa;
}
.gx-formula {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-size: 14px;
}
.gx-formula__name {
  font-weight: 600;
}
.gx-formula__eq {
  color: #6b7280;
}
.gx-formula__suffix {
  white-space: nowrap;
}
.gx-frac {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}
.gx-frac__num {
  padding: 0 8px 3px;
}
.gx-frac__den {
  padding: 3px 8px 0;
  border-top: 1.5px solid currentColor;
}

/*
 * Modal de "Nuevo reembolso" con preview de PDF.
 *
 * Por qué: el bundle TailAdmin no precompila varias utilidades que este layout
 * necesita (max-w-5xl, lg:flex-1, lg:overflow-* , transition-[max-width], etc.),
 * y esas clases fallan en SILENCIO. En vez de regenerar el bundle, este bloque
 * encapsula el comportamiento: el modal arranca angosto (solo formulario) y al
 * cargar el PDF se ensancha mostrando el preview a la DERECHA en desktop y DEBAJO
 * en móvil. Activado con la clase .gx-reembolso-modal--wide (la pone Alpine cuando
 * hay pdfPreviewUrl).
 */
.gx-reembolso-modal {
  max-width: 36rem; /* equivalente a max-w-xl: solo formulario */
  transition: max-width 0.2s ease;
}
.gx-reembolso-modal.gx-reembolso-modal--wide {
  max-width: 64rem; /* ~max-w-5xl: deja espacio para el preview */
}
/* Paso 2 (centros de costos, Gerente Distrital): la grid es estrecha (centro de
   costos + monto), así que el ensanchado solo busca alojar grid + preview del PDF
   lado a lado en desktop (≥1024px). La pone Alpine cuando hay preview en step 2. */
.gx-reembolso-modal.gx-reembolso-modal--cc {
  max-width: 64rem;
}
/* En el paso 2 el formulario (grid) usa el ancho completo del cuerpo, sin el tope
   de 36rem que aplica --wide al panel del formulario. */
.gx-reembolso-modal--cc .gx-reembolso-form {
  max-width: none;
}
/* El preview en el paso 2 se OCULTA en pantallas chicas (la grid necesita todo el
   ancho); reaparece en desktop (regla en el media query de abajo). */
.gx-reembolso-preview--step2 {
  display: none;
}
/* Cuerpo: apilado en móvil (form arriba, preview abajo), lado a lado en desktop. */
.gx-reembolso-body {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1 1 auto;
  overflow-y: auto;
}
/* Panel del formulario: ancho fijo cuando hay preview (en desktop). */
.gx-reembolso-form {
  flex: 1 1 auto;
  min-width: 0;
}
/* Panel del preview: ocupa el resto en desktop, altura acotada en móvil. En móvil
   damos un min-height para que el placeholder/loader sea visible aunque no haya PDF.
   El frame es flex:1 para llenar lo que quede bajo el header de botones (detalle). */
.gx-reembolso-preview {
  display: flex;
  flex-direction: column;
  min-height: 60vh;
}
.gx-reembolso-preview__frame {
  width: 100%;
  flex: 1 1 auto;
  min-height: 50vh;
}

@media (min-width: 1024px) {
  /* Altura fija alta DESDE EL INICIO (antes de cargar el PDF) para que el preview
     se perciba grande y no haya salto de altura al aparecer. El cuerpo flex llena
     este alto; sin PDF el formulario simplemente queda con más aire. */
  .gx-reembolso-modal {
    height: 85vh;
  }
  .gx-reembolso-body {
    flex-direction: row;
    overflow: hidden;
  }
  .gx-reembolso-form {
    overflow-y: auto;
  }
  .gx-reembolso-modal--wide .gx-reembolso-form {
    /* Ancho FIJO (flex: 0 0) — igual que el paso 2: con flex 1 1 auto el formulario
       cede ancho al preview (ambos grow:1 reparten el espacio libre) y el PDF acaba
       consumiendo ~90% del modal. Pinneado a 36rem el form mantiene su tamaño y el
       PDF ocupa solo el resto (~26rem en un modal de 64rem). */
    flex: 0 0 36rem;
    width: 36rem;
    max-width: 36rem;
    border-right: 1px solid rgb(243 244 246); /* gray-100 */
  }
  .dark .gx-reembolso-modal--wide .gx-reembolso-form {
    border-right-color: rgb(31 41 55); /* gray-800 */
  }
  .gx-reembolso-preview {
    flex: 1 1 auto;
    min-height: 0;
  }
  .gx-reembolso-preview__frame {
    min-height: 0;
  }
  /* Paso 2 en desktop: el preview reaparece lado a lado con la grid. La grid (form)
     se acota SOLO si hay preview presente (:has) para dejar ancho cómodo al PDF;
     sin preview, la grid sigue usando todo el ancho (sin borde ni hueco a la derecha). */
  .gx-reembolso-preview--step2 {
    display: flex;
  }
  .gx-reembolso-modal--cc:has(.gx-reembolso-preview--step2) .gx-reembolso-form {
    /* Ancho FIJO (flex: 0 0) — sin esto, al borrar rows el contenido encoge y el flex
       item cede ancho, dejando que el PDF consuma más de la izquierda. La grid es
       estrecha (CC + monto), así que 34rem alcanza y deja ~30rem al preview. */
    flex: 0 0 34rem;
    width: 34rem;
    max-width: 34rem;
    border-right: 1px solid rgb(243 244 246); /* gray-100 */
  }
  .dark .gx-reembolso-modal--cc:has(.gx-reembolso-preview--step2) .gx-reembolso-form {
    border-right-color: rgb(31 41 55); /* gray-800 */
  }
}

/* ── Carrusel coverflow (gx.coverflow / partials/image-carousel) ──────────────
   Solo las propiedades 3D que el bundle NO precompila; el resto de transforms
   por slide van inline vía slideStyle(i). Dimensiones del slide/contenedor aquí
   para no depender de arbitrary values ausentes. */
.gx-coverflow {
  position: relative;
  perspective: 1100px;
  height: 20rem;
}
.gx-coverflow__track {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
}
.gx-coverflow__slide {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 17rem;
  height: 14rem;
  transition:
    transform 0.35s ease,
    opacity 0.35s ease;
  backface-visibility: hidden;
  will-change: transform;
}

/* gx.confirm debe quedar SIEMPRE por encima de cualquier modal (que usan z-999999
   y, al teleportarse al body, empatan y ganan por orden de DOM). El confirm es la
   interacción terminal: un z mayor lo mantiene arriba aunque se invoque desde un modal. */
.z-\[1000000\] {
  z-index: 1000000;
}

/* ── Columna fija en scroll horizontal (gx-stickycol) ─────────────────────────
   Mantiene la primera columna (ID Requisición en Reembolsos) pegada a la izquierda
   al hacer scroll-X de una tabla con scroll interno. El bundle no compila
   `sticky left-0` con el z-index/fondo que esto necesita, así que vive aquí.
   El fondo es OPACO y por contexto (head/body/foot/hover) para tapar las celdas
   que pasan por debajo; el z-index supera a thead/tfoot sticky en las esquinas. */
.gx-stickycol {
  position: sticky;
  left: 0;
}
/* Cuerpo: sobre las celdas normales (z:0), debajo de head/foot sticky. */
.gx-stickycol--body {
  z-index: 15;
  background: #fff;
}
.dark .gx-stickycol--body {
  background: rgb(17 24 39); /* gray-900: igual que el contenedor de la tabla en dark */
}
/* Hover de fila: el fondo de la celda fija sigue al de la fila (gray-50 / white-3). */
tbody tr:hover .gx-stickycol--body {
  background: rgb(249 250 251); /* gray-50 */
}
.dark tbody tr:hover .gx-stickycol--body {
  background: rgb(31 41 55); /* gray-800 aprox (white/[0.03] sobre gray-900) */
}
/* Esquina header (sticky top + sticky left): z mayor que thead (z-20). */
.gx-stickycol--head {
  z-index: 30;
  background: rgb(249 250 251); /* gray-50: igual que thead */
}
.dark .gx-stickycol--head {
  background: rgb(17 24 39); /* gray-900: igual que thead */
}
/* Esquina footer (sticky bottom + sticky left): z mayor que tfoot (z-20). */
.gx-stickycol--foot {
  z-index: 30;
  background: rgb(243 244 246); /* gray-100: igual que tfoot */
}
.dark .gx-stickycol--foot {
  background: rgb(31 41 55); /* gray-800: igual que tfoot */
}

/* border-current: color de borde = color de texto. Lo usa la píldora provisional del
   día en curso ("¿cómo voy hoy?") para que el borde punteado tome el tono de la fase. */
.border-current {
  border-color: currentColor;
}

/* ── TreeGrid de Bonos y Comisiones: alto fijo + scroll interno + thead sticky ──
   El header ocupa 2 filas (encabezados + filtros de columna). Ambas se pegan al
   tope del contenedor scrolleable. La 2.a fila se pega debajo de la 1.a usando el
   alto real de la fila de encabezados (--gx-tg-head-h). position:sticky en <thead>
   no es fiable cross-browser en tablas, por eso se aplica por <th>. */
.gx-treegrid-scroll {
  max-height: 65vh;
}
.gx-treegrid-head-row > th {
  position: sticky;
  top: 0;
  z-index: 20;
  background: rgb(249 250 251); /* gray-50: igual que la fila de encabezados */
}
.dark .gx-treegrid-head-row > th {
  background: rgb(31 41 55); /* gray-800 */
}
.gx-treegrid-filter-row > th {
  position: sticky;
  top: var(--gx-tg-head-h, 44px);
  z-index: 20;
  background: rgb(255 255 255); /* white: igual que la fila de filtros */
}
.dark .gx-treegrid-filter-row > th {
  background: rgb(17 24 39); /* gray-900 */
}
/* tfoot sticky abajo (total de empleados + suma de comisiones). */
.gx-treegrid-foot-row > td {
  position: sticky;
  bottom: 0;
  z-index: 20;
  background: rgb(243 244 246); /* gray-100: igual que la fila de totales */
}
.dark .gx-treegrid-foot-row > td {
  background: rgb(31 41 55); /* gray-800 */
}

/* Utilities faltantes en el bundle compilado (usadas por el TreeGrid de Bonos). */
.table-fixed { table-layout: fixed; }
.min-w-0 { min-width: 0; }

/* display:contents — el wrapper no participa del layout y sus hijos fluyen en el
   grid del padre. Lo usan los partials con x-data propio (Empleados → Herramientas)
   para que sus cards compartan un solo grid sin romper el scope Alpine. */
.gx-contents { display: contents; }

/* ============================================================
 * Birrete 🎓 (documentación por sección) — halo pulsante cuando
 * la doc está pendiente de leer o con cambios sin releer
 * (Universidad · Documentación GX). El bundle no trae
 * animate-pulse; el efecto vive aquí como keyframe gx-*.
 * ============================================================ */
@keyframes gx-birrete-pulse {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--color-brand-500) 45%, transparent); }
  50%      { box-shadow: 0 0 0 7px color-mix(in srgb, var(--color-brand-500) 0%, transparent); }
}
.gx-birrete-attn {
  border-color: var(--color-brand-400) !important;
  color: var(--color-brand-500) !important;
  animation: gx-birrete-pulse 1.8s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
@media (prefers-reduced-motion: reduce) {
  .gx-birrete-attn {
    animation: none;
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-brand-500) 30%, transparent);
  }
}

/* ============================================================
 * Sello de certificación del birrete — estampa de estrella que
 * marca que la sección exige examen (Universidad, etapa 2).
 *
 * En px FIJOS a propósito. El modo compacto reescala la variable
 * `--spacing` (0.25rem a 0.19rem), que gobierna las utilidades de
 * alto, ancho e inset: con clases de Tailwind el sello caía de
 * 16 px a 12 px mientras `border-width` NO se reescala, así que el
 * anillo pasaba de ocupar el 25 % del círculo al 33 % y la estrella
 * quedaba como una manchita. Aquí el tamaño no depende de la
 * densidad; solo se afina un poco en compacto.
 *
 * El anillo va por `box-shadow` y no por `border`: no consume caja
 * interior, así que la estrella conserva su tamaño completo.
 *
 * OJO al escribir estos comentarios: la secuencia asterisco-slash
 * cierra el comentario. Escribirla dentro de la prosa (por ejemplo al
 * enumerar utilidades con comodín) convierte el resto del texto en
 * CSS basura y el navegador descarta la regla EN SILENCIO — fue un
 * bug real aquí: el sello salía sin fondo verde y sin tamaño.
 * ============================================================ */
.gx-cert-seal {
  position: absolute;
  /* Volado sobre la esquina: se lee como una estampa pegada encima del
     birrete, no como parte del dibujo del birrete. */
  top: -4px;
  right: -4px;
  width: 18px;
  height: 18px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 9999px;
  background-color: var(--color-success-500);
  color: #fff;
  box-shadow: 0 0 0 2px #fff;
}
.dark .gx-cert-seal { box-shadow: 0 0 0 2px var(--color-gray-900); }
/* La estrella ocupa casi todo el interior (14 px disponibles): a menos de eso
   se ve como un asterisco y no como una estrella. */
.gx-cert-seal svg { width: 13px; height: 13px; display: block; }

/* Compacto: el birrete baja de 40 px a ~30 px, así que el sello y su anillo se
   reducen en la misma proporción para que no se vea más grueso de lo que debe. */
.compact .gx-cert-seal {
  top: -3px;
  right: -3px;
  width: 15px;
  height: 15px;
  box-shadow: 0 0 0 1.5px #fff;
}
/* `dark` y `compact` viven en el MISMO elemento (el body), no anidados: el
   selector tiene que ser `.dark.compact` (sin espacio). Además su especificidad
   (0,3,0) le gana a `.compact .gx-cert-seal`, que si no pintaría el anillo
   blanco encima del oscuro por ir después en el archivo. */
.dark.compact .gx-cert-seal { box-shadow: 0 0 0 1.5px var(--color-gray-900); }
.compact .gx-cert-seal svg { width: 11px; height: 11px; }

/* Texto multi-párrafo respetando saltos de línea (whitespace-pre-line NO está
   en el bundle compilado). Usado por el resumen ejecutivo del análisis IA
   (pestaña Hallazgos de Administración de Horarios). */
.gx-pre-line { white-space: pre-line; }

/* Formato Markdown (subconjunto Slack/WhatsApp) en el cuerpo de los mensajes
   del chat — renderMessage() en chat-ui.js. El bundle no trae `prose` ni estilos
   para <code>/<pre>/<blockquote>, y preflight normaliza <strong>/<em>/<del>, así
   que se fijan explícitos aquí. El contenedor .chat-md-body usa whitespace-pre-line;
   los bloques de código lo sobreescriben con `pre` para preservar su formato. */
.chat-md-body strong { font-weight: 600; }
.chat-md-body em     { font-style: italic; }
.chat-md-body del    { text-decoration: line-through; opacity: 0.75; }

.chat-md-body code.gx-md-code,
.chat-md-body pre.gx-md-pre {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  font-size: 0.85em;
}
.chat-md-body code.gx-md-code {
  background-color: rgba(0, 0, 0, 0.06);
  padding: 0.1em 0.35em;
  border-radius: 4px;
}
.chat-md-body pre.gx-md-pre {
  white-space: pre;
  overflow-x: auto;
  max-width: 100%;
  background-color: rgba(0, 0, 0, 0.06);
  padding: 0.6em 0.8em;
  border-radius: 8px;
  margin: 0.35em 0;
  line-height: 1.45;
}
.chat-md-body pre.gx-md-pre code {
  background: none;
  padding: 0;
  border-radius: 0;
  font-size: inherit;
}
.dark .chat-md-body code.gx-md-code,
.dark .chat-md-body pre.gx-md-pre {
  background-color: rgba(255, 255, 255, 0.08);
}
.chat-md-body blockquote.gx-md-quote {
  border-left: 3px solid var(--color-brand-400, #95c45d);
  padding-left: 0.6em;
  margin: 0.2em 0;
  color: #4b5563; /* gray-600 */
}
.dark .chat-md-body blockquote.gx-md-quote {
  color: #9ca3af; /* gray-400 */
}

/* ---------------------------------------------------------------------------
   Streaming de agentes IA en el chat (burbuja provisional SSE).
   El bundle NO trae animate-pulse; cursor y dots viven aquí. */
.gx-stream-cursor {
  display: inline-block;
  width: 2px;
  height: 1em;
  margin-left: 2px;
  vertical-align: text-bottom;
  background: currentColor;
  animation: gx-stream-blink 1s steps(2, start) infinite;
}
@keyframes gx-stream-blink {
  to { visibility: hidden; }
}
.gx-stream-dots {
  display: inline-flex;
  gap: 3px;
  align-items: center;
}
.gx-stream-dots span {
  width: 4px;
  height: 4px;
  border-radius: 9999px;
  background: currentColor;
  opacity: 0.35;
  animation: gx-stream-dot 1.2s infinite;
}
.gx-stream-dots span:nth-child(2) { animation-delay: 0.2s; }
.gx-stream-dots span:nth-child(3) { animation-delay: 0.4s; }
@keyframes gx-stream-dot {
  0%, 60%, 100% { opacity: 0.35; transform: none; }
  30% { opacity: 1; transform: translateY(-2px); }
}

/* ---------------------------------------------------------------------------
   Fuentes/Referencias del agente IA — bloque colapsable (estilo chats de IA).
   renderMessage() extrae el pie "Fuentes:"/"Referencias:" y lo pinta como
   <details>; cerrado por defecto, se abre al clic. El bundle no trae estilos
   para <details>, viven aquí. */
.chat-md-body details.gx-sources {
  margin-top: 8px;
  border-top: 1px solid rgba(0, 0, 0, 0.08);
  padding-top: 6px;
}
.dark .chat-md-body details.gx-sources {
  border-top-color: rgba(255, 255, 255, 0.1);
}
.chat-md-body details.gx-sources > summary {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  cursor: pointer;
  list-style: none;
  font-size: 12px;
  font-weight: 500;
  color: var(--color-brand-500, #6aa84f);
  user-select: none;
}
.chat-md-body details.gx-sources > summary::-webkit-details-marker { display: none; }
.chat-md-body details.gx-sources > summary::before {
  content: '▸';
  display: inline-block;
  font-size: 10px;
  line-height: 1;
  transition: transform 0.15s ease;
}
.chat-md-body details.gx-sources[open] > summary::before {
  transform: rotate(90deg);
}
.chat-md-body details.gx-sources .gx-sources-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 9999px;
  background: var(--color-brand-500, #6aa84f);
  color: #fff;
  font-size: 10px;
  font-weight: 600;
}
.chat-md-body details.gx-sources ul {
  margin: 6px 0 2px;
  padding-left: 18px;
  list-style: disc;
  font-size: 12px;
  line-height: 1.5;
  color: #6b7280; /* gray-500 */
}
.dark .chat-md-body details.gx-sources ul {
  color: #9ca3af; /* gray-400 */
}

/* Indicador de entorno no-producción (partials/env-banner.php) */
.gx-env-line {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: var(--color-warning-500, #f79009);
  z-index: 999999;
  pointer-events: none;
}
.gx-env-pill {
  position: fixed;
  top: 3px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 999999;
  pointer-events: none;
  font-size: 11px;
  line-height: 1;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: #fff;
  background: var(--color-warning-500, #f79009);
  padding: 3px 10px;
  border-radius: 0 0 8px 8px;
  opacity: 0.92;
}

/* ---------------------------------------------------------------------------
   .gx-num — <input type="number"> sin los spinners nativos
   Las flechas de incrementar/decrementar las pinta el navegador pegadas al
   valor y estorban en tablas con inputs angostos. Aquí se ocultan: la captura
   queda por teclado (y las flechas ↑/↓ del teclado siguen funcionando, igual
   que la validación de min/max/step del tipo number).
   .gx-tnum — dígitos de ancho fijo para que columnas de ids queden alineadas
   (`tabular-nums` no está precompilado en el bundle de TailAdmin).
--------------------------------------------------------------------------- */
.gx-num::-webkit-inner-spin-button,
.gx-num::-webkit-outer-spin-button {
  appearance: none;
  -webkit-appearance: none;
  margin: 0;
}

.gx-num {
  -moz-appearance: textfield; /* Firefox: quita el spinner que aparece al hover */
  appearance: textfield;
}

.gx-tnum {
  font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------------------
   Reclutamiento (E2) + utilities ausentes del bundle que YA usaban vistas
   existentes con fallo silencioso (norma: utilities faltantes → custom.css).

   - bg-gray-900/60: el backdrop que documenta MODAL-RECIPE; el bundle solo
     trae /50, así que todos los modales dimmeaban menos de lo especificado.
   - shadow-2xl: contenedor del modal (users/index.php y sucesores).
   - disabled:opacity-40: botones Anterior/Siguiente de la paginación.
   - hover:underline: links de acción inline (tickets, requisiciones).
   - sm:w-80: ancho del combobox de zonas en toolbars.
   - pb-2.5 / border-l-2 / max-w-[75%]: burbujas y quotes del thread de
     candidaturas (paridad con el thread de Support).
--------------------------------------------------------------------------- */
.bg-gray-900\/60 {
  background-color: rgb(16 24 40 / 0.6);
}

.shadow-2xl {
  box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);
}

.disabled\:opacity-40:disabled {
  opacity: 0.4;
}

.hover\:underline:hover {
  text-decoration-line: underline;
}

@media (min-width: 640px) {
  .sm\:w-80 {
    width: 20rem;
  }
}

.pb-2\.5 {
  padding-bottom: 0.625rem;
}

.border-l-2 {
  border-left-width: 2px;
}

.max-w-\[75\%\] {
  max-width: 75%;
}

/* Ancho mínimo de las tablas del dashboard de Reclutamiento (scroll-x en mobile) */
.min-w-\[420px\] {
  min-width: 420px;
}

/* Portal de candidatos — detalle de postulación en 2 columnas (proceso+herramientas | chat).
   Split propio para no pelear con el plumbing de altura del layout público. */
.max-w-6xl { max-width: 72rem; }
.candidate-split { display: flex; flex-direction: column; gap: 1rem; }
.candidate-chat { display: flex; flex-direction: column; height: 32rem; }
.candidate-bubble { max-width: 85%; }
@media (min-width: 1024px) {
  /* El 18rem descuenta header + márgenes + el footer legal del portal (una sola
     fila en este breakpoint). Con los 14rem originales el footer empujaba la
     página y aparecía scroll vertical en la vista de chat. */
  .candidate-split { flex-direction: row; align-items: stretch; height: calc(100dvh - 18rem); }
  .candidate-tools { width: 42%; overflow-y: auto; }
  .candidate-chat  { width: 58%; height: auto; }
}

/* Bolsa /trabaja — la lista de vacantes va en 2 columnas desde lg y la página se
   ensancha para que las tarjetas no queden apretadas (las direcciones de sucursal
   son largas). El bundle no precompila variantes responsivas de max-width:
   lg:max-w-5xl falla en SILENCIO. */
@media (min-width: 1024px) {
  .lg\:max-w-5xl { max-width: 64rem; }
}

/* ApexCharts — menú de descarga limitado a PNG.
 * Apex no permite ocultar entradas del menú por configuración (`export` solo
 * define nombres de archivo), así que se apagan SVG y CSV por CSS. Por qué:
 * el CSV de un reporte sale por el botón Exportar (gx.export, receta del
 * proyecto) y tenerlo también aquí ofrece dos rutas distintas al mismo dato. */
.gx-chart-png-only .apexcharts-menu-item.exportSVG,
.gx-chart-png-only .apexcharts-menu-item.exportCSV {
  display: none;
}

/* El card en pantalla completa queda sobre el backdrop negro del navegador:
 * sin fondo propio, el tema claro se ve como texto oscuro sobre negro. */
[data-chart-card]:fullscreen {
  padding: 1.5rem;
  background-color: rgb(255 255 255);
}

.dark [data-chart-card]:fullscreen {
  background-color: rgb(17 24 39); /* gray-900 */
}

/* Costo de Inventario — reparto tabla / gráficas (45% / resto). No cae en la
 * escala de 12 columnas de Tailwind, y el bundle está precompilado: se define
 * aquí en vez de con una arbitrary value que no existiría en style.css.
 *
 * Era 35% hasta que la tabla ganó la columna "Categorías" (resumen del filtro):
 * con 4 columnas ya no cabía sin scroll horizontal, así que la tabla toma 10
 * puntos que salen de las gráficas (siguen entrando dos apiladas por página). */
@media (min-width: 1024px) {
  .gx-inv-split {
    grid-template-columns: 45% minmax(0, 1fr);
  }
}

/* ===========================================================================
 * Análisis de Distribuciones — tintes por columna del reporte producto × tienda.
 *
 * Paleta TAL CUAL la del reporte legacy (EPOD): son pasteles suaves y esos valores
 * exactos son el acuerdo visual con el usuario — no "subirlos de tono".
 *
 * Por qué en custom.css y no con clases del bundle: TailAdmin solo trae el nivel
 * -50 de estas paletas (`bg-success-50`, `bg-error-50`…), que no coincide con
 * estos valores, y el café y los dos amarillos no existen en la paleta.
 *
 * Cada grupo fija también el color de texto: sobre estos fondos claros la letra va
 * oscura en ambos temas, así que en dark mode el tinte se vuelve translúcido para
 * que el texto claro del bundle siga legible.
 * ======================================================================== */
.gx-dcol-inv           { background-color: rgb(212, 230, 247); color: rgb(31, 41, 55); }
.gx-dcol-alert         { background-color: rgb(243, 216, 218); color: rgb(31, 41, 55); }
.gx-dcol-ok            { background-color: rgb(202, 229, 205); color: rgb(31, 41, 55); }
.gx-dcol-cafe          { background-color: rgb(230, 214, 195); color: rgb(31, 41, 55); }
.gx-dcol-yellow        { background-color: rgb(253, 244, 208); color: rgb(31, 41, 55); }
.gx-dcol-yellow-strong { background-color: rgb(251, 230, 186); color: rgb(31, 41, 55); }

.dark .gx-dcol-inv           { background-color: rgba(96, 165, 250, 0.16); color: rgba(255, 255, 255, 0.9); }
.dark .gx-dcol-alert         { background-color: rgba(248, 113, 113, 0.16); color: rgba(255, 255, 255, 0.9); }
.dark .gx-dcol-ok            { background-color: rgba(52, 211, 153, 0.16);  color: rgba(255, 255, 255, 0.9); }
.dark .gx-dcol-cafe          { background-color: rgba(180, 143, 100, 0.20); color: rgba(255, 255, 255, 0.9); }
.dark .gx-dcol-yellow        { background-color: rgba(250, 204, 21, 0.12);  color: rgba(255, 255, 255, 0.9); }
.dark .gx-dcol-yellow-strong { background-color: rgba(250, 176, 21, 0.16);  color: rgba(255, 255, 255, 0.9); }

/* .gx-grid-gutter — columna vacía al final de las dos grids del análisis (la de
 * producto × tienda y la vertical). No lleva dato: existe para que el scroll
 * horizontal no termine pegado a la última tienda, que se sentía como si la tabla
 * estuviera cortada y obligaba a arrastrar hasta el tope para leer la última
 * columna completa.
 *
 * Es una celda real y no `padding-right` del contenedor con scroll: la tabla usa
 * `min-w-full`, así que el padding le quitaría ancho al contenido cuando la tabla
 * NO desborda, y el área desplazable que aporta el padding final no se comporta
 * igual en todos los navegadores. Una columna se comporta como cualquier otra.
 * ======================================================================== */
.gx-grid-gutter {
  width: 7rem;
  min-width: 7rem;
}

/* Leyenda de esa columna: dice que ahí no falta un dato. Va SOLO en el header, en
 * dos renglones ("Espacio" / "vacío") y a 10 px, para que quepa en el ancho de la
 * columna sin ensancharla; en el cuerpo y en el footer la columna se queda vacía. */
.gx-grid-gutter-label {
  font-size: 0.625rem;
  line-height: 1.2;
  letter-spacing: 0.02em;
}

/* ===========================================================================
 * Análisis de Distribuciones — celdas I / V / D de cada tienda destino.
 *
 * Distinto de los tintes `gx-dcol-*` de arriba: esos pintan una COLUMNA completa
 * (el dato es siempre el mismo tipo), aquí el color depende del ESTADO de la celda
 * — la I se lee junto con la V de la misma tienda:
 *
 *   I = 0            → rojo   (no hay pieza en la tienda: hueco que urge cubrir)
 *   I = 1 y V = 0    → café   (pieza parada: hay una y no se ha vendido)
 *   I = 1 y V >= 1   → verde  (uno-uno: la única pieza está rotando)
 *   I > 1            → gris   (con piso: es contexto, no llama la atención)
 *
 * `.gx-cell-d` es el tinte de la distribución (la columna que se viene a ver), más
 * acentuado que el `bg-blue-light-50` del bundle a pedido del usuario: con el
 * pastel anterior se perdía entre las I grises al recorrer 68 tiendas.
 * Solo se aplica cuando la celda tiene piezas: las D en cero van SIN tinte
 * (`storeCellClass`), porque pintarlas también llenaba el renglón de azul y
 * obligaba a leer los números para ver a quién se surte.
 *
 * Los valores exactos los definió el usuario; no "subirlos de tono" por cuenta
 * propia. Como en `gx-dcol-*`, cada clase fija también el color del texto (en dark
 * el tinte se vuelve translúcido para que la letra clara siga legible).
 * ======================================================================== */
.gx-cell-i-cero  { background-color: rgb(243, 216, 218); color: rgb(31, 41, 55); }
.gx-cell-i-uno   { background-color: rgb(217, 236, 219); color: rgb(31, 41, 55); }
.gx-cell-i-quieto { background-color: rgb(230, 214, 195); color: rgb(31, 41, 55); }
.gx-cell-i-mult  { background-color: rgb(230, 230, 230); color: rgb(31, 41, 55); }
.gx-cell-d       { background-color: rgb(209, 228, 252); color: rgb(31, 41, 55); }

/* .gx-store-edge — cierre de tienda: el borde derecho de la columna D, un pixel más
 * grueso que las líneas normales de la rejilla. Con 68 tiendas en horizontal, los
 * tríos I/V/D se leían como una sola franja de números; esta línea dice dónde
 * termina una tienda y empieza la siguiente.
 *
 * Va aquí y no como `border-r-2` porque esa clase NO existe en el bundle compilado
 * de TailAdmin (y una clase ausente falla en silencio). La tabla usa
 * `border-collapse: collapse`, así que estos 2px ganan sobre el borde de 1px de la
 * celda vecina en vez de sumarse: la línea queda de 2px, no de 3.
 *
 * Se aplica a las tres alturas de la columna —título, celda y total— para que el
 * cierre corra completo. */
.gx-store-edge      { border-right: 2px solid rgb(209, 213, 219); }
.dark .gx-store-edge { border-right-color: rgba(255, 255, 255, 0.16); }

.dark .gx-cell-i-cero   { background-color: rgba(248, 113, 113, 0.16); color: rgba(255, 255, 255, 0.9); }
.dark .gx-cell-i-uno    { background-color: rgba(52, 211, 153, 0.16);  color: rgba(255, 255, 255, 0.9); }
.dark .gx-cell-i-quieto { background-color: rgba(180, 143, 100, 0.20); color: rgba(255, 255, 255, 0.9); }
.dark .gx-cell-i-mult   { background-color: rgba(255, 255, 255, 0.08); color: rgba(255, 255, 255, 0.9); }
.dark .gx-cell-d        { background-color: rgba(96, 165, 250, 0.24);  color: rgba(255, 255, 255, 0.9); }

/* .gx-cell-tri — "esta celda tiene algo que decir": triangulito en la esquina
 * inferior derecha, de la misma familia de azul que el tinte de la columna de
 * distribución (bg-blue-light-50) pero MÁS FUERTE. Marca la celda sin cambiarle el
 * tinte a la columna ni robarle ancho al número (un anillo interior, que fue el
 * primer intento, competía con el tinte y se leía como "celda seleccionada").
 *
 * Es genérico A PROPÓSITO: el QUÉ dice lo pone el `title` de cada celda, no el
 * color. Hoy lo usan dos avisos distintos —columna D ajustada a mano y "Prod. para
 * distribuir" por debajo del inventario del origen— y la geometría vive aquí una
 * sola vez.
 *
 * Truco clásico de bordes: caja de 0×0 con el borde inferior pintado y el izquierdo
 * transparente ⇒ ángulo recto justo en la esquina inferior derecha.
 * `pointer-events: none` para que el triángulo no le robe el doble clic a la celda. */
.gx-cell-tri {
  position: relative;
}

.gx-cell-tri::after {
  content: '';
  position: absolute;
  right: 0;
  bottom: 0;
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 0 0 9px 9px;
  border-color: transparent transparent rgb(2, 132, 199) transparent;
  pointer-events: none;
}

.dark .gx-cell-tri::after {
  border-bottom-color: rgb(56, 189, 248);
}

/* Variante ÁMBAR del mismo triangulito: "hay piezas que esta celda no cuenta"
 * (mercancía en tránsito o en preparación). Comparte la geometría de arriba y solo
 * cambia el color, porque el azul ya significa otra cosa —"alguien tocó esto"— y dos
 * avisos distintos del mismo color no se pueden distinguir. Lo que dice cada marca
 * sigue viviendo en el `title` de la celda. */
.gx-cell-tri--pendiente::after {
  border-bottom-color: rgb(217, 119, 6);
}

.dark .gx-cell-tri--pendiente::after {
  border-bottom-color: rgb(251, 146, 60);
}

/* .gx-cell-preventa — "esta celda tiene preventa": cuadrito morado con una P en la
 * esquina inferior IZQUIERDA de la columna D. Va en `::before` a propósito: la esquina
 * derecha y el `::after` ya son del triangulito (`gx-cell-tri`), así que una celda
 * ajustada a mano Y con preventa muestra las dos marcas sin taparse.
 *
 * Morado porque el azul ("alguien tocó esto") y el ámbar ("hay piezas que no se
 * cuentan") ya significan otra cosa. El qué dice la marca sigue viviendo en el `title`
 * de la celda. `pointer-events: none` para no robarle el doble clic a la celda. */
.gx-cell-preventa {
  position: relative;
}

.gx-cell-preventa::before {
  content: 'P';
  position: absolute;
  left: 1px;
  bottom: 1px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 11px;
  height: 11px;
  border-radius: 3px;
  background-color: rgb(126, 34, 206);
  color: #fff;
  font-size: 8px;
  font-weight: 700;
  line-height: 1;
  pointer-events: none;
}

.dark .gx-cell-preventa::before {
  background-color: rgb(168, 85, 247);
}

/* ===========================================================================
 * .gx-zen — "modo zen" de una tabla: el card ocupa el viewport completo y tapa
 * todo lo demás (sidebar, header, título, pestañas).
 *
 * z-index 99999 a propósito, NO más: el header del layout también es 99999 pero
 * está ANTES en el DOM, así que el card le gana; y los toasts / modales globales
 * (montados al final del <body>, también 99999) siguen quedando ENCIMA, que es lo
 * que se necesita. Subirlo a 100000 taparía los avisos.
 *
 * Requiere que el contenedor con scroll de la tabla sea el hijo flexible
 * (`flex: 1; min-height: 0`), para que el thead/tfoot sticky sigan trabajando
 * sobre el mismo contenedor y no haya doble scroll.
 * ======================================================================== */
.gx-zen {
  position: fixed;
  inset: 0;
  z-index: 99999;
  display: flex;
  flex-direction: column;
  border-radius: 0;
  border: 0;
}

/* ==========================================================================
 * Reclutamiento — detalle de candidatura (`applications/detail.php`)
 *
 * La pantalla es de dos columnas: contenido del proceso a la izquierda y la
 * conversación a la derecha. Utilities que el bundle no precompila:
 *
 * - `hover:bg-error-50`: ya la usaba el botón "Eliminar (prueba)" del header
 *   con fallo silencioso (nunca se pintó el hover).
 * - `.gx-sticky-col`: la columna de la conversación se queda a la vista
 *   mientras se scrollea el expediente. Necesita `align-self: flex-start`
 *   porque un flex item se estira a la altura del contenedor y `sticky` no
 *   tendría margen para desplazarse. Solo desde `lg` — en mobile las columnas
 *   se apilan y pegarla taparía el contenido.
 *   El `top` descuenta el header sticky del layout (z-99999, ~4rem) + aire.
 * - `.gx-thread-tall`: alto del hilo. Antes era `max-h-96` fijo (24rem), que
 *   en desktop dejaba un hueco enorme junto a un expediente mucho más largo.
 * - `.gx-conv-col`: ancho de la columna de la conversación. Es PROPORCIONAL,
 *   no los 400px fijos de antes: con el sidebar abierto en un monitor de
 *   1440px el hilo quedaba en una tira angosta donde cada mensaje se partía en
 *   3 renglones. El `min-width` evita que en 1024px se comprima de más y el
 *   `max-width` evita que en ultrawide se coma la mitad de la pantalla.
 * ======================================================================== */
.hover\:bg-error-50:hover {
  background-color: var(--color-error-50);
}

/* leading-relaxed: el bundle no la precompiló y ya la usaban cinco vistas del
   portal de candidatos (bolsa, ayuda, login, casillas de consentimiento y
   postulaciones) con fallo silencioso — los textos legales largos se veían
   apretados. Al agregarla aquí se arreglan todas de una vez. */
.leading-relaxed {
  line-height: 1.625;
}

.gx-thread-tall {
  max-height: 24rem;
}

@media (min-width: 1024px) {
  .gx-sticky-col {
    position: sticky;
    top: 5.5rem;
    align-self: flex-start;
  }

  .gx-thread-tall {
    /* 19rem = header sticky + tira de estado + compositor + aire. */
    max-height: calc(100vh - 19rem);
    min-height: 22rem;
  }

  .gx-conv-col {
    width: 50%;
    min-width: 28rem;
    max-width: 42rem;
  }
}

@media (min-width: 1536px) {
  .gx-conv-col {
    width: 46%;
    max-width: 52rem;
  }
}

/* ==========================================================================
 * Impresión del aviso de privacidad (`trabaja/privacidad`)
 *
 * El aviso íntegro ofrece al candidato "una copia descargable o imprimible"
 * (§3), y el diálogo del navegador cubre ambas (imprimir / guardar como PDF).
 * Solo hay que sacar del papel el cromo del portal: header, botones y ligas de
 * navegación. `.gx-no-print` se marca en los elementos que no deben imprimirse.
 * ======================================================================== */
@media print {
  .gx-no-print,
  header {
    display: none !important;
  }
}

/* ==========================================================================
 * Velo bloqueante sobre un contenedor (no sobre la pantalla completa)
 *
 * Para operaciones largas que solo invalidan UNA zona de la vista —hoy
 * "Calcular Mínimo Necesario" sobre la tabla de productos del asistente de
 * Distribuciones—. Un overlay full-screen se siente desproporcionado; este se
 * queda dentro de la card y deja el resto de la modal legible.
 *
 * Vive aquí y no en clases del bundle porque necesita un color por tema y el
 * bundle precompilado no trae `bg-white/70` ni `bg-gray-900/70` (una clase
 * inexistente falla en silencio); un `style` inline no puede cambiar con
 * `.dark`.
 * ======================================================================== */
.gx-blocker {
  background: rgba(255, 255, 255, 0.72);
}

.dark .gx-blocker {
  background: rgba(17, 24, 39, 0.72);
}

/* ==========================================================================
 * .gx-col-side — columna lateral angosta SOLO en escritorio
 *
 * Para los cuerpos partidos lateral | principal (hoy el detalle de un
 * consolidado de Distribuciones: análisis incluidos | zonas destino). Se bajó
 * del 30% al 22% cuando la tabla de destinos ganó la columna de fecha de
 * recepción: los análisis incluidos son referencia (folio y totales) y la tabla
 * es lo que se lee.
 *
 * Vive aquí porque el ancho debe soltarse cuando las columnas se apilan en
 * móvil, y eso pide un media query: en `style` inline el porcentaje se
 * aplicaría también apilado (una tarjeta angosta pegada al borde izquierdo), y
 * el bundle no trae `lg:max-w-[22%]` (una clase inexistente falla en silencio).
 * ======================================================================== */
.gx-col-side {
  width: 100%;
}

@media (min-width: 1024px) {
  .gx-col-side {
    max-width: 22%;
    min-width: 210px;
  }
}

/* ---------------------------------------------------------------------------
   .bg-current — background-color: currentColor
   Utility estándar de Tailwind que el bundle de TailAdmin NO precompila. La usa
   el punto del chip "Sin responder" de la bandeja de candidaturas, que hereda el
   color del texto (ámbar o rojo según la antigüedad de la espera) en vez de
   duplicar el mapa de colores en dos clases. Sin esta regla el punto quedaría
   con fondo transparente — invisible, y sin ningún error.
---------------------------------------------------------------------------- */
.bg-current {
  background-color: currentColor;
}

/* ---------------------------------------------------------------------------
   .gx-text-sm-1 — un escalón de 1px por debajo de text-theme-sm
   Para celdas de texto largo que necesitan respirar más que el resto de la fila
   (hoy: la columna "Descripción" del detalle de saldos en Bonos y Comisiones).
   No se usa text-[13px]: las utilities arbitrarias NO están precompiladas en el
   bundle de TailAdmin y el navegador las ignora en silencio. Se replica el mismo
   −1px dentro de .compact, donde --text-theme-sm ya vale 13px.
---------------------------------------------------------------------------- */
.gx-text-sm-1 {
  font-size: 13px;
  line-height: 18px;
}

.compact .gx-text-sm-1 {
  font-size: 12px;
  line-height: 16px;
}

/* ---------------------------------------------------------------------------
   .gx-frozen-* — fondo de las columnas CONGELADAS del Reporte de cumplimiento
   (la franja Empleado + indicadores, `position: sticky`).

   Una celda sticky con fondo TRANSLÚCIDO deja ver las columnas de fecha pasando
   por debajo al hacer scroll horizontal: el encabezado se vuelve ilegible porque
   se leen dos textos encimados. En claro no se notaba —los tintes `-50` de
   TailAdmin son opacos—, pero sus equivalentes oscuros (`/15`, `/[0.12]`) son
   capas alfa sobre transparente, así que en modo oscuro el traslape era total.

   El color opaco de la superficie va en `background-color` y el tinte encima como
   `background-image` (un degradado de un solo color): así no hay que precalcular
   la mezcla a mano y el tono se mantiene idéntico al de la paleta.
---------------------------------------------------------------------------- */
.gx-frozen-plain { background-color: #ffffff; }
.gx-frozen-head  { background-color: #f9fafb; }  /* gray-50  */
.gx-frozen-blue  { background-color: #f0f9ff; }  /* blue-light-50 */
.gx-frozen-green { background-color: #ecfdf3; }  /* success-50 */
.gx-frozen-amber { background-color: #fffaeb; }  /* warning-50 */
.gx-frozen-brand { background-color: #ecf3ff; }  /* brand-50 */
.gx-frozen-red   { background-color: #fef3f2; }  /* error-50 */
.gx-frozen-mute  { background-color: #f2f4f7; }  /* gray-100 */

.dark .gx-frozen-plain,
.dark .gx-frozen-head,
.dark .gx-frozen-blue,
.dark .gx-frozen-green,
.dark .gx-frozen-amber,
.dark .gx-frozen-brand,
.dark .gx-frozen-red,
.dark .gx-frozen-mute { background-color: #101828; }  /* gray-900 — la base opaca */

.dark .gx-frozen-blue  { background-image: linear-gradient(rgba(11, 165, 236, .15), rgba(11, 165, 236, .15)); }
.dark .gx-frozen-green { background-image: linear-gradient(rgba(18, 183, 106, .15), rgba(18, 183, 106, .15)); }
.dark .gx-frozen-amber { background-image: linear-gradient(rgba(247, 144, 9, .15), rgba(247, 144, 9, .15)); }
.dark .gx-frozen-brand { background-image: linear-gradient(rgba(70, 95, 255, .12), rgba(70, 95, 255, .12)); }
.dark .gx-frozen-red   { background-image: linear-gradient(rgba(240, 68, 56, .15), rgba(240, 68, 56, .15)); }
.dark .gx-frozen-mute  { background-image: linear-gradient(rgba(102, 112, 133, .18), rgba(102, 112, 133, .18)); }

/* ── Matriz de permisos — columna "Acciones extra" ──────────────────────────
 * El número de columnas debe responder al ancho REAL de la celda, no al del
 * viewport: la matriz se monta en contenedores muy distintos (pantalla de
 * Permisos con panel lateral, modal de edición masiva, pestaña del detalle de
 * empleado y del puesto), así que un `sm:`/`xl:grid-cols-2` acierta en uno y
 * aplasta el resto — que fue justo el síntoma: dos columnas de ~180px con
 * claves de permiso sin espacios encimándose.
 *
 * `auto-fill` + `minmax(240px, 1fr)` reparte solas: una columna cuando no
 * caben dos, y las que quepan cuando hay espacio. El piso de 240px es el
 * ancho mínimo donde una etiqueta tipo "Incidencias (donde soy responsable)"
 * sigue siendo legible en dos renglones. */
.perm-extras-grid {
    display: grid;
    gap: 0.5rem 1.5rem;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
}

/* fill-brand-500 — NO viene en el bundle, y 13 vistas la usaban ya (access-logs,
 * vacaciones, provisión de nómina, uso de la API…) para pintar la flecha ACTIVA
 * del encabezado ordenable del estándar data-table-03.
 *
 * Al no existir, la clase se ignoraba en silencio: la flecha activa se quedaba
 * gris igual que la inactiva y no había forma visual de saber por qué columna
 * estaba ordenada la tabla. No fallaba nada — simplemente no se veía.
 *
 * El bundle sí trae fill-gray-*, fill-success-*, fill-error-* y fill-warning-*,
 * pero ninguna variante de brand. */
.fill-brand-500 {
    fill: var(--color-brand-500, #465fff);
}
.dark .dark\:fill-brand-500 {
    fill: var(--color-brand-500, #465fff);
}

/* Expediente de un endpoint sospechoso (Uso de la API → Sin autenticar).
 * Más ancho que max-w-3xl porque adentro va una tabla de 6 columnas con
 * user agents completos, y el bundle no trae max-w-4xl+ (ver .gx-ticket-modal
 * y .gx-erp-product-modal, mismo caso). Alto acotado por arriba: el expediente
 * de un caso con una sola llamada no tiene por qué ocupar la pantalla entera. */
.gx-suspect-modal {
  width: 100%;
  max-width: 60rem;
  max-height: 90vh;
}

/* opacity-25 — NO viene en el bundle (solo están 0, 40, 50, 75 y 100), y **92
 * ocurrencias en 32 archivos** ya la usaban: es el círculo de fondo del spinner
 * canónico que se copia en cada reporte
 * (`<circle class="opacity-25" …/>` + `<path class="opacity-75" …/>`).
 *
 * Al no existir, el anillo del spinner se pintaba SÓLIDO en vez de tenue, así que
 * el arco que gira no se distinguía del fondo y la animación se leía como un aro
 * grueso girando. Fallo silencioso clásico: nada rompe, solo se ve mal.
 *
 * Se agrega aquí —en vez de cambiar las 92 ocurrencias a `opacity-40`— porque el
 * markup del spinner es un idioma compartido: corregirlo en un solo lugar arregla
 * todas las vistas y evita que la próxima copia vuelva a traer la clase rota. */
.opacity-25 {
    opacity: 0.25;
}

/* Modal del detalle de rendimiento (RH → Asistencia → Rendimiento de asistencia).
 * Más ancho que max-w-3xl —el tope del bundle— porque adentro va la lista día por día
 * con fecha + causa + detalle + puntos en un solo renglón; a 768 px el detalle se parte
 * en tres líneas y deja de leerse como una tabla. Mismo caso que .gx-suspect-modal. */
.gx-perf-detail-modal {
  width: 100%;
  max-width: 56rem;
  /* El alto lo acota el modal y el scroll vive en el cuerpo (flex-1 + min-h-0), en vez de
   * un `max-h-[70vh]` en el hijo: los arbitrary values de altura NO están compilados en el
   * bundle y esa clase se ignoraba en silencio — el modal crecía hasta salirse de la
   * pantalla y el encabezado se iba con él. */
  max-height: 85vh;
  display: flex;
  flex-direction: column;
}

/* hover:bg-warning-50 — el bundle compila `bg-warning-50` pero NO su variante `hover:`,
 * así que la clase se ignora en silencio: el botón se queda sin realce al pasar el mouse
 * y nada avisa. La usan seis vistas (zonas de trabajo × 3, empleados, tickets y Auditoría
 * Móvil), todas creyendo que funciona. Se agrega acá —en vez de cambiar las seis— porque
 * el markup del botón "acción de advertencia" es un idioma compartido del proyecto:
 * arreglarlo en un lugar cubre lo que ya existe y lo que se copie después. */
.hover\:bg-warning-50:hover {
    background-color: var(--color-warning-50);
}

/* ---------------------------------------------------------------------------
 * gx.datepicker con `weekOnly`: resaltado de la SEMANA completa al pasar el mouse.
 *
 * En un campo que elige semanas, el hover nativo de flatpickr resalta UN día
 * (`background:#e6e6e6`) y al hacer clic el componente reescribe la selección a los
 * 7 días: la interfaz promete un día y entrega una semana. Con la fila entera
 * resaltada la regla se entiende ANTES del clic.
 *
 * La clase la pone `gx.datepicker` en los 7 días del bloque (ver `pintarSemana`), no
 * el CSS: los días son hermanos en una rejilla y no hay selector que alcance «los
 * otros 6 de esta fila» a partir del que tiene el puntero.
 *
 * `background` y no `background-color`: el hover nativo de flatpickr usa la
 * propiedad corta, así que con la larga ganaba él y no se veía nada.
 * ------------------------------------------------------------------------ */
.flatpickr-day.gx-week-hover {
    background: var(--color-brand-50);
    border-color: var(--color-brand-50);
}

.dark .flatpickr-day.gx-week-hover {
    background: rgba(70, 95, 255, 0.16);
    border-color: transparent;
}

/* El día seleccionado y los extremos del rango conservan su color: si el resaltado
 * los pisara, al pasar el mouse por la semana elegida se perdería de vista cuál
 * está seleccionada. */
.flatpickr-day.gx-week-hover.selected,
.flatpickr-day.gx-week-hover.startRange,
.flatpickr-day.gx-week-hover.endRange {
    background: var(--color-brand-500);
    border-color: var(--color-brand-500);
}

/* Inputs numéricos sin flechas
 * ------------------------------------------------------------------------
 * Las flechas nativas (spinner) roban ancho en celdas angostas y permiten
 * mover una cifra capturada con un clic distraído o con la rueda del mouse.
 * La captura de estos reportes es a teclado. */
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

input[type="number"] {
    -moz-appearance: textfield;
    appearance: textfield;
}

/* ═══════════════════════════════════════════════════════════════════════════
   COLORES QUE LAS VISTAS USAN Y EL BUNDLE NO COMPILÓ  (2026-08-29)

   El bundle de TailAdmin trae la PALETA COMPLETA como variables
   (`--color-brand-600`, `--color-warning-500`, …) pero solo compiló las
   utilities que la plantilla original usaba. Las vistas de gx-base fueron
   pidiendo otras y, como una clase desconocida **se ignora sin avisar**, el
   defecto nunca dio la cara: el hover que no cambia de color, el fondo tenue
   que no aparece.

   Barrido del 2026-08-29 sobre `class=`/`:class=` de todas las vistas: 150
   utilities usadas no existían. Aquí están las que se derivan de una variable
   YA definida en el bundle — ningún color inventado. Quedan 39, inventariadas
   en docs/TECH-DEBT.md: 11 piden un índice de color que el bundle no compiló y
   28 son estructurales (`w-64`, `max-w-lg`, `border-l-4`).

   Ojo con el reflejo de "falta una clase ⇒ agrégala": `dark-900` tenía 81 usos
   y se BORRÓ en vez de definirse, porque en 76 de 77 casos el equivalente que
   sí existe (`dark:bg-gray-900`) ya venía en el mismo atributo. Y la escala
   `yellow`/`amber` no se compiló: lo que la usaba se migró a la paleta de
   marca. Revisa el atributo completo antes de agregar nada.

   El caso que motivó el barrido: `partials/data-table/action-button` declara
   los tonos `warning` y `success` para sus kinds `deactivate`/`activate`, y
   los CUATRO faltaban ⇒ en ~20 vistas el ícono de activar/desactivar era el
   único de su fila sin hover.

   Mecánica copiada del bundle, no inventada: opacidades con
   `color-mix(in oklab, …, transparent)`, `dark:` como descendiente de `.dark`,
   y `hover:` envuelto en `@media (hover: hover)` para no dispararse en táctil.

   ⚠️ Generadas con un script a partir del barrido. Al agregar una clase nueva
   a una vista, verifícala antes con `./bin/bundle-has.sh <clase>`.
   ═══════════════════════════════════════════════════════════════════════════ */
.dark .dark\:text-gray-600 { color: var(--color-gray-600); }
@media (hover: hover) { .dark .dark\:hover\:bg-white\/\[0\.06\]:hover { background-color: color-mix(in oklab, var(--color-white) 6%, transparent); } }
.dark .dark\:bg-warning-500\/10 { background-color: color-mix(in oklab, var(--color-warning-500) 10%, transparent); }
.text-brand-600 { color: var(--color-brand-600); }
.dark .dark\:text-brand-300 { color: var(--color-brand-300); }
@media (hover: hover) { .hover\:border-gray-300:hover { border-color: var(--color-gray-300); } }
.dark .dark\:bg-error-500\/10 { background-color: color-mix(in oklab, var(--color-error-500) 10%, transparent); }
.focus\:ring-brand-500\/20:focus { --tw-ring-color: color-mix(in oklab, var(--color-brand-500) 20%, transparent); }
.dark .dark\:border-gray-600 { border-color: var(--color-gray-600); }
@media (hover: hover) { .dark .dark\:hover\:border-gray-700:hover { border-color: var(--color-gray-700); } }
@media (hover: hover) { .hover\:border-brand-400:hover { border-color: var(--color-brand-400); } }
.dark .dark\:bg-gray-900\/40 { background-color: color-mix(in oklab, var(--color-gray-900) 40%, transparent); }
.focus\:ring-brand-500\/30:focus { --tw-ring-color: color-mix(in oklab, var(--color-brand-500) 30%, transparent); }
.dark .dark\:bg-blue-500\/15 { background-color: color-mix(in oklab, var(--color-blue-500) 15%, transparent); }
.fill-gray-400 { fill: var(--color-gray-400); }
.dark .dark\:bg-white\/\[0\.06\] { background-color: color-mix(in oklab, var(--color-white) 6%, transparent); }
@media (hover: hover) { .dark .dark\:hover\:bg-brand-500\/10:hover { background-color: color-mix(in oklab, var(--color-brand-500) 10%, transparent); } }
@media (hover: hover) { .dark .dark\:hover\:bg-error-500\/10:hover { background-color: color-mix(in oklab, var(--color-error-500) 10%, transparent); } }
@media (hover: hover) { .hover\:bg-success-50:hover { background-color: var(--color-success-50); } }
@media (hover: hover) { .hover\:border-brand-300:hover { border-color: var(--color-brand-300); } }
.bg-white\/50 { background-color: color-mix(in oklab, var(--color-white) 50%, transparent); }
.border-warning-500\/40 { border-color: color-mix(in oklab, var(--color-warning-500) 40%, transparent); }
.dark .dark\:border-gray-800\/60 { border-color: color-mix(in oklab, var(--color-gray-800) 60%, transparent); }
@media (hover: hover) { .dark .dark\:hover\:border-gray-600:hover { border-color: var(--color-gray-600); } }
@media (hover: hover) { .dark .dark\:hover\:text-brand-300:hover { color: var(--color-brand-300); } }
@media (hover: hover) { .hover\:text-brand-400:hover { color: var(--color-brand-400); } }
@media (hover: hover) { .hover\:text-warning-500:hover { color: var(--color-warning-500); } }
.bg-error-500\/15 { background-color: color-mix(in oklab, var(--color-error-500) 15%, transparent); }
.border-brand-200 { border-color: var(--color-brand-200); }
.border-error-400 { border-color: var(--color-error-400); }
.dark .dark\:bg-gray-600 { background-color: var(--color-gray-600); }
.dark .dark\:bg-warning-500\/20 { background-color: color-mix(in oklab, var(--color-warning-500) 20%, transparent); }
.dark .dark\:border-error-500\/40 { border-color: color-mix(in oklab, var(--color-error-500) 40%, transparent); }
.dark .dark\:border-warning-500\/40 { border-color: color-mix(in oklab, var(--color-warning-500) 40%, transparent); }
@media (hover: hover) { .dark .dark\:hover\:bg-brand-500\/15:hover { background-color: color-mix(in oklab, var(--color-brand-500) 15%, transparent); } }
@media (hover: hover) { .dark .dark\:hover\:bg-brand-500\/\[0\.05\]:hover { background-color: color-mix(in oklab, var(--color-brand-500) 5%, transparent); } }
@media (hover: hover) { .dark .dark\:hover\:bg-warning-500\/10:hover { background-color: color-mix(in oklab, var(--color-warning-500) 10%, transparent); } }
.dark .dark\:ring-gray-900 { --tw-ring-color: var(--color-gray-900); }
@media (hover: hover) { .hover\:text-brand-700:hover { color: var(--color-brand-700); } }
@media (hover: hover) { .hover\:text-success-500:hover { color: var(--color-success-500); } }
.bg-gray-100\/70 { background-color: color-mix(in oklab, var(--color-gray-100) 70%, transparent); }
.bg-success-600 { background-color: var(--color-success-600); }
.bg-white\/90 { background-color: color-mix(in oklab, var(--color-white) 90%, transparent); }
.border-brand-400 { border-color: var(--color-brand-400); }
.dark .dark\:bg-brand-500\/5 { background-color: color-mix(in oklab, var(--color-brand-500) 5%, transparent); }
.dark .dark\:bg-gray-900\/50 { background-color: color-mix(in oklab, var(--color-gray-900) 50%, transparent); }
.dark .dark\:border-brand-500\/30 { border-color: color-mix(in oklab, var(--color-brand-500) 30%, transparent); }
.dark .dark\:divide-warning-500\/30 { border-color: color-mix(in oklab, var(--color-warning-500) 30%, transparent); }
.dark .dark\:fill-gray-500 { fill: var(--color-gray-500); }
@media (hover: hover) { .dark .dark\:hover\:bg-error-500\/15:hover { background-color: color-mix(in oklab, var(--color-error-500) 15%, transparent); } }
@media (hover: hover) { .dark .dark\:hover\:bg-success-500\/10:hover { background-color: color-mix(in oklab, var(--color-success-500) 10%, transparent); } }
@media (hover: hover) { .dark .dark\:hover\:bg-white\/\[0\.1\]:hover { background-color: color-mix(in oklab, var(--color-white) 10%, transparent); } }
.divide-warning-500\/30 { border-color: color-mix(in oklab, var(--color-warning-500) 30%, transparent); }
@media (hover: hover) { .hover\:bg-brand-500\/15:hover { background-color: color-mix(in oklab, var(--color-brand-500) 15%, transparent); } }
@media (hover: hover) { .hover\:text-success-600:hover { color: var(--color-success-600); } }
.bg-black\/70 { background-color: color-mix(in oklab, var(--color-black) 70%, transparent); }
.bg-brand-50\/40 { background-color: color-mix(in oklab, var(--color-brand-50) 40%, transparent); }
.bg-brand-50\/50 { background-color: color-mix(in oklab, var(--color-brand-50) 50%, transparent); }
.bg-brand-500\/20 { background-color: color-mix(in oklab, var(--color-brand-500) 20%, transparent); }
.bg-error-500\/10 { background-color: color-mix(in oklab, var(--color-error-500) 10%, transparent); }
.bg-gray-50\/40 { background-color: color-mix(in oklab, var(--color-gray-50) 40%, transparent); }
.bg-gray-50\/50 { background-color: color-mix(in oklab, var(--color-gray-50) 50%, transparent); }
.bg-gray-50\/60 { background-color: color-mix(in oklab, var(--color-gray-50) 60%, transparent); }
.bg-warning-700 { background-color: var(--color-warning-700); }
.bg-white\/15 { background-color: color-mix(in oklab, var(--color-white) 15%, transparent); }
.border-brand-300 { border-color: var(--color-brand-300); }
.border-error-500\/30 { border-color: color-mix(in oklab, var(--color-error-500) 30%, transparent); }
.dark .dark\:bg-black\/85 { background-color: color-mix(in oklab, var(--color-black) 85%, transparent); }
.dark .dark\:bg-blue-500\/10 { background-color: color-mix(in oklab, var(--color-blue-500) 10%, transparent); }
.dark .dark\:bg-blue-500\/\[0\.08\] { background-color: color-mix(in oklab, var(--color-blue-500) 8%, transparent); }
.dark .dark\:bg-brand-500\/\[0\.08\] { background-color: color-mix(in oklab, var(--color-brand-500) 8%, transparent); }
.dark .dark\:bg-gray-200 { background-color: var(--color-gray-200); }
.dark .dark\:bg-gray-800\/50 { background-color: color-mix(in oklab, var(--color-gray-800) 50%, transparent); }
.dark .dark\:bg-gray-900\/90 { background-color: color-mix(in oklab, var(--color-gray-900) 90%, transparent); }
.dark .dark\:bg-orange-400 { background-color: var(--color-orange-400); }
.dark .dark\:bg-warning-50\/10 { background-color: color-mix(in oklab, var(--color-warning-50) 10%, transparent); }
.dark .dark\:bg-white\/\[0\.01\] { background-color: color-mix(in oklab, var(--color-white) 1%, transparent); }
.dark .dark\:border-brand-500\/40 { border-color: color-mix(in oklab, var(--color-brand-500) 40%, transparent); }
.dark .dark\:border-orange-400 { border-color: var(--color-orange-400); }
@media (hover: hover) { .dark .dark\:hover\:bg-white\/\[0\.10\]:hover { background-color: color-mix(in oklab, var(--color-white) 10%, transparent); } }
@media (hover: hover) { .dark .dark\:hover\:border-brand-500\/40:hover { border-color: color-mix(in oklab, var(--color-brand-500) 40%, transparent); } }
@media (hover: hover) { .dark .dark\:hover\:border-brand-800:hover { border-color: var(--color-brand-800); } }
.dark .dark\:text-gray-900 { color: var(--color-gray-900); }
.dark .dark\:text-success-300 { color: var(--color-success-300); }
.dark .dark\:text-success-500\/80 { color: color-mix(in oklab, var(--color-success-500) 80%, transparent); }
.dark .dark\:text-white\/30 { color: color-mix(in oklab, var(--color-white) 30%, transparent); }
.focus\:ring-error-500\/30:focus { --tw-ring-color: color-mix(in oklab, var(--color-error-500) 30%, transparent); }
.focus\:ring-warning-500:focus { --tw-ring-color: var(--color-warning-500); }
.focus\:ring-warning-500\/10:focus { --tw-ring-color: color-mix(in oklab, var(--color-warning-500) 10%, transparent); }
.focus\:ring-white\/40:focus { --tw-ring-color: color-mix(in oklab, var(--color-white) 40%, transparent); }
.group-hover\:text-gray-400 { color: var(--color-gray-400); }
@media (hover: hover) { .hover\:bg-brand-100:hover { background-color: var(--color-brand-100); } }
@media (hover: hover) { .hover\:bg-brand-400:hover { background-color: var(--color-brand-400); } }
@media (hover: hover) { .hover\:bg-brand-50\/20:hover { background-color: color-mix(in oklab, var(--color-brand-50) 20%, transparent); } }
@media (hover: hover) { .hover\:bg-error-500\/20:hover { background-color: color-mix(in oklab, var(--color-error-500) 20%, transparent); } }
@media (hover: hover) { .hover\:bg-gray-100\/60:hover { background-color: color-mix(in oklab, var(--color-gray-100) 60%, transparent); } }
@media (hover: hover) { .hover\:bg-success-500:hover { background-color: var(--color-success-500); } }
@media (hover: hover) { .hover\:bg-success-700:hover { background-color: var(--color-success-700); } }
@media (hover: hover) { .hover\:bg-white\/20:hover { background-color: color-mix(in oklab, var(--color-white) 20%, transparent); } }
@media (hover: hover) { .hover\:border-error-500:hover { border-color: var(--color-error-500); } }
@media (hover: hover) { .hover\:text-brand-800:hover { color: var(--color-brand-800); } }
.text-blue-light-600 { color: var(--color-blue-light-600); }
.text-success-700\/80 { color: color-mix(in oklab, var(--color-success-700) 80%, transparent); }
.text-success-800 { color: var(--color-success-800); }
.text-warning-700\/70 { color: color-mix(in oklab, var(--color-warning-700) 70%, transparent); }

/* Las dos variantes oscuras del partial `action-button` (kinds `deactivate` y
   `activate`). NO salieron del barrido de `class=` porque ahí las clases viven
   dentro de un ARRAY PHP, no de un atributo — el mismo motivo por el que el
   defecto sobrevivió tanto. */
.dark .dark\:hover\:text-warning-500:hover { color: var(--color-warning-500); }
.dark .dark\:hover\:text-success-500:hover { color: var(--color-success-500); }
