:root {
  --bg: #0f1420;
  --panel: #161d2e;
  --panel-alt: #1d2740;
  --border: #2a3552;
  --text: #e8ecf6;
  --text-dim: #9aa5c0;
  --primary: #5b8cff;
  --primary-dim: #3a5fc4;
  --purple: #a67bf0;
  --radius: 12px;
  --topbar-h: 64px;
  --table-sticky-top-h: 0px;
  --tabs-h: 0px;
  font-size: 15px;

  /* Colores fijos por estado de renovación — se usan siempre para el mismo
     estado en badges, KPI cards y gráficas (doughnut, stacked bar), nunca
     por posición. Deben coincidir con ESTADO_COLORS en js/app.js. */
  --state-perdido: #ef5a6f;       /* rojo */
  --state-sin-gestionar: #f0a93a; /* naranja */
  --state-en-proceso: #5b8cff;    /* azul */
  --state-ganado: #7ee8a8;        /* verde claro */
  --state-renovado: #125b36;      /* verde oscuro (más oscuro que el verde de "Ganado (pend. pago)") */
  /* Alias retrocompatibles: mismo valor, para no reescribir cada uso. */
  --red: var(--state-perdido);
  --amber: var(--state-sin-gestionar);
  --green: var(--state-renovado);
  --green-light: var(--state-ganado);

  /* Colores de gráficas (Chart.js) que no pueden leer variables CSS
     directamente — js/app.js las lee vía getComputedStyle(). */
  --chart-grid: rgba(42, 53, 82, .33);
  --chart-label: #c9d2ea;
}

/* Tema claro: se activa poniendo data-theme="light" en <html> (ver script
   inline en el <head> de index.html/logins.html, que lo aplica ANTES de
   pintar para evitar el parpadeo del tema oscuro por defecto). El oscuro
   sigue siendo el tema principal/por defecto — este bloque solo sobreescribe
   lo necesario. Solo el verde claro de "Ganado (pend. pago)" cambia de tono
   (más oscuro, por contraste sobre fondo claro); el resto de --state-* se
   dejan igual a propósito. Debe coincidir con ESTADO_COLORS_LIGHT en
   js/app.js. */
html[data-theme="light"] {
  --bg: #f2f4f9;
  --panel: #ffffff;
  --panel-alt: #eef1f8;
  --border: #d9deea;
  --text: #1b2436;
  --text-dim: #5c6580;

  --state-ganado: #4a9d3a; /* verde más oscuro que el de tema oscuro (#7ee8a8) */

  --chart-grid: rgba(92, 101, 128, .18);
  --chart-label: #2a3350;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto, sans-serif;
}

.hidden { display: none !important; }

/* Topbar */
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 28px;
  border-bottom: 1px solid var(--border);
  background: var(--panel);
  position: sticky;
  top: 0;
  z-index: 20;
  flex-wrap: wrap;
  gap: 12px;
}
.topbar-left { display: flex; align-items: baseline; gap: 14px; }
.topbar-logo { height: 22px; width: auto; align-self: center; }
/* Dos logos superpuestos (dark/light), el CSS decide cuál mostrar según
   data-theme — así el logo correcto aparece desde el primer pintado, sin
   esperar a que app.js corra. */
.topbar-logo-light { display: none; }
html[data-theme="light"] .topbar-logo-dark { display: none; }
html[data-theme="light"] .topbar-logo-light { display: inline-block; }

/* Botón de cambio de tema: dos íconos superpuestos, el CSS decide cuál
   mostrar según data-theme (mismo mecanismo que los logos de arriba). En
   oscuro se ve el sol (invita a pasar a claro); en claro, la luna. */
#btnTheme .icon-theme-moon { display: none; }
html[data-theme="light"] #btnTheme .icon-theme-sun { display: none; }
html[data-theme="light"] #btnTheme .icon-theme-moon { display: block; }
.topbar-left h1 { font-size: 19px; margin: 0; font-weight: 700; }
.data-meta { font-size: 13px; color: var(--text-dim); display: inline-flex; align-items: center; gap: 6px; }
/* flex-wrap: con los íconos de Superadmin ya no caben en una línea en
   pantallas angostas (el de cerrar sesión se salía del borde). */
.topbar-right { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
/* Switch global Bookings / Cantidad comercios, en el centro del header. En
   pantallas anchas el topbar pasa a grid de 3 columnas (1fr auto 1fr) para
   que quede centrado de verdad aunque izquierda y derecha midan distinto;
   en angostas vuelve al flex con wrap de arriba y el switch simplemente
   baja a su propia línea. */
.topbar-center { display: flex; justify-content: center; align-items: center; gap: 10px; }
.topbar-center .mode-toggle button { padding: 7px 16px; white-space: nowrap; }
@media (min-width: 1280px) {
  .topbar { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); }
  /* grid-column explícito: accesos.html comparte este topbar pero no tiene
     el bloque central, y sin esto su .topbar-right caería en la columna 2. */
  .topbar-right { justify-content: flex-end; grid-column: 3; }
}

/* Botones de solo ícono (historial, cerrar sesión) con tooltip propio en
   vez del title nativo del navegador (más lento y menos consistente con
   el tema oscuro). El texto sale de data-tooltip. */
.icon-btn {
  width: 34px;
  height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  background: var(--panel-alt);
  color: var(--text-dim);
  border-radius: 8px;
  cursor: pointer;
  text-decoration: none;
  flex-shrink: 0;
}
.icon-btn:hover { border-color: var(--primary); color: var(--text); }
/* Ícono de la página en la que se está (Accesos / Actualizaciones). */
.icon-btn.is-current { border-color: var(--primary); color: var(--primary); }
[data-tooltip] { position: relative; }
[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  background: #0b0f18;
  color: #e8ecf6; /* fijo (no var(--text)): el fondo del tooltip siempre es oscuro, en ambos temas */
  border: 1px solid var(--border);
  padding: 5px 9px;
  border-radius: 6px;
  font-size: 11.5px;
  white-space: nowrap;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity .12s ease;
  z-index: 50;
}
[data-tooltip]:hover::after, [data-tooltip]:focus-visible::after { opacity: 1; visibility: visible; }

.btn {
  border: 1px solid var(--border);
  background: var(--panel-alt);
  color: var(--text);
  padding: 9px 16px;
  border-radius: 8px;
  font-size: 13.5px;
  cursor: pointer;
  transition: all .15s ease;
  white-space: nowrap;
}
.btn:hover:not(:disabled) { border-color: var(--primary); }
.btn:disabled { opacity: .4; cursor: not-allowed; }
.btn-primary { background: var(--primary); border-color: var(--primary); color: #fff; font-weight: 600; }
.btn-primary:hover { background: var(--primary-dim); }
.btn-secondary { background: transparent; }
.btn-ghost { background: transparent; border-color: transparent; color: var(--text-dim); text-decoration: underline; }

/* Splash de carga / sin datos */
.splash { display: flex; align-items: center; justify-content: center; min-height: 70vh; text-align: center; padding: 30px; }
.splash.hidden { display: none; }
.splash-inner { display: flex; flex-direction: column; align-items: center; gap: 26px; }
.splash-logo { height: 44px; width: auto; }
.splash-text { font-size: 19px; color: var(--text-dim); margin: 0; max-width: 640px; line-height: 1.5; }
.splash-spinner {
  width: 34px; height: 34px; border-radius: 50%;
  border: 3px solid var(--border); border-top-color: var(--primary);
  animation: splash-spin .8s linear infinite;
}
.splash.splash-leaving { position: fixed; inset: 0; z-index: 1000; background: var(--bg); animation: splash-fade-out .5s ease forwards; pointer-events: none; }
@keyframes splash-fade-out { to { opacity: 0; } }
.splash-leaving .splash-logo { animation: splash-logo-out .5s cubic-bezier(.4, 0, 1, 1) forwards; }
.splash-leaving .splash-spinner, .splash-leaving .splash-text { animation: splash-fade-out .2s ease forwards; }
@keyframes splash-logo-out { to { transform: scale(5); opacity: 0; } }
@keyframes splash-fade-in { from { opacity: 0; } to { opacity: 1; } }
#noData:not(.hidden), #dashboard:not(.hidden) { animation: splash-fade-in .6s ease; }
@keyframes splash-spin { to { transform: rotate(360deg); } }

/* Dashboard layout */
.dashboard { padding: 22px 28px 60px; }

.kpi-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 14px;
  margin-bottom: 22px;
}
.kpi-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px 18px;
}
.kpi-card .kpi-label { font-size: 12px; color: var(--text-dim); text-transform: uppercase; letter-spacing: .04em; margin-bottom: 8px; }
.kpi-card .kpi-value { font-size: 24px; font-weight: 700; line-height: 1.1; }
.kpi-card .kpi-sub { font-size: 12.5px; color: var(--text-dim); margin-top: 6px; }
.kpi-card.accent-green .kpi-value { color: var(--green); }
.kpi-card.accent-red .kpi-value { color: var(--red); }
.kpi-card.accent-amber .kpi-value { color: var(--amber); }
.kpi-card.accent-purple .kpi-value { color: var(--purple); }
.kpi-card.accent-primary .kpi-value { color: var(--primary); }

/* Filters: la caja (".filters") es solo el marco (borde/fondo/padding); el
   acomodo real vive en ".filters-row" (una por línea), para poder controlar
   exactamente qué va en cada línea y alinear "Limpiar filtros" al final de
   la última — algo que un grid con auto-wrap no permite controlar bien. */
.filters {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px 18px;
  margin-bottom: 22px;
}
.filter-group { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.filter-group label { font-size: 11.5px; color: var(--text-dim); text-transform: uppercase; letter-spacing: .03em; }
.filter-group input, .filter-group select {
  background: var(--panel-alt);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: 7px;
  padding: 7px 8px;
  font-size: 13px;
  width: 100%;
}
.filter-group select[multiple] { min-height: 62px; }

.filters-row { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px 16px; }
.filters-row .filter-group { flex: 1 1 160px; min-width: 150px; }
.filters-row + .filters-row { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border); }

/* Última línea: los filtros a la izquierda, "Limpiar filtros" + el conteo
   pegados a la derecha (no como celdas más del mismo ancho que las demás). */
.filters-row-end { justify-content: space-between; }
.filters-row-fields { display: flex; flex-wrap: wrap; gap: 12px 16px; flex: 1 1 auto; }
.filters-row-fields .filter-group { flex: 1 1 160px; min-width: 150px; }

.filter-actions { display: flex; align-items: center; gap: 12px; flex: 0 0 auto; white-space: nowrap; }
.filter-count { font-size: 12.5px; color: var(--text-dim); white-space: nowrap; }

/* Rango min/max */
.range-inputs { display: flex; align-items: center; gap: 6px; }
.range-inputs input { min-width: 0; }
.range-inputs span { color: var(--text-dim); font-size: 12px; }

/* Select "limpiable": envuelve el <select> y agrega una X que aparece
   cuando hay algo seleccionado. Se aplica vía JS (makeClearable). */
.clearable-select { position: relative; display: flex; }
.clearable-select select { padding-right: 26px; }
.clearable-select .clear-btn {
  position: absolute;
  top: 50%;
  right: 6px;
  transform: translateY(-50%);
  width: 18px;
  height: 18px;
  border: none;
  background: var(--border);
  color: var(--text-dim);
  border-radius: 50%;
  font-size: 12px;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}
.clearable-select .clear-btn:hover { background: var(--red); color: #fff; }
.clearable-select select[multiple] + .clear-btn { top: 12px; transform: none; }

/* Filtro más ancho dentro de una fila de filtros (ej. buscador de la tabla) */
.filter-group-wide { flex: 2 1 320px; }

/* Bandas sticky: título de overview, título+tabs de meses, título+filtros+
   encabezado de tabla. Todas se pegan justo debajo del topbar (que también
   es sticky) usando --topbar-h, calculado en JS con el alto real del topbar
   (evita depender de un valor fijo si cambia el contenido del topbar). */
.sticky-band {
  position: sticky;
  top: var(--topbar-h);
  z-index: 15;
  background: var(--panel);
}

/* Los títulos de los 3 informes que mandan las tabs de mes (detallado,
   facturación vs último uso, tabla) se pegan DEBAJO de las tabs, que a su vez
   están debajo del topbar. --tabs-h se calcula en JS (updateStickyOffsets). */
.sticky-band-sub { top: calc(var(--topbar-h) + var(--tabs-h)); }
/* Tabs de mes: sticky siempre (hijo directo de <main>), por encima de los
   títulos que se pegan debajo de ellas. */
.month-tabs-bar {
  z-index: 16;
  padding: 10px 18px 12px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 18px;
}

/* Switch dinero/cantidad de las KPIs */
.mode-toggle { display: flex; border: 1px solid var(--border); border-radius: 7px; overflow: hidden; }
.mode-toggle button {
  flex: 1;
  border: none;
  background: var(--panel-alt);
  color: var(--text-dim);
  padding: 7px 10px;
  font-size: 12.5px;
  cursor: pointer;
}
.mode-toggle button + button { border-left: 1px solid var(--border); }
.mode-toggle button.active { background: var(--primary); color: #fff; font-weight: 600; }

/* Multi-select con checkboxes: el <select multiple> queda oculto como fuente
   de verdad; el botón abre un modal (#msModalOverlay) con la lista de
   opciones, más estilizado y sin problemas de recorte que un panel flotante. */
.ms-native-hidden { display: none !important; }
.ms-dropdown { position: relative; }
.ms-toggle {
  width: 100%;
  text-align: left;
  background: var(--panel-alt);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: 7px;
  padding: 7px 40px 7px 8px;
  font-size: 13px;
  cursor: pointer;
  position: relative;
}
.ms-toggle::after {
  content: '▾';
  position: absolute;
  right: 8px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-dim);
  font-size: 10px;
}
.ms-toggle-label { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ms-clear {
  position: absolute;
  right: 22px;
  top: 50%;
  transform: translateY(-50%);
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--border);
  color: var(--text-dim);
  font-size: 12px;
  line-height: 18px;
  text-align: center;
  cursor: pointer;
}
.ms-clear:hover { background: var(--red); color: #fff; }

/* Más angosto que el .modal base (640px) — esta lista es de opciones
   cortas (nombres, estados), no necesita todo ese ancho. Selector de dos
   clases a propósito: ".modal" se define más abajo en el archivo con la
   misma especificidad que ".ms-modal" sola, y por orden de aparición
   ganaría igual — ".modal.ms-modal" sube la especificidad para que esto
   no dependa del orden entre las dos reglas. */
.modal.ms-modal { max-width: 380px; }
.ms-modal .ms-modal-list {
  max-height: 46vh;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 4px 0 18px;
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 6px;
}
.ms-modal-option {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 10px;
  border-radius: 6px;
  font-size: 13.5px;
  cursor: pointer;
}
.ms-modal-option:hover { background: var(--panel-alt); }
/* "Seleccionar todos" va fuera de "#msModalList" (ver app.js — así
   applyMsModal()/clearMsModal() no la confunden con una opción real), pero
   reutiliza el mismo look de fila; el borde inferior + margen la separan
   visualmente de la lista de abajo en vez de que se lean como una opción más. */
.ms-modal-select-all {
  font-weight: 600;
  border-bottom: 1px solid var(--border);
  margin: 4px 0 8px;
  padding-bottom: 12px;
}
.ms-modal-option input {
  width: 17px;
  height: 17px;
  accent-color: var(--primary);
  margin: 0;
  flex-shrink: 0;
}
.ms-modal-actions { display: flex; justify-content: flex-end; gap: 10px; }

/* Ícono "Perdido"/"Ganado" que reemplaza el pipeline crudo cuando el estado
   consolidado ya resolvió el resultado (ver pipelineConsolidado en app.js) */
.pipeline-lost { display: inline-flex; align-items: center; gap: 4px; color: var(--red); font-weight: 600; }
.pipeline-won { display: inline-flex; align-items: center; gap: 4px; color: var(--green); font-weight: 600; }
/* "Ganado (pend. pago)" en la columna Pipeline: mismo verde claro fijo del
   estado (sin ícono de check, que queda reservado para "Renovado"). */
.pipeline-ganado { color: var(--green-light); font-weight: 600; }

/* Ícono "i" de ayuda, chico, junto a cada dato individual (ver .timing-stat-head) */
.info-icon-mini {
  width: 15px;
  height: 15px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--panel-alt);
  color: var(--text-dim);
  font-size: 10px;
  font-style: italic;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  flex-shrink: 0;
  padding: 0;
}
.info-icon-mini:hover { border-color: var(--primary); color: var(--primary); }
.info-modal-body p { margin: 0 0 14px; font-size: 13.5px; line-height: 1.6; }
.info-modal-body p:last-child { margin-bottom: 0; }

/* Tabla de cumplimiento por etapa (modal por mes de la gráfica overview,
   ver openMonthDetailModal() en app.js). Sin <thead>/<tbody> a propósito
   (ver el comentario en openMonthDetailModal()) — la fila de encabezado se
   distingue con esta clase, no con una etiqueta de grupo de filas. */
.pct-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.pct-table th, .pct-table td { padding: 9px 10px; text-align: center; border-bottom: 1px solid var(--border); }
.pct-table .pct-table-head th { color: var(--text-dim); font-weight: 600; font-size: 12px; }
.pct-table tr:not(.pct-table-head) th { text-align: left; font-weight: 600; white-space: nowrap; }
.pct-table tr:last-child th, .pct-table tr:last-child td { border-bottom: none; }

/* Overview: resultados históricos y proyectados (ancho completo) */
.chart-overview {
  margin-bottom: 18px;
  min-height: 0;
}
.chart-overview-sticky {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: flex-end;
  gap: 14px;
  padding: 10px 0 14px;
  margin: -10px 0 0;
}
/* Selector con más especificidad que ".chart-card h3" (13.5px, tenue) para
   que este título quede del mismo tamaño que "Resultados mensuales"
   (.month-sticky h3, sin override propio => tamaño/peso/color por defecto
   del h3 del navegador) en vez del tamaño reducido que .chart-card da a los
   títulos de gráfica individuales. */
.chart-overview .chart-overview-sticky h3 { margin: 0; font-size: 1.17em; font-weight: 700; color: var(--text); }
.chart-overview-controls { display: flex; gap: 12px; flex-wrap: wrap; }
.chart-overview-controls .filter-group { min-width: 180px; }
/* Filtros de la gráfica de facturación: NO van en la banda sticky (solo el título se pega bajo las tabs), así que llevan su propio respiro. */
.fu-controls { align-items: flex-end; margin-bottom: 12px; }
.chart-overview-canvas-wrap { height: 320px; position: relative; }
.chart-overview-canvas-wrap canvas { width: 100% !important; height: 100% !important; }

/* Aclaración inline en "Resultados de {mes}" (#monthSectionTitle, ver
   applyMainFilters()) — más chica y tenue que el título, no un h3 aparte. */
.month-section-title-sub { font-size: 0.6em; font-weight: 400; color: var(--text-dim); margin-left: 6px; }

/* Resultados mensuales: título + tabs de mes, sticky mientras se hace
   scroll por los filtros/KPIs/gráficas de esta misma sección.
   A diferencia de ".chart-overview-sticky" (que hereda el padding
   horizontal de ".chart-card") ".month-section" no tiene su propia caja
   con padding, así que sin este padding acá el título/tabs/toggle quedan
   pegados al borde de la sección — sin el respiro de 18px que sí tienen
   ".filters"/".chart-card" justo debajo. El padding (no margin) deja el
   fondo del sticky-band a todo el ancho igual que antes. */
.month-sticky {
  padding: 10px 18px 14px;
  margin: -10px 0 22px;
}
.month-sticky h3 { margin: 0 0 10px; }
.month-controls-row { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 20px; }
.month-select-group { flex: 1 1 auto; min-width: 0; }
.month-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.month-tabs button {
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text-dim);
  padding: 8px 14px;
  border-radius: 8px;
  font-size: 13px;
  cursor: pointer;
  white-space: nowrap;
}
.month-tabs button:hover { border-color: var(--primary); color: var(--text); }
.month-tabs button.active {
  background: var(--primary);
  border-color: var(--primary);
  color: #fff;
  font-weight: 600;
}

/* Charts */
.charts-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
  gap: 14px;
  margin-bottom: 22px;
}
.chart-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px 18px;
  min-height: 300px;
  display: flex;
  flex-direction: column;
}
.chart-card.chart-wide { grid-column: span 2; }
/* Fuerza que esta tarjeta ("Valor por renovar vs. renovado") empiece
   siempre una fila nueva del grid, para que la primera fila tenga como
   máximo las 2 primeras tarjetas (la ancha "Renovado por día" + "Casos
   especiales") — en pantallas anchas con 3+ columnas, sin esto el
   auto-placement metía esta tarjeta de tercera en la misma fila. No afecta
   cuántas tarjetas siguen después en su misma fila (esas se acomodan solas). */
.chart-card.chart-newrow { grid-column-start: 1; }
.chart-card h3 { font-size: 13.5px; margin: 0 0 12px; color: var(--text-dim); font-weight: 600; }
.chart-card canvas { flex: 1; max-height: 280px; }

/* "Vencimientos": dos grupos (vencía este mes / vencía en otro mes), cada
   uno con 2 datos — números en vez de una gráfica. */
.timing-group { flex: 1; display: flex; flex-direction: column; justify-content: center; }
.timing-group + .timing-group { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border); }
.timing-group-title { font-size: 11.5px; color: var(--text-dim); text-transform: uppercase; letter-spacing: .03em; margin-bottom: 10px; }
.timing-stats { display: flex; justify-content: space-around; gap: 14px; }
.timing-stat { text-align: center; }
.timing-stat-head { display: flex; align-items: center; justify-content: center; gap: 6px; }
.timing-stat-label { font-size: 12.5px; color: var(--text-dim); }
.timing-stat-value { font-size: 26px; font-weight: 700; line-height: 1.1; color: var(--green); margin-top: 4px; }

/* Tasa de renovación por agente: lista (nombre + %), no barras — se pidió
   que ambos se vean grandes, cosa que una gráfica de barras no permite
   bien con muchos agentes. */
.agent-rate-list { flex: 1; overflow-y: auto; }
.agent-rate-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 11px 2px;
  border-bottom: 1px solid var(--border);
}
.agent-rate-row:last-child { border-bottom: none; }
.agent-rate-name { font-size: 15px; }
.agent-rate-value { font-size: 20px; font-weight: 700; color: var(--green); }

/* Table */
.table-section {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px 18px 8px;
}
/* padding-bottom (no el margin del header, que se colapsaba hacia afuera de la banda sticky): mismo respiro bajo el título que las otras bandas. */
.table-sticky-top { padding: 4px 0 14px; }
.table-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0; }
/* Mismo tamaño/peso/color que los otros títulos de sección ("Resultados
   generales"/"Resultados mensuales" — ver ".chart-overview-sticky h3" más
   arriba) en vez del tamaño reducido que traía antes. */
.table-header h3 { margin: 0; font-size: 1.17em; font-weight: 700; color: var(--text); }
/* Scroll de la tabla: SOLO horizontal y propio. Si la tabla es más ancha que
   la pantalla, se desplaza ella sola (la página no se mueve de lado); el
   scroll vertical sigue siendo el de la página completa, sin caja con scroll
   vertical propio.

   Ojo con el header pegajoso: cualquier "overflow-x" distinto de visible
   convierte la caja en contenedor de scroll también en Y (no existe la
   combinación "auto en X + visible en Y"; "clip" también pasa a "hidden"), y
   un <thead> sticky dentro de ella se pegaría contra esa caja y no contra la
   página. Por eso el header pegajoso NO es el <thead> de la tabla: es una
   COPIA (#tableHeadSticky, ver syncStickyTableHeader() en app.js) que vive
   afuera de .table-wrap, así su sticky sí es contra la página. El <thead>
   original queda invisible (pero presente: define los anchos de columna, y
   la copia lo replica) y la copia se le superpone con margin-bottom
   negativo. El scroll horizontal de la caja se refleja en la copia por JS. */
.table-wrap {
  overflow-x: auto;
  /* Barras con los colores del tema (las nativas salen blancas en oscuro). */
  scrollbar-color: var(--text-dim) var(--panel-alt);
}
.table-head-sticky {
  position: sticky;
  top: calc(var(--topbar-h) + var(--tabs-h) + var(--table-sticky-top-h));
  z-index: 14;
  overflow: hidden; /* sin barra propia: su scrollLeft lo maneja el JS */
  background: var(--panel);
}
.table-head-clone { table-layout: fixed; width: auto; }
#dataTable thead { visibility: hidden; }
table { width: 100%; border-collapse: collapse; font-size: 13px; }
thead th {
  text-align: left;
  padding: 9px 10px;
  color: var(--text-dim);
  border-bottom: 1px solid var(--border);
  cursor: pointer;
  white-space: nowrap;
  user-select: none;
  background: var(--panel);
}
thead th:hover { color: var(--text); }
thead th.sorted-asc::after { content: " ▲"; font-size: 10px; }
thead th.sorted-desc::after { content: " ▼"; font-size: 10px; }
tbody td { padding: 9px 10px; border-bottom: 1px solid var(--border); white-space: nowrap; }
tbody tr { cursor: pointer; transition: background .1s ease; }
/* Nombre del comercio: recortado con "..." si es muy largo, para dejarle
   espacio a la columna "Vendedor" sin ensanchar toda la tabla (la tabla no
   usa table-layout: fixed, así que el límite va en un span inline-block
   dentro de la celda, no en el <td> directamente). */
.td-truncate { display: inline-block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }
.td-comercio { max-width: 200px; }
tbody tr:hover { background: var(--panel-alt); }

.badge { padding: 3px 9px; border-radius: 999px; font-size: 11.5px; font-weight: 600; display: inline-block; }
.badge-renovado { background: rgba(18,91,54,.18); color: var(--state-renovado); }
.badge-perdido { background: rgba(239,90,111,.15); color: var(--state-perdido); }
.badge-proceso { background: rgba(91,140,255,.15); color: var(--state-en-proceso); }
.badge-ganado { background: rgba(126,232,168,.18); color: var(--state-ganado); }
.badge-sin { background: rgba(240,169,58,.15); color: var(--state-sin-gestionar); }

.table-pagination { display: flex; justify-content: center; gap: 6px; padding: 14px 0; flex-wrap: wrap; }
.table-pagination button {
  border: 1px solid var(--border);
  background: var(--panel-alt);
  color: var(--text-dim);
  padding: 5px 11px;
  border-radius: 6px;
  cursor: pointer;
  font-size: 12.5px;
}
.table-pagination button.active { background: var(--primary); color: #fff; border-color: var(--primary); }
.table-pagination button:hover:not(.active) { color: var(--text); }

/* Modal */
.modal-overlay {
  position: fixed; inset: 0; background: rgba(5,8,16,.65);
  display: flex; align-items: center; justify-content: center;
  z-index: 100; padding: 20px;
}
.modal {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  max-width: 640px;
  width: 100%;
  max-height: 85vh;
  overflow-y: auto;
  padding: 26px 28px;
  position: relative;
}
.modal-close {
  position: absolute; top: 14px; right: 16px;
  background: none; border: none; color: var(--text-dim);
  font-size: 22px; cursor: pointer; line-height: 1;
}
.modal-close:hover { color: var(--text); }
.modal h2 { margin: 0 0 18px; font-size: 18px; }
/* Modal de detalle de cliente: el único con encabezado sticky (título +
   resumen), pegado arriba mientras se hace scroll por el resto del modal.
   En vez del truco de "wrapper con margen negativo" que se usaba antes para
   que el fondo del header llegara borde a borde (compensando el padding de
   ".modal"), ".modal-detail" directamente no tiene padding propio y lo
   reparte entre ".modal-sticky-header" (arriba) y ".modal-body" (el resto)
   — así el header es HIJO DIRECTO de ".modal" (el contenedor con scroll),
   sin ningún wrapper de por medio.
   Esto no es cosmético: un wrapper (aunque no tenga ninguna propiedad CSS
   propia — ni margin, ni position) entre el elemento "sticky" y su
   ancestro con scroll le impide quedar pegado (se probó exhaustivamente:
   falla igual con el wrapper en position static/relative, sin margen, sin
   nada — solo se arregla quitándolo o poniéndole "display: contents"). El
   margen negativo directo en ".modal-sticky-header" tampoco es opción:
   "position: sticky" en un elemento con margen negativo en el eje que
   tiene "top" activo hace que ese margen no se tenga en cuenta al calcular
   su posición de reposo, dejando un hueco por el que se ve el contenido
   detrás. Si esto necesita tocarse de nuevo, mantener el padding repartido
   sin wrapper — no reintroducir ninguna de las dos variantes anteriores. */
.modal-detail { padding: 0; }
.modal-detail .modal-body { padding: 0 28px 26px; }
.modal-sticky-header {
  position: sticky;
  top: 0;
  z-index: 2;
  padding: 26px 28px 14px;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
}
.modal-sticky-header h2 { margin: 0 0 10px; padding-right: 26px; }
.modal-sticky-facts {
  display: flex;
  flex-wrap: wrap;
  column-gap: 22px;
  row-gap: 4px;
  margin: 0;
}
.modal-sticky-facts:empty { display: none; }
.modal-sticky-facts dt { color: var(--text-dim); font-size: 11px; }
.modal-sticky-facts dt::after { content: ':'; }
.modal-sticky-facts dd { margin: 0; font-size: 12.5px; font-weight: 600; display: inline-flex; align-items: center; gap: 4px; }
.logout-modal p { margin: 0 0 20px; font-size: 14px; color: var(--text-dim); }
.modal.superadmin-modal { max-width: 360px; }
.superadmin-modal form { display: flex; flex-direction: column; gap: 14px; }
.superadmin-modal p.superadmin-error { margin: 0; font-size: 13px; color: var(--red); }
.superadmin-modal .ms-modal-actions { margin-top: 4px; }
.modal-body dl { display: grid; grid-template-columns: 160px 1fr; gap: 9px 14px; margin: 0; }
.modal-body dt { color: var(--text-dim); font-size: 12.5px; }
.modal-body dd { margin: 0; font-size: 13.5px; }
.modal-section-title {
  grid-column: 1 / -1;
  font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--primary); border-top: 1px solid var(--border);
  padding-top: 12px; margin-top: 6px;
}

@media (max-width: 720px) {
  .chart-card.chart-wide { grid-column: span 1; }
  .topbar { flex-direction: column; align-items: flex-start; }
}


/* ---- Filtros globales: botón del header + panel lateral ------------------
   El botón va junto al switch Bookings/Cantidad (.topbar-center). El panel
   es un "drawer" a la derecha dentro de un .modal-overlay (z-index 100); el
   modal compartido de multi-select (#msModalOverlay) se abre ENCIMA, por eso
   sube a 110. */
.filters-btn {
  display: inline-flex; align-items: center; gap: 6px;
  /* Discreto a propósito: sin relleno ni borde marcado y texto tenue, para
     que el switch Bookings/Cantidad siga siendo lo que más se ve. */
  border: 1px solid transparent; background: transparent;
  color: var(--text-dim); text-decoration: none; padding: 6px 10px; border-radius: 7px;
  font-size: 12px; white-space: nowrap;
}
.filters-btn:hover { color: var(--text); border-color: var(--border); }
.filters-badge {
  min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px;
  background: transparent; color: var(--primary); border: 1px solid var(--primary); font-size: 11px; font-weight: 700;
  display: inline-flex; align-items: center; justify-content: center;
}
#msModalOverlay { z-index: 110; }
.filters-drawer-overlay { justify-content: flex-end; align-items: stretch; padding: 0; }
.filters-drawer {
  background: var(--panel); border-left: 1px solid var(--border);
  width: min(460px, 100%); height: 100%;
  display: flex; flex-direction: column;
}
.filters-drawer-head { position: relative; padding: 18px 56px 12px 22px; border-bottom: 1px solid var(--border); }
.filters-drawer-head h2 { margin: 0; font-size: 18px; }
.filters-drawer-body {
  flex: 1 1 auto; overflow-y: auto; padding: 18px 22px;
  display: grid; grid-template-columns: 1fr 1fr; gap: 16px 14px; align-content: start;
}
.filters-drawer-body .filter-group-range { grid-column: 1 / -1; }
.filters-drawer-foot {
  display: flex; align-items: center; gap: 14px;
  padding: 14px 22px; border-top: 1px solid var(--border);
}
/* Tabla: solo queda la búsqueda propia (el resto de filtros es global). */
.filters.table-search { margin-bottom: 14px; }
.filters.table-search .filter-group-wide { flex: 0 1 320px; }
