/*
  Inspector Piecitos — hoja de estilos principal
  Fase 3

  TEMPORAL_FASE_0: paleta y estilos neutros, provisionales — igual que
  desde Fase 0. NO representan la identidad visual definitiva del
  producto (esa llega en Fase 11, con logo/colores reales extraídos del
  Excel en docs/phase_01/08_IDENTIDAD_VISUAL.md).

  Los tokens de :root se mantienen estables entre fases para que Fase 12
  pueda incorporar dark mode sin reescribir el layout.
*/

:root {
  /* Fase 11.1 — remapeadas a la identidad real Dr. Scholl's/Inspector
     Piecitos definida en Fase 11.0 (app/css/design-system.css,
     cargado antes de este archivo). Se conservan los NOMBRES de
     token ya usados por cada selector de este archivo (cientos de
     usos) — sólo se REASIGNAN aquí los que tienen un nombre distinto
     en design-system.css; los que comparten nombre exacto (--bg-elevated,
     --text-primary/secondary/inverse, --success/warning/danger(-soft),
     --radius-sm/md/lg) NO se repiten aquí a propósito — redeclararlos
     con `var(--mismo-nombre)` sería una autorreferencia inválida en
     CSS; al omitirlos, el valor real ya definido en design-system.css
     simplemente seguía vigente en cascada, sin ningún cambio de
     comportamiento. Esto reskinea toda la app (botones, inputs, shell,
     badges, modales) sin tocar selector por selector ni módulo por
     módulo. */
  --bg-primary: var(--bg-surface);
  --bg-secondary: var(--bg-app);
  --border: var(--border-subtle);
  --accent: var(--brand-primary);
  --accent-hover: var(--brand-primary-hover);
  --accent-soft: rgba(32, 41, 75, 0.08);
  /* Fase 11.4B — `--accent` (navy) se usa tanto como FONDO SÓLIDO
     (.btn--primary — debe seguir navy siempre, instrucción 2/3) como
     color de TEXTO/BORDE de énfasis (hover de .btn--ghost, foco de
     inputs, enlaces, avatares…) sobre superficies que en dark SÍ
     cambian — navy-sobre-navy-oscuro es invisible ahí. `--accent-text`
     separa ese segundo uso: en light es idéntico a `--accent` (mismo
     resultado visual, cero cambio aprobado); en dark pasa a
     `--brand-accent` (dorado), el mismo acento ya usado para
     estados activos/énfasis en toda la app. */
  --accent-text: var(--brand-primary);

  --space-1: var(--space-2xs);
  --space-2: var(--space-xs);
  --space-3: var(--space-md);
  --space-4: var(--space-lg);
  --space-5: 40px;

  --font-base: var(--font-family-base);

  --header-height: 64px;
  --footer-height: 44px;
  --sidebar-width: 248px;
  --sidebar-width-collapsed: 72px;

  --transition-fast: var(--motion-fast) var(--ease-standard);
  --transition-base: var(--motion-normal) var(--ease-standard);
  --focus-ring: 0 0 0 3px var(--accent-soft), 0 0 0 1px var(--accent);
}

/* PENDIENTE_FASE_12: dark mode real. Placeholder de intención, sin activar. */
/*
@media (prefers-color-scheme: dark) {
  :root {
    --bg-primary: #14161a;
    ...
  }
}
*/

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
}

body {
  font-family: var(--font-base);
  background: var(--bg-secondary);
  color: var(--text-primary);
  min-height: 100vh;
}

a {
  color: var(--accent-text);
}

button {
  font-family: inherit;
}

:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: var(--radius-sm);
}

/* Ajuste Fase 12A, instrucción 9 — utilidad estándar "visualmente oculto,
   presente para lectores de pantalla" (técnica WCAG habitual, no una
   librería). Para encabezados de columna sin texto visible (ej. la
   columna de foto en tablas) u otro texto que sólo necesita anunciarse
   por voz. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- Skip link (accesibilidad por teclado) ---------- */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--accent);
  color: var(--text-inverse);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
}

.skip-link:focus {
  left: var(--space-2);
  top: var(--space-2);
}

/* ---------- Botones ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  padding: 0.6rem 1.1rem;
  font-size: 0.88rem;
  font-weight: 700;
  cursor: pointer;
  transition: background-color var(--transition-fast), border-color var(--transition-fast), transform var(--transition-fast), box-shadow var(--transition-fast);
}

.btn:active {
  transform: translateY(1px) scale(0.98);
}

.btn:disabled {
  opacity: 0.6;
  cursor: not-allowed;
  transform: none;
}

.btn--primary {
  background: var(--accent);
  color: var(--text-inverse);
  box-shadow: 0 4px 12px rgba(32, 41, 75, 0.28);
}

.btn--primary:hover:not(:disabled) {
  background: var(--accent-hover);
  transform: translateY(-1px);
  box-shadow: 0 6px 16px rgba(32, 41, 75, 0.34);
}

/* Fase 11.2.4, instrucción 14-18 — nuevo tercer nivel entre Primary
   (navy sólido) y Ghost (neutro): azul muy claro + texto/borde navy,
   para acciones relevantes que no deben competir con la acción
   principal de la pantalla (p.ej. "Consultar", "Ver", "Verificar
   ahora") — nunca reemplaza a Ghost por completo, sólo da una tercera
   opción de énfasis controlado. */
.btn--secondary {
  background: var(--accent-soft);
  border-color: rgba(32, 41, 75, 0.16);
  color: var(--brand-primary);
}

.btn--secondary:hover:not(:disabled) {
  background: #dde3f0;
  border-color: var(--brand-primary);
}

/* Fase 11.4B — dark: `--accent-soft`/`--brand-primary` (fondo/texto
   navy claros sobre blanco en light) se invierten a un tinte casi
   invisible + texto navy oscuro sobre una superficie ya oscura —
   texto ilegible por definición (instrucción 6/12). El botón pasa a
   texto/borde en `--text-primary` (claro) sobre el mismo tinte suave. */
:root[data-theme="dark"] .btn--secondary {
  border-color: var(--border-strong);
  color: var(--text-primary);
}

:root[data-theme="dark"] .btn--secondary:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.14);
  border-color: var(--accent-text);
  color: var(--text-primary);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .btn--secondary {
    border-color: var(--border-strong);
    color: var(--text-primary);
  }

  :root:not([data-theme="light"]) .btn--secondary:hover:not(:disabled) {
    background: rgba(255, 255, 255, 0.14);
    border-color: var(--accent-text);
    color: var(--text-primary);
  }
}

/* Secundario (Fase 11.2 — instrucción 24: Primary/Secondary/Danger deben
   distinguirse claramente; .btn--ghost es el botón "Secondary" ya usado
   en toda la app — Cancelar/Reintentar/Editar — sin cambiar su clase). */
.btn--ghost {
  background: var(--bg-app);
  border-color: var(--border-strong);
  color: var(--text-secondary);
}

.btn--ghost:hover:not(:disabled) {
  background: var(--bg-secondary);
  border-color: var(--accent-text);
  color: var(--accent-text);
}

.btn--block {
  width: 100%;
}

/* ---------- Formularios ---------- */
.form-field {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin-bottom: var(--space-3);
  text-align: left;
}

.form-field label {
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.form-field input {
  font: inherit;
  padding: 0.65rem var(--space-3);
  border: 1.5px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-primary);
  color: var(--text-primary);
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.form-field input:hover {
  border-color: var(--text-secondary);
}

.form-field input:focus-visible {
  border-color: var(--accent-text);
  box-shadow: 0 0 0 3px var(--accent-soft);
  outline: none;
}

/* ---------- Pantalla de login — Fase 11.2: composición ÁREA VISUAL + CARD ---------- */
.login-screen {
  min-height: 100vh;
  background: var(--brand-primary);
}

.login-layout {
  display: flex;
  min-height: 100vh;
  width: 100%;
  justify-content: center;
}

/* Panel visual: fondo SVG abstracto (analítica de datos, nunca foto
   clínica/stock — instrucción 19) + mensaje de marca. Oculto en pantallas
   angostas (instrucción 43: sin regresión, la card sigue siendo protagonista).
   Ajuste — RÉPLICA FIEL DE REFERENCIA: 4 bloques independientes con
   posición propia (marca arriba, stats flotantes sobre el arte, columna
   de texto abajo-izquierda, footer de ancho completo abajo) — ya no un
   único bloque centrado por flex, para poder tener un footer más ancho
   que la columna de texto, igual que la referencia. */
.login-art {
  position: relative;
  flex: 1 1 52%;
  min-height: 100vh;
  overflow: hidden;
}

.login-art__svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* Fila de marca superior (logo Scholl's real, ya usado en el resto de la
   app + nombre de producto) — replica el lockup de la referencia, PERO
   sin su ícono pequeño de "piecitos" (instrucción explícita: desactualizado). */
.login-art__brand {
  position: absolute;
  z-index: 1;
  top: var(--space-2xl);
  left: var(--space-2xl);
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.login-art__brand-logo {
  height: 46px;
  width: auto;
}

.login-art__brand-divider {
  width: 1px;
  height: 34px;
  background: rgba(255, 255, 255, 0.25);
}

.login-art__brand-product {
  display: flex;
  flex-direction: column;
  line-height: 1.3;
}

.login-art__brand-name {
  font-size: 1.05rem;
  font-weight: 800;
  color: #ffffff;
}

.login-art__brand-tag {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  color: rgba(255, 255, 255, 0.55);
}

/* Badges de métrica flotando sobre el pie (guiño sutil a resultados,
   instrucción del usuario) — posiciones en % relativas al panel completo,
   pensadas para acompañar el contorno del pie sin taparlo. */
.login-art__stats {
  position: absolute;
  inset: 0;
  z-index: 1;
}

.login-art__stat {
  position: absolute;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  color: #ffffff;
}

.login-art__stat--1 {
  top: 21%;
  left: 83%;
}

.login-art__stat--2 {
  top: 61%;
  left: 46%;
}

.login-art__stat--3 {
  top: 67.5%;
  left: 80%;
}

.login-art__stat-icon {
  display: flex;
  flex-shrink: 0;
  color: var(--brand-accent);
}

.login-art__stat-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--brand-accent);
  flex-shrink: 0;
}

.login-art__stat-text {
  display: flex;
  flex-direction: column;
  font-size: 0.74rem;
  font-weight: 600;
  line-height: 1.3;
  color: rgba(255, 255, 255, 0.85);
  white-space: nowrap;
}

.login-art__stat-text strong {
  font-size: 0.92rem;
  font-weight: 800;
  color: #ffffff;
}

.login-art__content {
  position: absolute;
  z-index: 1;
  left: var(--space-2xl);
  right: var(--space-2xl);
  bottom: 92px;
  max-width: 480px;
  color: #ffffff;
}

.login-art__title {
  margin: 0 0 var(--space-3);
  font-size: 1.9rem;
  font-weight: 800;
  line-height: 1.25;
  letter-spacing: -0.01em;
}

/* Ajuste — REDISEÑO PANEL AZUL: segunda línea del título en dorado de
   marca (`--brand-accent`, no cambia entre temas — design-system.css),
   mismo recurso de jerarquía visual que ya usa el resto de la UI. */
.login-art__title-accent {
  color: var(--brand-accent);
}

.login-art__subtitle {
  margin: 0;
  font-size: 0.95rem;
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.72);
  max-width: 38ch;
}

/* Beneficios cortos (instrucción "bullets"): icono dorado + texto blanco,
   mismos iconos SVG del set propio (`icons.js`) ya usados en el resto de
   la app — ninguno hace referencia a pies/podología. */
.login-art__bullets {
  list-style: none;
  margin: var(--space-4) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.login-art__bullet {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 0.88rem;
  font-weight: 600;
  color: #ffffff;
}

.login-art__bullet-icon {
  display: flex;
  flex-shrink: 0;
  color: var(--brand-accent);
}

/* Footer de ancho completo (más ancho que la columna de texto, igual que
   la referencia) — texto izquierdo con marcador, derecho de 2 líneas. */
.login-art__footer {
  position: absolute;
  z-index: 1;
  left: var(--space-2xl);
  right: var(--space-2xl);
  bottom: var(--space-xl);
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: var(--space-4);
}

.login-art__footer-left {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
  white-space: nowrap;
}

.login-art__footer-dash {
  width: 14px;
  height: 2px;
  background: var(--brand-accent);
  flex-shrink: 0;
}

.login-art__footer-right {
  margin: 0;
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: 1.7;
  color: rgba(255, 255, 255, 0.4);
  text-align: right;
}

.login-panel {
  flex: 1 1 420px;
  max-width: 560px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
  background: var(--bg-app);
}

.login-card {
  width: 100%;
  max-width: 380px;
  align-self: center;
  background: var(--bg-elevated);
  border-radius: var(--radius-card);
  padding: var(--space-2xl) var(--space-xl);
  box-shadow: var(--shadow-elevated);
  animation: fade-in-up var(--transition-base);
}

.login-recovery {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border-subtle);
}

.login-recovery__title {
  margin: 0 0 var(--space-1);
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--text-primary);
}

.login-recovery__body {
  margin: 0;
  font-size: 0.8rem;
  line-height: 1.55;
  color: var(--text-secondary);
}

@media (max-width: 860px) {
  .login-art {
    display: none;
  }
  .login-panel {
    flex: 1 1 100%;
    max-width: none;
    background: var(--bg-app);
  }
}

.dev-credentials-slot {
  width: 100%;
  max-width: 460px;
}

.dev-credentials-panel {
  background: var(--bg-elevated);
  border: 1px solid var(--warning);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  box-shadow: 0 8px 24px rgba(20, 22, 26, 0.06);
  animation: fade-in-up var(--transition-base);
}

.dev-credentials-panel__warning {
  margin: 0 0 var(--space-3);
  padding: var(--space-2) var(--space-3);
  background: var(--warning-soft, rgba(202, 138, 4, 0.12));
  color: var(--warning);
  border-radius: var(--radius-sm);
  font-size: 0.8rem;
  line-height: 1.4;
}

.dev-credentials-panel__title {
  margin: 0 0 var(--space-3);
  font-size: 1rem;
}

.dev-credentials-panel__table-wrap {
  overflow-x: auto;
}

.dev-credentials-panel__table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
}

.dev-credentials-panel__table th,
.dev-credentials-panel__table td {
  text-align: left;
  padding: var(--space-2) var(--space-2);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

.dev-credentials-panel__table code {
  background: var(--bg-secondary);
  padding: 0.1rem 0.35rem;
  border-radius: var(--radius-sm);
}

@media (max-width: 860px) {
  .login-layout {
    flex-direction: column;
    align-items: center;
  }
  .dev-credentials-slot {
    max-width: 460px;
  }
}

.login-brand {
  text-align: center;
  margin-bottom: var(--space-4);
}

/* Fase 11.2.3, instrucción 21-22: logo real de Scholl's ARRIBA de
   "Inspector Piecitos" en la card — sobre blanco, sin necesitar el
   contorno CSS que sí hace falta en el sidebar navy. */
/* Fase 11.2.4, instrucción 28-29: +~22% (48px→58px) — todavía se
   percibía pequeño respecto al espacio disponible de la card. */
/* Ajuste puntual — +15% (58px→67px): sólo `height` cambia; `width: auto`
   ya mantiene la relación de aspecto real del archivo (623×350) sin
   necesitar tocar el atributo `width`/`height` del <img>. */
.login-brand__scholls {
  display: block;
  height: 67px;
  width: auto;
  margin: 0 auto var(--space-3);
}

.login-brand__product {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}

.login-brand__mark {
  width: 22px;
  height: 22px;
  color: var(--brand-primary);
}

.login-brand__name {
  margin: 0;
  font-size: 1.25rem;
}

.login-brand__tag {
  margin: 0;
  color: var(--text-secondary);
  font-size: 0.85rem;
}

.login-error {
  background: var(--danger-soft);
  color: var(--danger);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  font-size: 0.85rem;
  margin-bottom: var(--space-3);
}

/* ---------- Loader ---------- */
.loader {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  color: var(--text-secondary);
}

.loader__spinner {
  width: 28px;
  height: 28px;
  border: 3px solid var(--border);
  border-top-color: var(--accent-text);
  border-radius: 50%;
  animation: spin 800ms linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

@keyframes fade-in-up {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ---------- Shell: SIDEBAR (marca+nav) + (HEADER/MÓDULO/FOOTER) — Fase 11.2 ----------
   Reestructurado desde Fase 11.1: la marca pasó del header al TOPE del
   sidebar (patrón SaaS de la referencia visual — instrucción 15). El
   shell ocupa exactamente 100vh y cada columna (.sidebar/.module-outlet)
   es su propio contenedor de scroll independiente — el requisito de
   "sidebar fija, sólo el contenido central se desplaza" (instrucción 11)
   queda resuelto estructuralmente, no con position:fixed. */
.shell {
  height: 100vh;
  overflow: hidden;
  /* Fase 11.2.1 — gradiente casi imperceptible en vez de un plano sólido
     (instrucción 7), mismo tono base de --bg-app. */
  background: linear-gradient(180deg, var(--bg-app) 0%, var(--bg-app-deep, var(--bg-app)) 100%);
}

.shell-body {
  display: grid;
  grid-template-columns: var(--sidebar-width) 1fr;
  height: 100%;
  min-height: 0;
  position: relative;
  transition: grid-template-columns var(--transition-base);
}

.shell-body--sidebar-collapsed {
  grid-template-columns: var(--sidebar-width-collapsed) 1fr;
}

.shell-body--sidebar-collapsed .sidebar-link__label,
.shell-body--sidebar-collapsed .sidebar-group__title,
.shell-body--sidebar-collapsed .sidebar-brand__text,
.shell-body--sidebar-collapsed .sidebar-brand__client-logo {
  display: none;
}

.shell-main {
  display: grid;
  grid-template-rows: var(--header-height) 1fr var(--footer-height);
  min-height: 0;
  height: 100%;
}

.shell-header {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 0 var(--space-5);
  background: var(--bg-header, var(--bg-primary));
  border-bottom: 1px solid var(--border-subtle);
  position: relative;
  z-index: 5;
}

.shell-header__spacer {
  flex: 1;
}

/* Fase 11.4B — selector de tema Light/Dark (instrucción 9): icono
   discreto en el header, mismo patrón de botón circular ya usado por
   `.modal-close` (30px, fondo neutro, icono heredado por currentColor)
   — nunca un componente nuevo. */
.theme-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border: none;
  border-radius: 50%;
  background: var(--bg-secondary);
  color: var(--text-secondary);
  cursor: pointer;
  flex-shrink: 0;
  transition: background-color var(--transition-fast), color var(--transition-fast);
}

.theme-toggle:hover {
  background: var(--accent-soft);
  color: var(--accent-text);
}

/* Ajuste — TOGGLE DE TEMA EN LOGIN: mismo botón circular de arriba
   (`.theme-toggle`), sólo se añade el anclaje — la pantalla de login
   no tiene header propio donde montarlo. Fijo en la esquina superior
   derecha de toda la pantalla; se apoya sobre `.login-panel`
   (fondo `--bg-app`, ya theme-aware) tanto en desktop como en el
   layout angosto donde `.login-art` se oculta (< 860px). */
.login-screen__theme-toggle {
  position: fixed;
  top: var(--space-4);
  right: var(--space-4);
  z-index: 10;
}

/* Ajuste puntual — BOTÓN COLAPSAR/EXPANDIR SIDEBAR: ya no vive en el
   header blanco (hueco liberado, el breadcrumb lo ocupa solo, sin CSS
   extra). Ahora es un chip circular montado sobre el límite
   sidebar/contenido — hermano de `.sidebar` dentro de `.shell-body`
   (`position:relative`, ver Shell arriba), posicionado con
   `left: var(--sidebar-width)` + `translate(-50%,0)` para que
   aproximadamente el 50% quede sobre el navy y el 50% sobre el
   contenido, en ambos estados (expandido/colapsado — sólo cambia el
   valor de `left`, nunca su posición vertical). */
.sidebar-toggle {
  position: absolute;
  top: 21px;
  left: var(--sidebar-width);
  transform: translate(-50%, 0);
  z-index: 20;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  border: 1px solid rgba(252, 197, 9, 0.32);
  background: var(--brand-primary-deep, #10152e);
  box-shadow: 0 3px 10px rgba(8, 10, 24, 0.38), 0 0 0 3px var(--bg-app, #f4f5f7);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: #ffffff;
  padding: 0;
  transition: left var(--transition-base), background-color var(--transition-fast), border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.sidebar-toggle:hover {
  background: var(--brand-primary, #20294b);
  border-color: rgba(252, 197, 9, 0.55);
  box-shadow: 0 4px 12px rgba(8, 10, 24, 0.46), 0 0 0 3px var(--bg-app, #f4f5f7);
}

/* Ajuste rápido — BOTÓN SIDEBAR EN DARK: el borde dorado tenue (32%)
   se pensó contra el anillo claro (`--bg-app`) del light mode; en dark
   ese mismo anillo ya es oscuro y el botón pierde definición contra el
   resto de la escena, también oscura. Borde blanco sólo en dark. */
:root[data-theme="dark"] .sidebar-toggle {
  border-color: #ffffff;
}

:root[data-theme="dark"] .sidebar-toggle:hover {
  border-color: #ffffff;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .sidebar-toggle {
    border-color: #ffffff;
  }

  :root:not([data-theme="light"]) .sidebar-toggle:hover {
    border-color: #ffffff;
  }
}

.sidebar-toggle:focus-visible {
  outline: 2px solid var(--brand-accent, #fcc509);
  outline-offset: 2px;
}

/* Chevron: expandido → apunta a la izquierda ("contraer"); colapsado →
   apunta a la derecha ("expandir"). El path base (chevronRight) ya
   apunta a la derecha, así que sólo el estado EXPANDIDO (sin la clase
   `--sidebar-collapsed`) necesita el giro de 180°. */
.sidebar-toggle svg.icon {
  transform: rotate(180deg);
  transition: transform var(--transition-fast);
}

.shell-body--sidebar-collapsed .sidebar-toggle {
  left: var(--sidebar-width-collapsed);
}

.shell-body--sidebar-collapsed .sidebar-toggle svg.icon {
  transform: rotate(0deg);
}

/* Icono real de "piecitos" (docs/phase_01/assets_extracted/image2.png —
   par de huellitas, identificado en Fase 11.2.1 entre los assets ya
   extraídos del Excel original; copiado a app/assets/brand/ porque
   Netlify sólo sirve /app, regla 15) recoloreado vía mask-image en vez
   de emoji/huella inventada — permite CUALQUIER color de marca
   (blanco sobre navy, marino sobre blanco) sin editar el PNG fuente. */
.piecitos-mark {
  display: inline-block;
  -webkit-mask: url("../assets/brand/piecitos-icon.png") center / contain no-repeat;
  mask: url("../assets/brand/piecitos-icon.png") center / contain no-repeat;
  background-color: currentColor;
  flex-shrink: 0;
}

/* Brand lockup del sidebar (Fase 11.2.2, instrucción 19-21): UNA sola
   composición vertical apretada — Scholl's (logo real, ÓVALO como
   forma predominante, directo sobre el navy) → Inspector Piecitos
   (icono real + nombre) → versión — nunca 4 piezas sueltas. Antes
   (11.2.1) el logo vivía en una placa blanca rectangular "desconectada"
   del resto; corregido: el logo va directo sobre el degradado, con un
   borde blanco fino + sombra (vía `filter: drop-shadow` apilado —
   traza el contorno real del PNG, incluida la palabra "PODÓLOGOS", sin
   editar el archivo) en vez de una tarjeta. */
/* Fase 11.2.3 — bloque de marca CENTRADO (antes align-items:flex-start,
   la causa real de que el logo se viera "descentrado" — el óvalo del
   PNG ya está horizontalmente centrado dentro de su propio lienzo,
   verificado con centro de masa de píxeles opacos: 311.2 de 623 —
   el problema era el contenedor, no el asset) y logo con más presencia
   (46px→64px de alto). */
.sidebar-brand {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 8px;
  min-width: 0;
  padding: var(--space-2) var(--space-3) var(--space-3);
  margin-bottom: var(--space-2);
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

/* Ajuste puntual — +15% (64px→74px): sólo `height` cambia; `width: auto`
   ya mantiene la relación de aspecto real del archivo (623×350). */
.sidebar-brand__client-logo {
  display: block;
  height: 74px;
  width: auto;
  margin: 0 auto;
  filter:
    drop-shadow(1.5px 0 0 #fff)
    drop-shadow(-1.5px 0 0 #fff)
    drop-shadow(0 1.5px 0 #fff)
    drop-shadow(0 -1.5px 0 #fff)
    drop-shadow(0 8px 16px rgba(0, 0, 0, 0.4));
}

.sidebar-brand__product {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
}

.sidebar-brand__product-icon {
  width: 22px;
  height: 22px;
  color: #ffffff;
  flex-shrink: 0;
}

.sidebar-brand__text {
  display: flex;
  flex-direction: column;
  line-height: 1.25;
  min-width: 0;
}

.sidebar-brand__name {
  font-weight: 800;
  font-size: 1rem;
  white-space: nowrap;
  color: var(--text-inverse);
  letter-spacing: -0.01em;
}

.sidebar-brand__tag {
  color: var(--brand-accent, rgba(255, 255, 255, 0.6));
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  white-space: nowrap;
}

.breadcrumb {
  min-width: 0;
  font-size: 0.85rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: flex;
  align-items: baseline;
  gap: 6px;
}

.breadcrumb__group {
  color: var(--text-muted);
  font-weight: 500;
}

.breadcrumb__sep {
  color: var(--text-muted);
  opacity: 0.6;
}

.breadcrumb__page {
  color: var(--text-primary);
  font-weight: 700;
}

/* ---------- Menú de usuario (Fase 11.2 — sustituye avatar+2 botones sueltos) ---------- */
.user-menu {
  position: relative;
  flex-shrink: 0;
}

.user-menu__trigger {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  border: 1px solid transparent;
  background: transparent;
  border-radius: var(--radius-pill);
  padding: 6px var(--space-2) 6px 6px;
  cursor: pointer;
  transition: background-color var(--transition-fast), border-color var(--transition-fast);
}

.user-menu__trigger:hover,
.user-menu--open .user-menu__trigger {
  background: var(--bg-secondary);
  border-color: var(--border-subtle);
}

.user-menu__avatar {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--brand-accent, var(--accent-soft));
  color: var(--brand-primary, var(--accent));
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.75rem;
  font-weight: 800;
  flex-shrink: 0;
}

.user-menu__info {
  display: flex;
  flex-direction: column;
  line-height: 1.25;
  text-align: left;
}

.user-menu__name {
  font-size: 0.83rem;
  font-weight: 700;
  color: var(--text-primary);
}

.user-menu__role {
  font-size: 0.7rem;
  color: var(--text-secondary);
  text-transform: capitalize;
}

.user-menu__caret {
  display: flex;
  color: var(--text-muted);
  transition: transform var(--transition-fast);
}

.user-menu--open .user-menu__caret {
  transform: rotate(180deg);
}

.user-menu__panel {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  min-width: 220px;
  background: var(--bg-popover, var(--bg-elevated));
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-elevated);
  padding: var(--space-2);
  display: flex;
  flex-direction: column;
  gap: 2px;
  z-index: 20;
  animation: fade-in-up var(--transition-fast) var(--ease-standard);
}

.user-menu__panel[hidden] {
  display: none;
}

.user-menu__item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  border: none;
  background: transparent;
  border-radius: var(--radius-md);
  padding: var(--space-2) var(--space-3);
  font: inherit;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-primary);
  text-align: left;
  cursor: pointer;
  transition: background-color var(--transition-fast);
}

.user-menu__item:hover {
  background: var(--bg-secondary);
}

.user-menu__item--danger {
  color: var(--danger);
}

.user-menu__item--danger:hover {
  background: var(--danger-soft);
}

/* Fase 11.2.1 — degradado real (antes: mismo color en ambos stops, en
   la práctica un navy plano). Sutil, dentro de la familia marino, sin
   tonos "gaming"/eléctricos: diagonal navy→navy-profundo + un highlight
   radial muy discreto arriba-izquierda para dar sensación de luz/profundidad. */
/* Fase 11.2.2 — degradado de 3 stops (antes 2, apenas perceptible):
   navy claro arriba → navy base al centro → navy profundo abajo, más
   highlight radial discreto — debe notarse en el screenshot sin caer
   en "gaming"/neón (instrucción 17). */
/* Fase 11.2.3 — degradado subido de intensidad (11.2.2 seguía leyéndose
   casi plano): 3 stops más separados + highlight radial más presente. */
.sidebar {
  background:
    radial-gradient(140% 60% at 10% 0%, rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0) 55%),
    linear-gradient(
      180deg,
      var(--brand-primary-light, #304071) 0%,
      var(--brand-primary) 42%,
      var(--brand-primary-deep, #10152e) 100%
    );
  height: 100%;
  overflow-y: auto;
  padding: var(--space-3) var(--space-3) var(--space-4);
  display: flex;
  flex-direction: column;
  box-shadow: 4px 0 24px rgba(0, 0, 0, 0.18), inset -1px 0 0 rgba(255, 255, 255, 0.06);
}

.sidebar-groups {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/* Ajuste puntual — PANEL DE NAVEGACIÓN: cada categoría
   (Operación/Análisis/Administración) es ahora un <button> clicable
   (antes un <p> puramente decorativo) — mismo tamaño/tipografía/color
   de siempre, sólo se le agrega la flechita y el estado hover/foco. */
.sidebar-group__title {
  width: 100%;
  margin: 0 0 var(--space-2);
  padding: 4px var(--space-3);
  border: none;
  background: none;
  border-radius: var(--radius-md);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  font: inherit;
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--brand-accent, rgba(255, 255, 255, 0.5));
  cursor: pointer;
  transition: background-color var(--transition-fast), color var(--transition-fast);
}

.sidebar-group__title:hover,
.sidebar-group__title:focus-visible {
  background: rgba(255, 255, 255, 0.06);
  color: #ffffff;
}

.sidebar-group__title:focus-visible {
  outline: 2px solid var(--brand-accent, #fcc509);
  outline-offset: -1px;
}

.sidebar-group__chevron {
  display: inline-flex;
  flex-shrink: 0;
  transition: transform 240ms var(--ease-standard, ease), color var(--transition-fast);
}

.sidebar-group__title:hover .sidebar-group__chevron {
  color: #ffffff;
}

/* Colapso con "sensación": animación real de altura vía
   `grid-template-rows` (1fr -> 0fr), sin medir alturas en JS ni saltos
   bruscos de un `display:none` instantáneo — mismo truco ya usado en
   Alertas para el panel de ayuda expandible. `.sidebar-group__collapse`
   es el grid de 1 fila; `.sidebar-list` (dentro) sólo necesita
   `overflow:hidden`/`min-height:0` para que el recorte se vea
   progresivo en vez de que el contenido se "trague" de golpe. */
.sidebar-group__collapse {
  display: grid;
  grid-template-rows: 1fr;
  opacity: 1;
  transition: grid-template-rows 260ms var(--ease-standard, ease), opacity 200ms ease;
}

.sidebar-group__collapse > .sidebar-list {
  overflow: hidden;
  min-height: 0;
}

.sidebar-group--collapsed .sidebar-group__chevron {
  transform: rotate(-90deg);
}

.sidebar-group--collapsed .sidebar-group__collapse {
  grid-template-rows: 0fr;
  opacity: 0;
}

/* En el rail colapsado (sólo íconos) la flechita/categoría no se ve
   (regla ya existente, `.sidebar-group__title{display:none}` más
   abajo) — ninguna categoría debe quedar "atrapada" contraída ahí,
   porque no hay forma de volver a abrirla sin expandir antes el
   sidebar completo. */
.shell-body--sidebar-collapsed .sidebar-group--collapsed .sidebar-group__collapse {
  grid-template-rows: 1fr;
  opacity: 1;
}

.sidebar-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.sidebar-link {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 0.6rem var(--space-3);
  border-radius: var(--radius-lg);
  color: rgba(255, 255, 255, 0.62);
  text-decoration: none;
  font-size: 0.9rem;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  transition: background-color var(--transition-fast), color var(--transition-fast), transform var(--transition-fast);
}

.sidebar-link__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  color: rgba(255, 255, 255, 0.55);
  transition: color var(--transition-fast), transform var(--transition-fast);
}

.sidebar-link:hover {
  background: rgba(255, 255, 255, 0.08);
  color: #ffffff;
  transform: translateX(2px);
}

.sidebar-link:hover .sidebar-link__icon {
  color: #ffffff;
}

.sidebar-link--active {
  background: #ffffff;
  color: var(--brand-primary);
  font-weight: 700;
  box-shadow: var(--shadow-elevated);
  transform: none;
}

.sidebar-link--active:hover {
  transform: none;
}

.sidebar-link--active .sidebar-link__icon {
  color: var(--brand-accent-ink, var(--brand-primary));
}

/* Fase 11.2.3, instrucción 10-11 — crédito discreto anclado al pie del
   sidebar (`.sidebar-groups{flex:1}` empuja esto al fondo sin que se
   desplace con la navegación). */
.sidebar-footer-brand {
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  text-align: center;
}

.sidebar-footer-brand__full {
  display: block;
  font-size: 0.7rem;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.55);
  letter-spacing: 0.01em;
}

.sidebar-footer-brand__full strong {
  color: rgba(255, 255, 255, 0.85);
  font-weight: 700;
}

.sidebar-footer-brand__compact {
  display: none;
  width: 30px;
  height: 30px;
  margin: 0 auto;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.08);
  color: rgba(255, 255, 255, 0.7);
  align-items: center;
  justify-content: center;
  font-size: 0.66rem;
  font-weight: 800;
  letter-spacing: 0.02em;
  cursor: default;
}

.sidebar-scrim {
  display: none;
}

/* Fase 11.2.3, instrucción 8-9 — sidebar COLAPSADA: bug real corregido.
   `.sidebar-link` heredaba el padding horizontal de la versión
   expandida (`--space-3` a cada lado) sobre un rail de sólo 40px de
   contenido útil — con `justify-content` implícito (flex-start) el
   ícono quedaba pegado a la izquierda, visualmente descentrado. Ahora,
   en collapsed, cada link es un botón cuadrado 44×44 centrado por el
   propio `.sidebar-list` (nunca padding heredado/translate/margin
   asimétrico). */
.shell-body--sidebar-collapsed .sidebar-brand {
  align-items: center;
}

.shell-body--sidebar-collapsed .sidebar-list {
  align-items: center;
}

.shell-body--sidebar-collapsed .sidebar-link {
  width: 44px;
  height: 44px;
  padding: 0;
  gap: 0;
  justify-content: center;
  margin-inline: auto;
}

.shell-body--sidebar-collapsed .sidebar-footer-brand__full {
  display: none;
}

.shell-body--sidebar-collapsed .sidebar-footer-brand__compact {
  display: flex;
}

.module-outlet {
  overflow-y: auto;
  height: 100%;
  padding: var(--space-5);
}

.module-placeholder {
  max-width: 720px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-card, var(--radius-md));
  box-shadow: var(--shadow-card, none);
  padding: var(--space-4);
  animation: fade-in-up var(--transition-base);
}

.module-placeholder h1 {
  margin-top: 0;
  font-size: 1.4rem;
}

.module-placeholder__note {
  color: var(--text-secondary);
}

.tabs-preview {
  display: flex;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.tab-preview {
  border: 1px dashed var(--border);
  color: var(--text-secondary);
  border-radius: var(--radius-sm);
  padding: var(--space-1) var(--space-3);
  font-size: 0.8rem;
}

.status-list {
  margin: var(--space-3) 0 0;
  padding-left: var(--space-4);
  color: var(--text-secondary);
}

.shell-footer {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-2);
  padding: 0 var(--space-5);
  background: var(--bg-header, #ffffff);
  border-top: 1px solid var(--border-subtle);
  color: var(--text-muted);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.01em;
}

/* ---------- Toasts ---------- */
#toast-root {
  position: fixed;
  bottom: var(--space-4);
  right: var(--space-4);
  z-index: 1000;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.toast {
  background: var(--text-primary);
  color: var(--text-inverse);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  font-size: 0.85rem;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity var(--transition-base), transform var(--transition-base);
  box-shadow: 0 4px 16px rgba(20, 22, 26, 0.2);
  max-width: 320px;
}

.toast--visible {
  opacity: 1;
  transform: translateY(0);
}

.toast--warning {
  background: var(--warning);
}

.toast--info {
  background: var(--text-primary);
}

/* ---------- Responsive ---------- */
@media (max-width: 1024px) {
  :root {
    --sidebar-width: 200px;
  }
}

@media (max-width: 768px) {
  .user-menu__info {
    display: none;
  }

  .shell-body {
    grid-template-columns: 1fr;
  }

  /* En móvil el sidebar es un drawer fuera de flujo (position:fixed,
     fuera de pantalla) — la fórmula de escritorio (`left:
     var(--sidebar-width)`, tomada del ancho de columna del grid) ya no
     describe ningún borde real aquí, así que el botón vuelve a anclarse
     al rincón superior-izquierdo, donde vivía el hamburger original —
     el comportamiento del drawer/scrim no cambia en nada, sólo la
     posición de este botón. Tamaño levemente reducido (34px, dentro del
     rango pedido) y pegado al borde para no invadir el breadcrumb, que
     en este ancho arranca justo en el padding del header
     (`.shell-header{padding:0 var(--space-5)}` = 40px) — sin este
     ajuste el botón (38px desde left:14px) le tapaba la primera letra. */
  .sidebar-toggle,
  .shell-body--sidebar-collapsed .sidebar-toggle {
    left: 4px;
    top: 15px;
    width: 34px;
    height: 34px;
    transform: none;
  }

  .sidebar {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    width: var(--sidebar-width);
    max-width: 80vw;
    transform: translateX(-100%);
    transition: transform var(--transition-base);
    z-index: 50;
    box-shadow: 4px 0 16px rgba(20, 22, 26, 0.12);
  }

  .shell-body--drawer-open .sidebar {
    transform: translateX(0);
  }

  .shell-body--drawer-open .sidebar-link__label,
  .shell-body--drawer-open .sidebar-brand__text {
    display: flex;
  }

  .sidebar-scrim {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(20, 22, 26, 0.32);
    z-index: 40;
  }

  .sidebar-scrim[hidden] {
    display: none;
  }

  .module-outlet {
    padding: var(--space-3);
  }

  .shell-footer {
    font-size: 0.72rem;
  }
}

@media (max-width: 420px) {
  .shell-footer span:last-child {
    display: none;
  }

  .login-card {
    padding: var(--space-4) var(--space-3);
  }
}

/* ==========================================================================
   Fase 4 — Parametrizables
   ========================================================================== */

.btn--sm {
  padding: var(--space-1) var(--space-2);
  font-size: 0.8rem;
}

.btn--danger {
  background: var(--danger);
  color: var(--text-inverse);
}

.btn--danger:hover:not(:disabled) {
  background: #942e24;
}

.form-field--inline {
  flex-direction: row;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: 0;
}

.form-field--inline label {
  margin: 0;
  white-space: nowrap;
}

.form-field--checkbox label {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-weight: 500;
  color: var(--text-primary);
}

.form-field select {
  font: inherit;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-primary);
  color: var(--text-primary);
}

.form-hint {
  color: var(--text-secondary);
  font-size: 0.8rem;
  margin: 0 0 var(--space-3);
}

.form-hint-inline {
  font-weight: 400;
  color: var(--text-secondary);
}

.has-error input,
.has-error select {
  border-color: var(--danger);
}

.field-error {
  color: var(--danger);
  font-size: 0.78rem;
  margin: var(--space-1) 0 0;
}

/* ---------- Cabecera de página + shell de Parametrizables — Fase 11.2 ---------- */
.param-shell h1 {
  margin: 0 0 4px;
  font-size: var(--text-page-title-size);
  font-weight: var(--text-page-title-weight);
  line-height: var(--text-page-title-lh);
  letter-spacing: -0.01em;
  color: var(--text-primary);
}

.param-shell__subtitle {
  color: var(--text-secondary);
  margin: 0 0 var(--space-4);
  font-size: 0.9rem;
}

/* .eval-list-header (Evaluaciones/Base de Datos/Exportar/Conexión) comparte
   esta misma jerarquía de encabezado de página. */
.eval-list-header h1 {
  margin: 0 0 4px;
  font-size: var(--text-page-title-size);
  font-weight: var(--text-page-title-weight);
  letter-spacing: -0.01em;
}

.param-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  background: var(--bg-surface-secondary);
  border: none;
  border-radius: var(--radius-lg);
  padding: 4px;
  margin-bottom: var(--space-4);
  width: fit-content;
  max-width: 100%;
}

/* Ajuste visual puntual — borde navy de marca sólo en el grupo de tabs
   de PARAMETRIZABLES (Modelo de evaluación/Umbrales/Metas/Actividad/
   Migración inicial), como unidad completa (nunca por pestaña
   individual). `.param-tabs` es compartida con Usuarios y roles (sus
   pestañas Agentes/Usuarios y roles no deben cambiar) — se distingue
   vía el `aria-label` ya existente, sin clase nueva ni tocar JS. */
.param-tabs[aria-label="Secciones de Parametrizables"] {
  border: 1.5px solid var(--border-brand);
}

.param-tab {
  border: none;
  background: transparent;
  border-radius: var(--radius-md);
  padding: 0.5rem var(--space-3);
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-secondary);
  cursor: pointer;
  white-space: nowrap;
  transition: color var(--transition-fast), background-color var(--transition-fast), box-shadow var(--transition-fast);
}

.param-tab:hover {
  color: var(--text-primary);
}

.param-tab--active {
  color: var(--brand-primary);
  background: var(--bg-elevated);
  box-shadow: var(--shadow-subtle);
}

.param-panel {
  max-width: 100%;
  background: var(--bg-elevated);
  border: none;
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  padding: var(--space-5);
}

/* Fase 11.2.1 — "toolbar card": por defecto, todo `.param-panel__toolbar`
   es su propia superficie (NIVEL 1 — instrucción 8/13), porque casi
   siempre aparece SUELTO bajo `.param-shell` (Evaluaciones/Base de
   Datos/Exportar/Alertas) y antes "flotaba" directo sobre el canvas.
   La única excepción es cuando ya vive DENTRO de `.param-panel`
   (Parametrizables/Usuarios y roles) — ahí NO se le da una segunda
   superficie (evita "card dentro de card", instrucción 9/19: esa
   combinación ya es la pantalla mejor resuelta, no degradarla) — ver
   el override más abajo. */
/* Ajuste visual global — borde navy de marca envolviendo la tarjeta
   completa de la toolbar (nunca cada control individual por
   separado). Mismo selector que ya cubre Evaluaciones/Base de Datos/
   Dashboard/Alertas/Reportes/Exportar/Conexión (todas usan
   `.param-panel__toolbar` suelta bajo `.param-shell`) — un solo punto
   de cambio. La excepción anidada de Usuarios y roles/Parametrizables
   (`.param-panel .param-panel__toolbar`, más abajo) no se toca: ahí
   el toolbar no es su propia tarjeta, es parte de `.param-panel`. */
.param-panel__toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
  background: var(--bg-elevated);
  border: 1.5px solid var(--border-brand);
  border-radius: var(--radius-lg);
  padding: var(--space-3) var(--space-4);
  box-shadow: var(--shadow-subtle);
}

.param-panel__toolbar-actions {
  display: flex;
  gap: var(--space-2);
  margin-left: auto;
  flex-wrap: wrap;
}

/* Fase 11.2.4, instrucción 23-27 — bug real corregido: sólo `.param-search`
   (Usuarios/Parametrizables) tenía esta regla; `.eval-filtro-q`
   (Evaluaciones) y `.bd-filtro-q` (Base de Datos) son inputs
   `type="search"`/`type="text"` con OTRO nombre de clase — quedaban sin
   ningún estilo (input nativo del navegador), la causa real de que
   "Buscar por agente o auditor" se viera "perdida". Selector ampliado a
   cualquier buscador de texto dentro de una toolbar + icono de lupa
   vectorial (mismo trazo que `icons.js`, embebido como SVG porque un
   `background-image` no puede importar el módulo JS). */
.param-panel__toolbar input[type="search"],
.param-panel__toolbar input[type="text"].param-search,
.param-panel__toolbar .param-search {
  flex: 1;
  min-width: 200px;
  padding: var(--space-2) var(--space-3) var(--space-2) 38px;
  border: 1.5px solid var(--border-subtle);
  border-radius: var(--radius-md);
  font: inherit;
  background: var(--bg-app) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%236b7690' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='10.5' cy='10.5' r='6.5'/%3E%3Cpath d='m19.5 19.5-4.35-4.35'/%3E%3C/svg%3E") no-repeat 12px center;
  background-size: 16px 16px;
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.param-panel__toolbar input[type="search"]::-webkit-search-cancel-button {
  margin-right: 2px;
}

.param-panel__toolbar input[type="search"]:focus-visible,
.param-panel__toolbar .param-search:focus-visible {
  border-color: var(--accent-text);
  box-shadow: 0 0 0 3px var(--accent-soft);
  outline: none;
}

.param-panel__toolbar select {
  padding: var(--space-2) var(--space-3);
  border: 1.5px solid var(--border-subtle);
  border-radius: var(--radius-md);
  font: inherit;
  font-size: 0.85rem;
  background: var(--bg-app);
  color: var(--text-primary);
}

/* Microfase UI — "Mostrar usuarios inactivos" (Usuarios y roles):
   filtro pequeño integrado en la misma toolbar, mismo criterio
   tipográfico que `.param-panel__toolbar select` de arriba — nunca una
   card nueva. `.param-panel__toolbar` ya es flex-wrap, así que en
   390px este control simplemente baja de línea, sin overflow. */
.param-filter-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 0.85rem;
  color: var(--text-primary);
  white-space: nowrap;
  cursor: pointer;
}

.param-panel__new {
  margin-left: auto;
}

/* ---------- Tablas administrativas ---------- */
/* Fase 11.2.1 — la tabla es NIVEL 2 (card real, no un fondo apenas
   distinguible del canvas): mismo razonamiento de excepción anidada
   que el toolbar de arriba, ver override `.param-panel` más abajo. */
.data-table-wrapper {
  overflow-x: auto;
  background: var(--bg-elevated);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-subtle);
}

/* Ajuste visual controlado — contenedor con scroll vertical propio y
   acotado: el título/buscador/toolbar de la pantalla quedan FUERA de
   este contenedor (normal flow, nunca se desplazan); sólo el área de
   registros se desplaza, y el header de la tabla (`position:sticky`)
   permanece visible dentro de ella. Opt-in por pantalla (instrucción
   2) — se aplica explícitamente donde corresponde, nunca de forma
   global a `.data-table-wrapper` (evita tocar Alertas/Dashboard). */
.table-scroll-area {
  max-height: min(620px, calc(100vh - 360px));
  overflow: auto;
}

/* `.data-table-wrapper` normalmente maneja su PROPIO scroll horizontal
   (`overflow-x:auto`), lo que por especificación hace que su
   `overflow-y` calculado también sea `auto` — convirtiéndola, sin
   querer, en el contenedor de scroll más cercano para el `sticky` del
   thead (uno que nunca se desplaza, porque su alto siempre es exacto
   al contenido). Anulado aquí para que `.table-scroll-area` sea el
   ÚNICO contenedor de scroll real y el sticky funcione contra ella. */
.table-scroll-area .data-table-wrapper {
  overflow: visible;
}

/* Excepción anidada (instrucción 9/19): dentro de `.param-panel`
   (Parametrizables/Usuarios y roles) el toolbar y la tabla NO llevan
   su propia superficie — el `.param-panel` que ya los envuelve es la
   única card; darles una segunda rompería esa pantalla, hoy la mejor
   resuelta, con "card dentro de card". */
.param-panel .param-panel__toolbar,
.param-panel .data-table-wrapper {
  background: transparent;
  border: none;
  box-shadow: none;
}

.param-panel .param-panel__toolbar {
  padding: 0;
  margin-bottom: var(--space-4);
}

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-table-size);
}

.data-table th,
.data-table td {
  text-align: left;
  padding: var(--space-3) var(--space-3);
  border-bottom: 1px solid var(--border-subtle);
  vertical-align: middle;
}

/* Fase 11.2.3 — mucho más contraste (instrucción 15-17): antes se
   confundía con cualquier otra banda NIVEL 1 de la app; ahora tiene su
   propio token, más oscuro, y un borde inferior propio (2px) que la
   separa de las filas como una cabecera real, no una fila más. */
/* Fase 11.2.5 — matiz amarillo Scholl's (instrucción 5-6), global vía
   `.data-table` (Base de Datos, Evaluaciones, Alertas, Usuarios y
   roles, Parametrizables cuando aplique). */
/* Corrección visual — el degradado vive en `<thead>` (una sola capa
   que cubre el ancho total de la tabla), NUNCA por `<th>` — así nunca
   se reinicia columna por columna. `<thead>` admite `position:sticky`
   en navegadores modernos (Chromium/Firefox actuales), así que el
   sticky se mueve aquí también: un solo contexto sticky, en vez de N
   (uno por celda) — mismo comportamiento "permanece visible al hacer
   scroll", implementación más simple y coherente con el degradado
   único. */
.data-table thead {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--header-gradient, var(--bg-table-header));
  border-bottom: 2px solid var(--border-header-accent, var(--border-strong));
  border-top-left-radius: var(--radius-lg);
  border-top-right-radius: var(--radius-lg);
}

/* Ajuste puntual — BORDE DERECHO RECTO EN HEADERS DE TABLA, exclusivo
   de Alertas y Usuarios y roles: esquina superior izquierda se
   conserva curva (regla de arriba, sin cambios); sólo la derecha pasa
   a recta. `.alert-tabla` ya es una clase propia de Alertas; Usuarios
   y roles comparte `.data-table`/`.param-tab-panel` con Parametrizables
   (mismo componente `renderDataTable`), así que se escoge vía
   `.usuarios-shell` (clase única del módulo, ver ajuste STICKY EN
   USUARIOS Y ROLES más abajo — antes usaba un combinador de hermano
   general vía `aria-label`, pero dejó de servir cuando `.param-tabs`
   pasó a vivir dentro de `.usuarios-sticky-top`) — nunca toca
   Parametrizables ni Evaluaciones/Base de Datos (ya correctas,
   instrucción 6). */
.alert-tabla thead,
.usuarios-shell .data-table thead {
  border-top-right-radius: 0;
}

/* Ajuste rápido — USUARIOS Y ROLES: regla dedicada y explícita para la
   tabla de "Usuarios y roles → Usuarios y roles" (`.usuarios-tabla`,
   ver ajuste HOVER/SELECCIÓN EN USUARIOS Y ROLES), sin depender de la
   regla compartida de arriba (`.usuarios-shell`, que también cubre
   Agentes) — esquina izquierda intacta (hereda `var(--radius-lg)` de
   `.data-table thead` de arriba), sin tocar borde inferior ni
   degradado. No se toca `.agentes-tabla` en ningún punto. */
.usuarios-tabla .data-table thead {
  border-top-right-radius: 0;
}

/* Ajuste puntual — ALERTAS STICKY + COMPACTACIÓN SUPERIOR, instrucción
   6: `.alert-tabla-scroll` (más abajo) pasó a ser su propio contenedor
   de scroll acotado — el header ya se pega correctamente con el
   `top:0` que trae `.data-table thead` por defecto (relativo a ESE
   contenedor, no a la página), sin necesitar conocer la altura del
   bloque superior sticky. */

/* Ajuste puntual — STICKY EN USUARIOS Y ROLES: mismo principio de 2
   niveles apilados de `position:sticky` (nunca `position:fixed`),
   integrados al flujo normal de `#module-outlet` (el mismo contenedor
   que ya hace scroll para cualquier módulo). Nivel 1 (título+subtítulo+
   tabs) vive en `usuariosRoles.js`; nivel 2 (toolbar de
   búsqueda/filtros/"+ Nuevo…") vive DENTRO de cada vista de pestaña
   (agentesView.js/usuariosView.js, ambas comparten `.param-panel__toolbar`
   sin cambios de markup). `.usuarios-shell` es una clase nueva y única
   de este módulo (usuariosRoles.js) — se usa para escopar, en vez del
   truco de `aria-label`+hermano usado en otros ajustes, porque aquí
   `.param-tabs` pasó a vivir DENTRO de `.usuarios-sticky-top` y ya no es
   hermano directo de `.param-tab-panel`. Nunca toca Parametrizables
   (que comparte `.param-panel__toolbar`/`.param-panel__table` pero no
   `.usuarios-shell`). */
.usuarios-sticky-top {
  position: sticky;
  top: 0;
  z-index: 6;
  background: var(--bg-app);
}

.usuarios-shell .param-panel__toolbar {
  position: sticky;
  top: var(--usuarios-tier1-offset, 92px);
  z-index: 5;
  background: var(--bg-elevated);
}

/* Nivel 3 — el header de la tabla (`.data-table thead`, degradado
   navy/esquina izquierda curva/derecha recta/divisores, sin tocar
   ninguna de esas reglas) sigue pegándose con su `top:0` por defecto,
   pero relativo a ESTE contenedor acotado — nunca a la página —
   mismo patrón ya probado en Alertas/Evaluaciones/Base de Datos: un
   único scroll real (las filas), nunca doble. La altura disponible se
   resta dinámicamente de los 2 niveles de arriba (`--usuarios-controls-
   offset`, medidos con ResizeObserver) más el padding/footer globales;
   piso de 200px para que nunca colapse a 0 filas visibles. */
.usuarios-shell .param-panel__table {
  overflow: auto;
  max-height: max(200px, min(620px, calc(100vh - var(--usuarios-controls-offset, 220px) - 200px)));
  border-radius: var(--radius-lg);
}

.usuarios-shell .param-panel__table .data-table-wrapper {
  overflow: visible;
}

/* Ajuste visual puntual — la barra debe leerse como UNA sola pieza:
   0 divisores verticales entre columnas (ni border-left/right, ni
   outline, ni box-shadow interno) — sólo color/tipografía por celda,
   nunca separación. */
.data-table thead th {
  color: var(--text-table-header);
  font-weight: 700;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  background: transparent;
  padding-top: var(--space-3);
  padding-bottom: var(--space-3);
  border-bottom: none;
  border-inline: none;
  outline: none;
  box-shadow: none;
}

/* Divisores verticales sutiles entre columnas — líneas superpuestas al
   degradado único de `<thead>` (nunca lo reinician ni lo cortan en
   bloques independientes; el último `<th>` queda sin línea). */
.data-table thead th:not(:last-child) {
  border-right: 1px solid rgba(255, 255, 255, 0.22);
}

.data-table tbody tr:last-child td {
  border-bottom: none;
}

/* Fase 11.2.3 — hover más elegante (instrucción 18-19): tinte muy
   sutil + un acento de borde interior dorado discreto, en vez de
   pintar toda la fila con un gris/azul fuerte que la hacía parecer
   "seleccionada". */
/* Fase 11.2.4, instrucción 9-13 — hover rediseñado: el dorado dejó de
   ser protagonista (regla 11: más azul en la interfaz, dorado sólo
   como acento de marca). Tinte navy muy suave + sombra ligera en vez
   de una línea de borde — se siente "interactivo", no "seleccionado". */
.data-table tbody tr {
  transition: background-color 200ms var(--ease-standard), box-shadow 200ms var(--ease-standard);
}

.data-table tbody tr:hover {
  background: var(--row-hover-tint);
  box-shadow: 0 3px 10px rgba(20, 30, 60, 0.08);
  position: relative;
}

/* Ajuste visual puntual — hover EXCLUSIVO de Evaluaciones y Base de
   Datos (`.eval-list-table`/`.bd-table`, los wrappers de sus
   respectivos listView.js): gris neutro suave (nunca azul/amarillo de
   fondo), sin elevación ni desplazamiento — sobrio. El resto de las
   tablas (Alertas/Usuarios/Parametrizables) conserva el hover navy
   genérico de arriba, sin tocar (fuera de alcance de este ajuste). */
/* Ajuste puntual — BORDES CURVOS EN HEADERS DE TABLAS, exclusivo de
   Evaluaciones/Base de Datos (mismas 2 clases de arriba). Reutiliza el
   patrón ya probado en "Nueva Evaluación" (`.eval-categoria-group`, más
   abajo): un WRAPPER con `border-radius` recorta lo que hay dentro, en
   vez de confiar en `border-top-left-radius`/`-right-radius` puestos
   directamente sobre `<thead>` (ya declarados arriba, instrucción 1 —
   pero `display:table-header-group` no las recorta de forma fiable en
   todos los navegadores: por eso el header seguía viéndose cuadrado
   pese a esa regla). `.eval-list-table`/`.bd-table` YA son
   `.table-scroll-area` (`overflow:auto`, el contenedor de scroll real)
   — sólo hacía falta el radio: `overflow:auto` ya recorta a
   `border-radius` sin necesitar `overflow:hidden`, así que el sticky y
   el scroll horizontal existentes no se tocan (instrucción 4/validación
   "sticky sin romperse"). El degradado sigue siendo el mismo, continuo,
   de `<thead>` (instrucción 7); esto sólo recorta sus esquinas
   exteriores — nunca redondea cada `<th>` por separado (instrucción 8)
   ni toca los divisores verticales existentes (instrucción 3/9). */
.eval-list-table,
.bd-table {
  border-radius: var(--radius-lg);
}

/* Ajuste rápido — HOVER/SELECCIÓN EN USUARIOS Y ROLES: mismo patrón,
   extendido a Agentes (`.agentes-tabla`) y Usuarios y roles
   (`.usuarios-tabla`, sus respectivos wrappers en agentesView.js/
   usuariosView.js) — mismos selectores combinados que ya usan
   Evaluaciones/Base de Datos, nunca un bloque aparte.
   Ajuste rápido — PARAMETRIZABLES: mismo patrón, extendido a Umbrales
   (`.umbrales-tabla`) y Metas (`.metas-tabla`, sus respectivos
   wrappers en umbralesView.js/metasView.js) — el resto de
   Parametrizables (Modelo de evaluación, Actividad, Migración inicial)
   no se toca. */
.eval-list-table .data-table tbody tr,
.bd-table .data-table tbody tr,
.agentes-tabla .data-table tbody tr,
.usuarios-tabla .data-table tbody tr,
.umbrales-tabla .data-table tbody tr,
.metas-tabla .data-table tbody tr {
  transition: background-color 180ms ease;
}

.eval-list-table .data-table tbody tr:hover,
.bd-table .data-table tbody tr:hover,
.agentes-tabla .data-table tbody tr:hover,
.usuarios-tabla .data-table tbody tr:hover,
.umbrales-tabla .data-table tbody tr:hover,
.metas-tabla .data-table tbody tr:hover {
  background: var(--row-hover-bg);
  box-shadow: none;
}

/* Evaluaciones conserva la barra izquierda #fcc509 de 4px (requisito
   ya aprobado) sobre la primera celda — nunca sobre `<tr>` directo,
   inconsistente con `border-collapse:collapse`. */
.eval-list-table .data-table tbody tr:hover > td:first-child,
.bd-table .data-table tbody tr:hover > td:first-child,
.agentes-tabla .data-table tbody tr:hover > td:first-child,
.usuarios-tabla .data-table tbody tr:hover > td:first-child,
.umbrales-tabla .data-table tbody tr:hover > td:first-child,
.metas-tabla .data-table tbody tr:hover > td:first-child {
  box-shadow: inset 4px 0 0 #fcc509;
}

.table-loading,
.table-empty {
  color: var(--text-secondary);
  padding: var(--space-2xl) var(--space-4);
  text-align: center;
  background: var(--bg-app);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-lg);
}

.row-actions {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
}

.row-actions .btn {
  padding: 0.4rem 0.85rem;
  font-size: 0.78rem;
  border-radius: var(--radius-pill);
}

.row-avatar {
  display: inline-flex;
  width: 32px;
  height: 32px;
}

.row-avatar__img {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  object-fit: cover;
}

.row-avatar__placeholder {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent-text);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 0.8rem;
}

/* Ajuste rápido — AVATARES EN DARK: en light, `--accent-soft` (tinte
   navy) ya se distingue de la card blanca sin necesitar borde; en
   dark ese mismo tinte pasa a un overlay claro casi imperceptible
   (instrucción "en dark el mismo tinte se pierde contra superficies
   ya oscuras", Fase 11.4B) — la "bolita" queda sin borde propio de la
   card. Se le agrega un borde perimetral sutil SÓLO en dark, mismo
   token `--border-strong` ya usado para bordes que necesitan
   distinguirse de una superficie oscura — también al avatar con foto
   real, por consistencia de forma. */
:root[data-theme="dark"] .row-avatar__img,
:root[data-theme="dark"] .row-avatar__placeholder {
  border: 1px solid var(--border-strong);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .row-avatar__img,
  :root:not([data-theme="light"]) .row-avatar__placeholder {
    border: 1px solid var(--border-strong);
  }
}

/* ---------- Badges de estado — Fase 11.2.1: sistema único, altura y
   padding consistentes (antes demasiado pequeño/apretado — instrucción 10),
   reutilizado también por `.alert-badge`/`.nivel-badge`/`.tendencia-badge`/
   `.conexion-badge`/chips más abajo, mismo lenguaje visual. ---------- */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 22px;
  padding: 0 var(--space-2) 0 10px;
  border-radius: var(--radius-pill);
  font-size: 0.72rem;
  font-weight: 700;
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  white-space: nowrap;
}

.badge::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}

.badge--activo,
.badge--vigente,
.badge--exito,
.badge--activa {
  background: var(--success-soft);
  color: var(--success);
}

.badge--inactivo,
.badge--historico {
  background: var(--bg-secondary);
  color: var(--text-secondary);
}

.badge--borrador {
  background: var(--warning-soft);
  color: var(--warning);
}

.badge--error,
.badge--anulada {
  background: var(--danger-soft);
  color: var(--danger);
}

/* ---------- Modal — Fase 11.2 ---------- */
.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(15, 18, 32, 0.55);
  backdrop-filter: blur(2px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-3);
  z-index: 2000;
  animation: fade-in-up 150ms ease;
}

.no-scroll {
  overflow: hidden;
}

.modal-dialog {
  background: var(--bg-elevated);
  border-radius: var(--radius-card, var(--radius-lg));
  width: 100%;
  max-width: 480px;
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  box-shadow: var(--shadow-modal, 0 16px 48px rgba(20, 22, 26, 0.25));
}

.modal-dialog--lg {
  max-width: 680px;
}

.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border-subtle);
}

.modal-title {
  margin: 0;
  font-size: 1.1rem;
  font-weight: 700;
}

.modal-close {
  border: none;
  background: var(--bg-secondary);
  width: 30px;
  height: 30px;
  border-radius: 50%;
  cursor: pointer;
  color: var(--text-secondary);
  font-size: 1rem;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background-color var(--transition-fast), color var(--transition-fast);
}

.modal-close:hover {
  background: var(--danger-soft);
  color: var(--danger);
}

.modal-body {
  padding: var(--space-5);
  overflow-y: auto;
}

.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
  margin-top: var(--space-4);
}

.confirm-dialog__message {
  color: var(--text-primary);
  margin: 0 0 var(--space-3);
}

.confirm-dialog__actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
}

/* ---------- Credencial temporal ---------- */
.credential-reveal__value {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  background: var(--bg-secondary);
  border: 1px dashed var(--border);
  border-radius: var(--radius-sm);
  padding: var(--space-3);
  text-align: center;
  font-size: 1.1rem;
  letter-spacing: 0.03em;
  margin: var(--space-3) 0;
  user-select: all;
}

/* ---------- Foto de agente ---------- */
.photo-field__label {
  display: block;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-secondary);
  margin-bottom: var(--space-2);
}

.photo-field__row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.photo-field__avatar {
  width: 72px;
  height: 72px;
  border-radius: 50%;
  overflow: hidden;
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.photo-field__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.photo-field__placeholder {
  font-size: 0.7rem;
  color: var(--text-secondary);
  text-align: center;
}

.photo-field__actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  align-items: flex-start;
}

.photo-field__hint {
  margin: 0;
  font-size: 0.75rem;
  color: var(--text-secondary);
}

.photo-field__error {
  margin: 0;
  font-size: 0.78rem;
  color: var(--danger);
}

/* ---------- Selector de persona existente ---------- */
.persona-picker {
  border: none;
  padding: 0;
  margin: 0 0 var(--space-3);
}

.persona-picker legend {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-secondary);
  padding: 0;
  margin-bottom: var(--space-1);
}

.radio-option {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 0.88rem;
  margin-bottom: var(--space-1);
}

.persona-picker__results {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-top: var(--space-1);
}

.persona-picker__result {
  text-align: left;
  border: 1px solid var(--border);
  background: var(--bg-primary);
  border-radius: var(--radius-sm);
  padding: var(--space-1) var(--space-2);
  cursor: pointer;
  font-size: 0.85rem;
}

.persona-picker__result:hover {
  background: var(--bg-secondary);
}

.persona-picker__selected {
  font-size: 0.85rem;
  color: var(--success);
  margin: var(--space-1) 0 0;
}

/* ---------- Modelo de evaluación: categorías/criterios ---------- */
.peso-resumen {
  font-size: 0.88rem;
  font-weight: 600;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  display: inline-block;
  margin-bottom: var(--space-3);
}

.peso-resumen--ok {
  background: var(--success-soft);
  color: var(--success);
}

.peso-resumen--warning {
  background: var(--warning-soft);
  color: var(--warning);
}

.modelo-vigencia {
  color: var(--text-secondary);
  font-size: 0.85rem;
  margin: 0 0 var(--space-2);
}

.modelo-estado-badge {
  display: inline-flex;
}

/* Fase 11.2.2 — bug sistémico corregido: usaba `--bg-app` (el MISMO
   tono del canvas detrás) — una card "se funde" con el fondo si
   comparte su color exacto, sin importar cuánta sombra tenga. Toda
   superficie NIVEL 1 (secundaria, "recesada" dentro de una card
   blanca) usa desde esta fase `--bg-surface-secondary`, un token
   dedicado — nunca vuelve a apuntar por accidente al canvas. */
.categoria-card {
  background: var(--bg-surface-secondary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  margin-bottom: var(--space-3);
  padding: var(--space-1) var(--space-4);
  transition: box-shadow var(--transition-fast), border-color var(--transition-fast);
}

.categoria-card[open] {
  border-color: var(--border-strong);
  box-shadow: var(--shadow-subtle);
}

.categoria-card summary {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  cursor: pointer;
  list-style: none;
  padding: var(--space-2) 0;
}

.categoria-card summary::-webkit-details-marker {
  display: none;
}

.categoria-card__nombre {
  font-weight: 600;
  flex: 1;
}

.categoria-card__codigo {
  color: var(--text-secondary);
  font-weight: 400;
  font-size: 0.8rem;
}

.categoria-card__peso {
  color: var(--text-secondary);
  font-size: 0.82rem;
  white-space: nowrap;
}

.categoria-card__peso--warning {
  color: var(--danger);
  font-weight: 600;
}

.categoria-card__body {
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
  margin-top: var(--space-2);
}

.criterio-list {
  list-style: none;
  margin: var(--space-2) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.criterio-list__item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  padding: var(--space-1) 0;
  font-size: 0.86rem;
}

.criterio-list__item > span:first-child {
  flex: 1;
  min-width: 200px;
}

.color-swatch {
  display: inline-block;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--swatch-color, transparent);
  border: 1px solid var(--border);
  vertical-align: middle;
}

.module-placeholder--inline {
  max-width: none;
  animation: none;
}

/* ---------- Responsive: tablas → cards en móvil ---------- */
@media (max-width: 640px) {
  .data-table thead {
    display: none;
  }

  .data-table,
  .data-table tbody,
  .data-table tr,
  .data-table td {
    display: block;
    width: 100%;
  }

  .data-table tr {
    border-bottom: 1px solid var(--border);
    padding: var(--space-2) 0;
  }

  .data-table td {
    border-bottom: none;
    padding: var(--space-1) 0;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--space-2);
  }

  .data-table td::before {
    content: attr(data-label);
    font-weight: 600;
    color: var(--text-secondary);
    font-size: 0.75rem;
    text-transform: uppercase;
    flex-shrink: 0;
  }

  .param-panel__toolbar {
    flex-direction: column;
    align-items: stretch;
  }

  .param-panel__new {
    margin-left: 0;
  }

  .param-panel__toolbar-actions {
    margin-left: 0;
  }
}

/* ==========================================================
   Fase 5 — Evaluaciones
   ========================================================== */

.eval-list-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-bottom: var(--space-5);
}

/* Microfase — AJUSTES EVALUACIONES + HOME + PALETA DASHBOARD,
   instrucción 1: sólo separación — las cards en sí (`.dash-kpi-card`,
   dentro de `.dash-kpi-grid--4`) son 100% las mismas de Dashboard, sin
   ningún CSS propio de Evaluaciones. */
.eval-kpis-wrap {
  margin-bottom: var(--space-4);
}

/* Ajuste visual puntual — EVALUACIONES + DASHBOARD, instrucción 1: los
   4 KPIs se mudan de su fila propia a la MISMA franja del título — una
   variante EXCLUSIVA de Evaluaciones (`.eval-list-header--con-kpis`,
   nunca la clase base `.eval-list-header`, compartida con Base de
   Datos/Exportar/Conexión — instrucción 3: "NO modificar Base de
   Datos"). Título | KPIs | botón, centrados verticalmente en una sola
   franja; en viewports angostos, `flex-wrap` ya heredado de
   `.eval-list-header` los pasa a varias líneas sin romper nada. */
.eval-list-header--con-kpis {
  align-items: center;
  /* Ajuste puntual — ALTURA KPIs + COMPACTAR ESPACIO, instrucción 2:
     la franja título+KPIs+botón ya es su propia unidad visual — el
     `margin-bottom` heredado de `.eval-list-header` (var(--space-5),
     40px) es el que empujaba los filtros lejos. Se reduce SÓLO en esta
     variante (nunca en la clase base, compartida con Base de
     Datos/Exportar/Conexión). */
  margin-bottom: 14px;
}

.eval-list-header__titles {
  flex: 0 1 auto;
  min-width: 200px;
}

.eval-list-header--con-kpis .eval-kpis-wrap {
  flex: 1 1 420px;
  min-width: 280px;
  margin-bottom: 0; /* ya no es una fila propia — instrucción 1: "eliminar el espacio vertical" */
  /* Ajuste rápido — EVALUACIONES, instrucción única: sube la fila de
     KPIs ~6-8px para aprovechar el espacio superior disponible — sólo
     el wrap de KPIs (título y botón, mismos hermanos de fila, no se
     tocan; no cambia alto/ancho de las cards). */
  margin-top: -7px;
}

/* Cards compactas: mismo lenguaje visual de Dashboard (`.dash-kpi-card`
   intacto — misma card blanca, mismo borde semántico, misma jerarquía
   tipográfica) — sólo se reducen tamaños para caber en la franja del
   título, nunca se cambia su estructura/clases. */
.eval-list-header--con-kpis .eval-kpis-wrap .dash-kpi-grid--4 {
  gap: var(--space-2);
}

/* Ajuste puntual — ALTURA KPIs + COMPACTAR ESPACIO, instrucción 1: las
   cards habían quedado demasiado bajas/achatadas (~64px) — se les
   devuelve "cuerpo" (72-82px) subiendo el padding vertical y el
   respiro interno entre label/valor/sub, sin tocar tamaños de fuente
   ("misma tipografía base") ni el ancho de columna. */
.eval-list-header--con-kpis .eval-kpis-wrap .dash-kpi-card {
  padding: 13px 12px;
  gap: 10px;
  align-items: center;
  border-left-width: 3px;
}

.eval-list-header--con-kpis .eval-kpis-wrap .dash-kpi-card__icon,
.eval-list-header--con-kpis .eval-kpis-wrap .dash-dona {
  width: 30px;
  height: 30px;
}

.eval-list-header--con-kpis .eval-kpis-wrap .dash-kpi-card__icon {
  padding: 6px;
}

.eval-list-header--con-kpis .eval-kpis-wrap .dash-kpi-card__body {
  gap: 3px;
}

.eval-list-header--con-kpis .eval-kpis-wrap .dash-kpi-card__label {
  font-size: calc(0.58rem + 3px);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.eval-list-header--con-kpis .eval-kpis-wrap .dash-kpi-card__valor {
  font-size: calc(1.15rem + 2px + 3px);
}

.eval-list-header--con-kpis .eval-kpis-wrap .dash-kpi-card__sub {
  font-size: calc(0.62rem + 3px);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Instrucción 2 — nota discreta bajo el listado: texto pequeño, gris
   secundario, sin card ni fondo, para no competir visualmente con la
   tabla. */
.eval-list-nota {
  margin: var(--space-2) 0 0;
  font-size: 0.8rem;
  color: var(--text-secondary);
}

/* Identidad del agente en el detalle de evaluación (instrucción 37). */
.eval-detail-identidad {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.eval-detail-identidad h1 {
  margin: 0 0 4px;
}

.eval-filtro-fecha-label {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-secondary);
}

/* Fase 11.2.1 — los `<input type="date">` de las bandas de filtro no
   tenían regla propia (quedaban con el estilo nativo del navegador,
   la causa real de "controles estilizados sólo superficialmente" /
   "no se sienten parte del mismo componente" — instrucción 12). */
.eval-filtro-fecha-label input[type="date"] {
  padding: var(--space-2) var(--space-3);
  border: 1.5px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--bg-app);
  color: var(--text-primary);
  font: inherit;
  font-size: 0.85rem;
}

.eval-filtro-fecha-label input[type="date"]:focus-visible {
  border-color: var(--accent-text);
  box-shadow: 0 0 0 3px var(--accent-soft);
  outline: none;
}

.nivel-badge {
  display: inline-flex;
  align-items: center;
  height: 22px;
  padding: 0 var(--space-3);
  border-radius: var(--radius-pill);
  font-size: 0.72rem;
  font-weight: 700;
  line-height: 1;
  white-space: nowrap;
  color: #fff;
  background: var(--nivel-color, #666);
}

.nivel-badge--lg {
  font-size: 1rem;
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-md);
}

/* ---------- Formulario de nueva evaluación ---------- */

.eval-form__modelo {
  color: var(--text-secondary);
  font-size: 0.85rem;
  margin: var(--space-2) 0 var(--space-4);
}

/* Fase 11.2.2 — cabecera Agente/Fecha lado a lado (antes 100% ancho
   cada una, instrucción 35) y aprovechamiento del ancho en desktop
   para las categorías (instrucción 32: grid de 2 columnas). */
.eval-form__cabecera {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: var(--space-4);
  align-items: start;
}

/* Fase 11.2.5, instrucción 23-24 — 2 bloques de identidad compactos
   lado a lado (agente evaluado + auditor actual), nunca cards enormes. */
.eval-identidades {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin: 0 0 var(--space-4);
}

.eval-agente-preview,
.eval-auditor-preview {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex: 1 1 240px;
  padding: var(--space-3) var(--space-4);
  background: var(--bg-surface-secondary);
  border-radius: var(--radius-lg);
}

.eval-agente-preview[hidden] {
  display: none;
}

.eval-agente-preview__info {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.eval-agente-preview__nombre {
  font-weight: 700;
  color: var(--text-primary);
}

.eval-agente-preview__meta {
  font-size: 0.8rem;
  color: var(--text-secondary);
}

.eval-form__categorias {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-4);
  align-items: start;
  margin-bottom: var(--space-4);
}

@media (max-width: 900px) {
  .eval-form__cabecera {
    grid-template-columns: 1fr;
  }
  .eval-form__categorias {
    grid-template-columns: 1fr;
  }
}

/* Fase 11.2.2 — NIVEL 2 real (card blanca elevada, instrucción 15) —
   antes `--bg-app`, idéntico al canvas. */
/* Fase 11.2.5, instrucción 7-10 — header de categoría con el mismo
   matiz amarillo que las tablas (antes gris, "se perdía contra el
   fondo"); la cabecera queda DENTRO de la card (`overflow:hidden` ya
   la recorta al radio), nunca una franja flotante aparte. Mismo
   patrón para Nueva Evaluación y Ver Evaluación — ambas usan esta
   misma clase, instrucción 8/36. */
.eval-categoria-group {
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  padding: 0;
  overflow: hidden;
  background: var(--bg-elevated);
  box-shadow: var(--shadow-card);
}

.eval-categoria-group legend,
.eval-categoria-group h3 {
  font-weight: 700;
  font-size: 0.95rem;
  padding: var(--space-3) var(--space-4);
  margin: 0;
  width: 100%;
  background: var(--bg-header-accent);
  color: var(--text-header-accent);
  border-bottom: 2px solid var(--border-header-accent);
}

.eval-categoria-group--readonly h3 {
  margin: 0;
}

.eval-criterio-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border-subtle);
  transition: background-color var(--transition-fast);
}

.eval-criterio-row:hover {
  background: var(--bg-secondary);
}

.eval-criterio-row:last-child {
  border-bottom: none;
}

.eval-criterio-row__texto {
  flex: 1 1 260px;
}

.eval-criterio-row__peso,
.eval-criterio-row__puntaje {
  color: var(--text-secondary);
  font-size: 0.8rem;
  white-space: nowrap;
}

.eval-criterio-row__respuesta {
  display: flex;
  gap: var(--space-2);
  white-space: nowrap;
}

.eval-criterio-row__respuesta label {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-weight: 500;
  padding: 5px var(--space-3) 5px var(--space-2);
  border-radius: var(--radius-pill);
  border: 1.5px solid var(--border-subtle);
  transition: border-color var(--transition-fast), background-color var(--transition-fast);
}

/* Ajuste visual puntual — semántica de color por respuesta (nunca un
   único acento neutro para "seleccionado"): Sí = success, No = danger,
   reutilizando los tokens semánticos ya existentes (nunca un color
   nuevo). Sólo el control seleccionado cambia — la fila no se pinta. */
.eval-criterio-row__respuesta label:has(input[value="SI"]:checked) {
  border-color: var(--success);
  background: var(--success-soft);
  font-weight: 700;
  color: var(--success);
}

.eval-criterio-row__respuesta label:has(input[value="NO"]:checked) {
  border-color: var(--danger);
  background: var(--danger-soft);
  font-weight: 700;
  color: var(--danger);
}

.eval-criterio-row__respuesta input[value="SI"]:checked {
  accent-color: var(--success);
}

.eval-criterio-row__respuesta input[value="NO"]:checked {
  accent-color: var(--danger);
}

.eval-form__resumen {
  font-weight: 700;
  margin: var(--space-4) 0;
  padding: var(--space-3) var(--space-4);
  background: var(--accent-soft);
  border-radius: var(--radius-md);
  color: var(--brand-primary);
}

/* ---------- Detalle de evaluación ---------- */

/* Fase 11.2.2 — "datos generales" como card blanca claramente elevada
   (instrucción 13) — antes `--bg-app`, se fundía con el canvas. */
.eval-detail-meta {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-4);
  background: var(--bg-elevated);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-subtle);
  padding: var(--space-4);
  margin-bottom: var(--space-4);
}

.eval-detail-meta__label {
  display: block;
  font-size: 0.72rem;
  text-transform: uppercase;
  color: var(--text-secondary);
  margin-bottom: 2px;
}

/* Identidad del auditor dentro del grid de datos generales
   (instrucción 25) — avatar pequeño en línea, nunca escondido en la
   tabla de criterios. */
.eval-detail-meta__auditor {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

/* Fase 11.2.2 — resultado con mayor jerarquía (instrucción 14): card
   blanca real (antes `--bg-app`, texto plano sobre el canvas) + acento
   de borde con el MISMO color semántico del nivel de desempeño
   (`--nivel-color`, inline desde detailView.js — nunca un color nuevo). */
.eval-resultado-banner {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  border: none;
  border-left: 5px solid var(--nivel-color, var(--border-strong));
  border-radius: var(--radius-lg);
  background: var(--bg-elevated);
  box-shadow: var(--shadow-card);
  margin-bottom: var(--space-4);
}

.eval-resultado-banner__puntaje {
  font-size: 2rem;
  font-weight: 800;
}

.eval-resultado-banner__puntaje span {
  font-size: 1rem;
  font-weight: 400;
  color: var(--text-secondary);
}

.eval-observacion {
  margin: 0 0 var(--space-4);
}

.eval-observacion--anulada {
  color: var(--danger);
}

/* Fase 11.2.4, instrucción 2-8 — VER EVALUACIÓN sigue la MISMA
   filosofía de NUEVA EVALUACIÓN: grid de 2 columnas en desktop (misma
   regla que `.eval-form__categorias`, mismo breakpoint 900px), nunca
   una columna larga. 0 información oculta — sólo se reordena. */
.eval-respuestas {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-4);
  align-items: start;
  margin-bottom: var(--space-4);
}

@media (max-width: 900px) {
  .eval-respuestas {
    grid-template-columns: 1fr;
  }
}

/* Fila de criterio compacta en modo lectura: Pregunta (jerarquía
   principal) | peso | resultado | puntaje — grid en vez de flex-wrap,
   nunca se desordena al reducir el ancho de la columna. */
.eval-criterio-row--readonly {
  display: grid;
  grid-template-columns: 1fr auto auto auto;
  align-items: center;
  gap: var(--space-2) var(--space-3);
}

.eval-criterio-row--readonly .eval-criterio-row__texto {
  font-weight: 600;
  color: var(--text-primary);
}

.eval-criterio-row--readonly .eval-criterio-row__puntaje {
  font-weight: 700;
  color: var(--text-primary);
  white-space: nowrap;
}

@media (max-width: 640px) {
  .eval-criterio-row {
    flex-direction: column;
    align-items: flex-start;
  }
}

/* ==========================================================
   Fase 7 — Base de Datos
   ========================================================== */

.data-table__sort-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  font-weight: inherit;
  text-transform: inherit;
  letter-spacing: inherit;
  color: inherit;
  cursor: pointer;
}

.data-table__sort-btn:hover {
  color: var(--accent);
}

.data-table__sort-arrow {
  font-size: 0.7em;
  opacity: 0.7;
}

.table-error {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  color: var(--text-secondary);
  padding: var(--space-2xl) var(--space-4);
  text-align: center;
  background: var(--bg-app);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-lg);
}

.table-error__message {
  color: var(--danger);
  font-weight: 600;
}

.bd-toolbar-row {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
  align-items: center;
  margin-bottom: var(--space-3);
}

.bd-summary {
  color: var(--text-secondary);
  font-size: 0.8rem;
  font-weight: 600;
  margin: 0 0 var(--space-3);
}

.bd-agente-cell {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.bd-pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  margin-top: var(--space-3);
  flex-wrap: wrap;
}

.bd-pagination__info {
  color: var(--text-secondary);
  font-size: 0.82rem;
}

.bd-load-more {
  display: flex;
  justify-content: center;
  margin-top: var(--space-3);
}

/* ==========================================================
   Fase 8 — Dashboard
   ========================================================== */

/* Ajuste rápido — DASHBOARD: bloque fijo = título + filtros;
   scrolleable = KPIs + gráficos. 2 bloques reales (`flex:0 0 auto` +
   `flex:1 1 auto`), sin `position:sticky` — el bloque de arriba vive
   FUERA del contenedor de scroll, así que no compite con nada. */
.dash-shell {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
}

.dash-header-fixed {
  flex: 0 0 auto;
  margin-bottom: var(--space-4);
}

.dash-charts-scroll {
  flex: 1 1 auto;
  min-height: 220px;
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-gutter: stable;
  padding-right: var(--space-2);
}

.dash-filtros-wrap {
  margin-bottom: 0;
}

/* Fase 11.3A — franja de filtros reconstruida estructuralmente
   (instrucción 1): TODOS los grupos (Año/Mes/Asesor/Auditor/Nivel)
   comparten ahora la MISMA forma (label arriba + control abajo,
   `.dash-filtro-grupo`) — "Limpiar filtros" recibe un label invisible
   del mismo alto para quedar en la misma fila. `align-items:flex-end`
   (en vez del `center` genérico de `.param-panel__toolbar`) alinea el
   borde inferior de todos los controles en una sola línea, sin ningún
   `margin-top` adivinado. Multiselección/faceted filtering/periodos no
   consecutivos/OR-AND: sin cambios, es el mismo `filtros.js`. */
.dash-filtros {
  align-items: flex-end;
  row-gap: var(--space-3);
}

/* Fase 11.4A — responsive: `.dash-filtros` (Dashboard Y Alertas, mismo
   componente) hereda `align-items:flex-end` de la regla de arriba,
   pensada sólo para la fila horizontal de escritorio — sin este
   override, al apilarse en columna (`.param-panel__toolbar{flex-direction:
   column}` más abajo, `max-width:640px`) ese `flex-end` pasa a operar
   sobre el eje CRUZADO ahora horizontal y empuja cada grupo de filtro
   contra el borde derecho, dejando un hueco vacío a la izquierda en vez
   de ocupar el ancho completo. Mismo breakpoint que ya usa el resto de
   `.param-panel__toolbar` — no se inventa uno nuevo. */
@media (max-width: 640px) {
  .dash-filtros {
    align-items: stretch;
  }
}

.dash-filtros select {
  padding: var(--space-2) var(--space-3);
  border: 1.5px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--bg-app);
  color: var(--text-primary);
  font: inherit;
  height: 38px;
}

/* El multiselect (Año/Mes/Asesor/Auditor) seguía con el estilo "sólo
   funcional" de Fase 10.4 (radio/borde propios, distintos del resto
   del sistema) — la causa real, junto con el `align-items` de arriba,
   de que la franja se viera desalineada. Ajustado SÓLO dentro de
   `.dash-shell` (Dashboard) para no generar ningún cambio visible en
   Reportes, que reutiliza el mismo componente `multiSelect.js` con su
   propio wrapper (`.reporte-shell`, sin tocar). */
.dash-shell .multiselect {
  width: 100%;
}

.dash-shell .multiselect__button {
  height: 38px;
  border: 1.5px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--bg-app);
}

.dash-shell .multiselect__panel {
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-elevated);
}

/* Fase 10.4 — envoltorio de cada multiselect del Dashboard/Reportes, con su etiqueta encima (mismo patrón visual que .form-field, sin rediseñar). */
.dash-filtro-grupo {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 150px;
}

.dash-filtro-grupo__label {
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-secondary);
}

.dash-filtro-grupo--accion {
  min-width: 0;
}

.dash-agente-seleccionado__card {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-4) var(--space-2) var(--space-2);
  margin-bottom: var(--space-3);
  background: var(--bg-elevated);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-subtle);
  width: fit-content;
}

.dash-agente-avatar {
  display: inline-flex;
  width: 36px;
  height: 36px;
  flex-shrink: 0;
}

.dash-agente-avatar__img,
.dash-agente-avatar__placeholder {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  object-fit: cover;
}

.dash-agente-avatar__placeholder {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--accent);
  color: var(--text-inverse);
  font-weight: 700;
}

.dash-agente-seleccionado__nombre {
  font-weight: 600;
}

.dash-ver-bd-btn {
  margin-left: var(--space-2);
}

/* ---------- Tarjetas KPI ---------- */

.dash-kpis-wrap {
  margin-bottom: var(--space-4);
}

.dash-kpi-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: var(--space-4);
}

/* Microfase — AJUSTES EVALUACIONES + HOME + PALETA DASHBOARD,
   instrucción 1: Evaluaciones reutiliza `.dash-kpi-card` tal cual (cero
   CSS nuevo por card) — sólo el contenedor necesita 4 columnas en vez
   de 5, porque no incluye "Cobertura de asesores". */
.dash-kpi-grid--4 {
  grid-template-columns: repeat(4, 1fr);
}

/* Fase 11.3A — tarjeta KPI premium: card blanca real (antes
   `--bg-primary`/`--border` planos, sin sombra), radio/sombra del
   sistema global, ícono en chip circular navy, valor con más peso. El
   borde izquierdo semántico (verde/rojo/neutro contra la meta) se
   conserva — instrucción 2, nunca se pierde el semáforo. */
.dash-kpi-card {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-4);
  background: var(--bg-elevated);
  border: none;
  border-left: 4px solid var(--border-strong);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  min-width: 0;
  transition: box-shadow var(--transition-fast), transform var(--transition-fast);
}

.dash-kpi-card:hover {
  box-shadow: var(--shadow-elevated);
  transform: translateY(-1px);
}

.dash-kpi-card--ok {
  border-left-color: var(--success);
}

.dash-kpi-card--alerta {
  border-left-color: var(--danger);
}

.dash-kpi-card--neutro {
  border-left-color: var(--border-strong);
}

.dash-kpi-card__icon {
  flex-shrink: 0;
  width: 38px;
  height: 38px;
  padding: 8px;
  border-radius: var(--radius-md);
  background: var(--accent-soft);
  color: var(--brand-primary);
  box-sizing: border-box;
}

.dash-kpi-card__icon svg {
  width: 100%;
  height: 100%;
}

.dash-kpi-card__icon--dona {
  position: relative;
  background: transparent;
  padding: 0;
}

.dash-dona {
  width: 38px;
  height: 38px;
}

.dash-kpi-card__body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.dash-kpi-card__label {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-secondary);
  font-weight: 700;
}

.dash-kpi-card__valor {
  font-size: 1.9rem;
  font-weight: 800;
  line-height: 1.15;
  color: var(--text-primary);
}

.dash-kpi-card__sub {
  font-size: 0.74rem;
  color: var(--text-secondary);
  white-space: normal;
}

/* ---------- Gráficos ---------- */

/* Ajuste visual puntual — 3 gráficos por fila en desktop (antes 2) para
   reducir el scroll vertical del Dashboard; cae a 2 columnas antes de
   volverse ilegible (≤1450px) y a 1 columna en el breakpoint
   estructural ya existente (≤1100px). */
/* Ajuste visual puntual — 3 columnas verticales INDEPENDIENTES (flex,
   nunca CSS Grid con filas): antes cada FILA de grid forzaba a todas
   sus cards a la altura de la más alta de esa fila, dejando huecos
   grandes bajo las cards más cortas. Ahora cada `.dash-charts-col` es
   su propia pila (`flex-direction:column`) sin relación de altura con
   las otras — el reparto (izquierda = cards altas, centro/derecha =
   estándar) lo decide `dashboard.js`, no el CSS. */
.dash-charts-grid {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 20px; /* ajuste final puntual — gap fijo 20px (preferido; máx. 24px), antes var(--space-4)=24px */
  transition: opacity var(--transition-fast);
}

.dash-charts-col {
  display: flex;
  flex-direction: column;
  gap: 20px; /* ajuste final puntual — mismo gap fijo entre cards dentro de cada pila */
  flex: 1 1 0;
  min-width: 0;
}

@media (max-width: 1450px) {
  /* 3 columnas siguen existiendo en el DOM (mismo reparto
     izquierda/centro/derecha) — a este ancho sólo caben 2 por fila;
     la tercera pasa a una fila propia debajo, sigue siendo su misma
     pila independiente, nunca reagrupada por CSS Grid. */
  .dash-charts-col {
    flex-basis: calc(50% - 10px);
  }
}

.dash-charts-grid--cargando {
  opacity: 0.5;
}

/* Fase 11.3A, instrucción 4 — misma card premium que el resto del
   sistema (antes plana: `--bg-primary`/`--border`, sin sombra). */
.dash-chart-card {
  background: var(--bg-elevated);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
}

.dash-chart-card__titulo {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 1rem;
  font-weight: 700;
  color: var(--text-primary);
}

/* Ajuste rápido — DASHBOARD, instrucción 2: ⓘ de ayuda por gráfico —
   tooltip propio (nunca el `title` nativo, que no se puede estilar),
   compacto y superpuesto (`position:absolute`) — nunca reserva
   espacio ni desplaza el layout de la card, y al vivir aparte del
   `canvas-wrap` no interfiere con el hover/click del propio gráfico. */
.dash-chart-card__info {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  font-size: 0.72rem;
  font-weight: 600;
  line-height: 1;
  color: var(--text-muted);
  cursor: help;
  flex-shrink: 0;
}

.dash-chart-card__info:hover,
.dash-chart-card__info:focus-visible {
  color: var(--brand-primary);
  outline: none;
}

.dash-chart-card__info-tooltip {
  position: absolute;
  z-index: 20;
  top: calc(100% + 8px);
  left: 0;
  width: max-content;
  max-width: 220px;
  padding: 7px 11px;
  border-radius: var(--radius-md);
  background: var(--brand-primary);
  color: #ffffff;
  font-size: 0.74rem;
  font-weight: 500;
  line-height: 1.4;
  text-align: left;
  box-shadow: var(--shadow-elevated);
  opacity: 0;
  visibility: hidden;
  transform: translateY(2px);
  transition: opacity 160ms ease, transform 160ms ease, visibility 160ms;
  pointer-events: none;
}

.dash-chart-card__info:hover .dash-chart-card__info-tooltip,
.dash-chart-card__info:focus-visible .dash-chart-card__info-tooltip {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.dash-chart-card__nota {
  margin: 2px 0 var(--space-3);
  font-size: 0.78rem;
  color: var(--text-secondary);
}

/* Ajuste visual puntual — alto reducido ~15% (240px→204px) para
   compactar el Dashboard. */
.dash-chart-card__canvas-wrap {
  position: relative;
  height: 204px;
}

/* Fase 8.1 — "Top de ítems con mayor oportunidad de mejora": 10 barras
   horizontales con texto necesitan más alto que el resto para no
   apretarse. Fase 11.3A — "Top asesores" (ahora ranking con avatar)
   necesita el mismo alto extra por la misma razón: 10 filas. */
.dash-chart-card__canvas-wrap[data-chart="itemsOportunidad"],
.dash-chart-card__canvas-wrap[data-chart="topAsesores"] {
  height: 290px; /* ~15% menos que 340px */
}

/* Ajuste puntual — alinear el borde inferior de "Comparación por
   categoría"/"Consistencia por asesor" (columnas centro/derecha) con
   "Cumplimiento por categoría" (columna izquierda), reportado con
   captura real. Un intento previo quitó el alto extra por completo
   (204px = el mismo que Cumplimiento) asumiendo que "mismo alto de
   card = mismo borde inferior" — FALSO aquí: cada columna es su
   propia pila independiente (distribuirEnColumnas(), dashboard.js) y
   la columna izquierda acumula más alto ANTES de esta fila (2 cards
   altas encima) que centro/derecha (2 cards estándar encima) — un
   ~205px de diferencia real, medida con getBoundingClientRect()
   contra el render actual, no asumida. El alto que sí iguala el
   borde inferior real de hoy es ~410px (204px base + esos ~205px de
   diferencia de arranque entre columnas). */
.dash-chart-card__canvas-wrap[data-chart="radar"],
.dash-chart-card__canvas-wrap[data-chart="consistencia"] {
  height: 389px; /* ajuste rápido: -10px, -5px, -2px, -1px, -1px más */
}

.dash-chart-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  color: var(--text-secondary);
  font-size: 0.85rem;
  text-align: center;
  padding: var(--space-3);
}

/* Fase 11.3A, instrucciones 6-7 — "Top asesores": ranking real con
   avatar (antes barras de Chart.js sin foto). Ocupa el mismo
   `.dash-chart-card__canvas-wrap`; scroll propio si excede el alto de
   la card (nunca desborda el layout). */
.dash-ranking {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  height: 100%;
  overflow-y: auto;
}

.dash-ranking__item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-2) var(--space-2) 0;
  border-bottom: 1px solid var(--border-subtle);
}

.dash-ranking__item:last-child {
  border-bottom: none;
}

.dash-ranking__pos {
  flex-shrink: 0;
  width: 22px;
  text-align: center;
  font-size: 0.78rem;
  font-weight: 800;
  color: var(--text-muted);
}

.dash-ranking__item:nth-child(1) .dash-ranking__pos {
  color: var(--brand-accent-ink, #7a5b00);
}

.dash-ranking__avatar {
  flex-shrink: 0;
}

.dash-ranking__info {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
  flex: 1;
}

.dash-ranking__nombre {
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.dash-ranking__meta {
  font-size: 0.72rem;
  color: var(--text-secondary);
}

.dash-ranking__bar {
  display: block;
  height: 5px;
  border-radius: var(--radius-pill);
  background: var(--bg-surface-secondary);
  overflow: hidden;
}

.dash-ranking__bar-fill {
  display: block;
  height: 100%;
  border-radius: var(--radius-pill);
  background: linear-gradient(90deg, var(--brand-primary-light, #304071), var(--brand-primary));
}

.dash-ranking__valor {
  flex-shrink: 0;
  font-size: 1.05rem;
  font-weight: 800;
  color: var(--brand-primary);
}

@media (max-width: 1100px) {
  .dash-kpi-grid {
    grid-template-columns: repeat(3, 1fr);
  }
  .dash-charts-col {
    flex-basis: 100%;
  }
}

@media (max-width: 640px) {
  .dash-kpi-grid {
    grid-template-columns: 1fr 1fr;
  }
  .dash-kpi-card {
    padding: var(--space-2);
  }
  .dash-kpi-card__valor {
    font-size: 1.2rem;
  }
  .dash-chart-card__canvas-wrap {
    height: 170px; /* ~15% menos que 200px */
  }
  .dash-chart-card__canvas-wrap[data-chart="itemsOportunidad"],
  .dash-chart-card__canvas-wrap[data-chart="topAsesores"] {
    height: 255px; /* ~15% menos que 300px */
  }
}

@media (max-width: 420px) {
  .dash-kpi-grid {
    grid-template-columns: 1fr;
  }
}

/* ==========================================================
   Fase 9 — Módulo Alertas
   ========================================================== */

/* Ajuste puntual — ALERTAS STICKY + COMPACTACIÓN SUPERIOR, instrucción
   1: título+ayuda+filtros+tabs+KPIs quedan pegados arriba mientras se
   recorre la tabla — un solo `position:sticky` integrado al flujo
   normal de `#module-outlet` (el mismo contenedor que YA hace scroll
   para cualquier módulo, `overflow-y:auto` — nunca `position:fixed`
   independiente, nunca un contenedor de scroll nuevo). El fondo opaco
   evita que las filas ya desplazadas se transparenten debajo. */
.alert-sticky-top {
  position: sticky;
  top: 0;
  z-index: 6;
  background: var(--bg-app);
  padding-top: 0; /* evita que el primer px del título se recorte contra el borde superior al pegarse */
}

/* Instrucción 3 — título+subtítulo y el acceso de ayuda en la misma
   fila cuando hay espacio (desktop); el toggle es compacto por
   defecto. Cuando `<details>` se abre, el panel entero pasa a ocupar
   el 100% del ancho (se "cae" a su propia línea bajo el título) para
   no forzar el texto largo del panel dentro de una columna angosta —
   nunca cambia su contenido, sólo cuánto espacio ocupa mientras está
   abierto (instrucción 3: "puede aumentar temporalmente la altura"). */
.alert-header-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-bottom: 12px; /* instrucción 5: título/ayuda → filtros, 12-16px */
}

.alert-header-row .eval-list-header__titles {
  flex: 1 1 auto;
  min-width: 200px;
}

.alert-header-row .alert-help {
  flex: 0 0 auto;
  margin-bottom: 0;
}

.alert-header-row .alert-help[open] {
  flex: 1 0 100%;
}

/* Ajuste puntual — ALERTAS STICKY + COMPACTACIÓN SUPERIOR, instrucción
   4/5: `.param-shell__subtitle` es una regla COMPARTIDA (Evaluaciones/
   Base de Datos/Exportar/Conexión también la usan) — nunca se edita
   directamente (regresión en esas pantallas); se sobreescribe sólo
   dentro de `.alert-header-row`, el único lugar donde el bloque
   superior sticky necesita el título compacto. El margen inferior de
   24px (`var(--space-4)`) era, medido, el mayor contribuyente aislado
   a la altura total del bloque sticky. */
.alert-header-row .param-shell__subtitle {
  margin-bottom: 0;
}

.alert-header-row .eval-list-header__titles h1 {
  margin-bottom: 0;
}

/* Microfase — DOCUMENTAR LÓGICA DEL MÓDULO ALERTAS, instrucción 1:
   panel de ayuda expandible — acordeón nativo `<details>`, mismo patrón
   ya usado en Reportes (`.reporte-criterio-categoria`, "sin rediseñar")
   — nunca un modal grande, se abre/cierra con un clic sin JS propio. */
.alert-help {
  margin-bottom: var(--space-4);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  background: var(--bg-elevated);
  /* El toggle (más abajo) pasa a tener su propio fondo navy — overflow
     hidden recorta ese fondo a las esquinas redondeadas reales de
     esta tarjeta (mismo radio, `--radius-lg`), sin que el toggle
     necesite declarar exactamente el mismo valor para verse bien
     encajado — mismo truco ya usado en `<thead>` de las tablas. */
  overflow: hidden;
}

/* Ajuste rápido — BOTÓN AYUDA ALERTAS: mismo énfasis visual que
   `.btn--primary` (botones destacados del resto de la app) —
   reutilizado tal cual, nunca un estilo nuevo: fondo/color/sombra/
   hover/transición/peso tipográfico calcados de esa clase. Tamaño,
   posición y comportamiento expandir/contraer sin cambios. */
.alert-help__toggle {
  display: flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  list-style: none;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--text-inverse);
  background: var(--accent);
  box-shadow: 0 4px 12px rgba(32, 41, 75, 0.28);
  transition: background-color var(--transition-fast), transform var(--transition-fast), box-shadow var(--transition-fast);
  user-select: none;
}

.alert-help__toggle:hover {
  background: var(--accent-hover);
  transform: translateY(-1px);
  box-shadow: 0 6px 16px rgba(32, 41, 75, 0.34);
}

.alert-help__toggle::-webkit-details-marker {
  display: none;
}

.alert-help__toggle::after {
  content: "▸";
  margin-left: auto;
  color: rgba(255, 255, 255, 0.75);
  transition: transform var(--transition-fast);
}

.alert-help[open] .alert-help__toggle::after {
  transform: rotate(90deg);
}

.alert-help__icon {
  font-size: 1rem;
  line-height: 1;
}

.alert-help__panel {
  padding: 0 var(--space-4) var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.alert-help__titulo {
  margin: 0;
  font-size: 1rem;
  font-weight: 700;
  color: var(--text-primary);
}

.alert-help__bloque h3 {
  margin: 0 0 4px;
  font-size: 0.8rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--brand-primary);
}

.alert-help__bloque ul {
  margin: 0;
  padding-left: 18px;
  font-size: 0.84rem;
  line-height: 1.5;
  color: var(--text-secondary);
}

.alert-help__bloque li + li {
  margin-top: 3px;
}

/* Copy revisada (bloques Clasificación/Reincidencia/Indicadores
   críticos/Periodo de análisis pasaron de <ul> a <p> — misma tipografía
   que las listas, para que el panel se lea como una sola voz). */
.alert-help__bloque p {
  margin: 0;
  font-size: 0.84rem;
  line-height: 1.5;
  color: var(--text-secondary);
}

.alert-help__bloque p strong {
  color: var(--text-primary);
}

.alert-help__bloque > * + * {
  margin-top: 6px;
}

/* Instrucción 2 — icono de ayuda discreto en headers de tabla.
   Ajuste Fase 12A, instrucción 6 — mismo mecanismo YA accesible de
   Dashboard (`.dash-chart-card__info`, arriba): tooltip propio en vez
   de `title` nativo (no operable por teclado), visible también en
   `:focus-visible`, nunca sólo con `:hover`. */
.alert-th-info {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-left: 2px;
  color: rgba(255, 255, 255, 0.6);
  cursor: help;
  font-size: 0.85em;
}

.alert-th-info:hover,
.alert-th-info:focus-visible {
  color: #ffffff;
  outline: none;
}

.alert-th-info__tooltip {
  position: absolute;
  z-index: 20;
  top: calc(100% + 8px);
  left: 50%;
  width: max-content;
  max-width: 240px;
  padding: 7px 11px;
  border-radius: var(--radius-md);
  background: var(--brand-primary);
  color: #ffffff;
  font-size: 0.74rem;
  font-weight: 500;
  text-transform: none;
  letter-spacing: normal;
  line-height: 1.4;
  text-align: left;
  box-shadow: var(--shadow-elevated);
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, 2px);
  transition: opacity 160ms ease, transform 160ms ease, visibility 160ms;
  pointer-events: none;
}

.alert-th-info:hover .alert-th-info__tooltip,
.alert-th-info:focus-visible .alert-th-info__tooltip {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
}

.alert-filtros-wrap {
  margin-bottom: 8px; /* instrucción 5: tabs → KPIs, 8-12px */
}

/* `.param-panel__toolbar` es una regla COMPARTIDA (Evaluaciones/Base de
   Datos/Dashboard/Reportes/Exportar/Conexión también la usan como
   "toolbar card" suelta) — nunca se edita directamente. Sobreescrita
   sólo dentro de `.alert-filtros-wrap`: `margin-bottom:0` evita que el
   propio toolbar sume su margen por defecto (var(--space-4)=24px) al
   de `.alert-filtros-wrap` (8px, arriba) — los chips ya viven DENTRO
   del toolbar (ajuste rápido — FILTROS DE ESTADO EN ALERTAS), así que
   el único espaciador filtros→tabs es el de `.alert-filtros-wrap`.
   Ajuste rápido — PANEL DE FILTROS ALERTAS: el padding vertical del
   panel (antes comprimido a 1px arriba/abajo para el presupuesto de
   alto del sticky) vuelve al mismo `var(--space-3)`=16px que ya usa
   Evaluaciones sin ningún override propio — misma "respiración" entre
   los controles y el borde exterior en ambos módulos. El padding
   lateral (`var(--space-4)`=24px), borde, radio y sombra ya eran los
   mismos heredados de la regla compartida, sin cambios. El padding
   interno de cada select/input (más abajo) NO se toca — su tamaño
   sigue igual, instrucción explícita. */
.alert-filtros-wrap .param-panel__toolbar {
  margin-bottom: 0;
}

.alert-filtros-wrap .param-panel__toolbar select,
.alert-filtros-wrap .param-panel__toolbar input[type="search"],
.alert-filtros-wrap .param-panel__toolbar input[type="date"] {
  padding-top: 0;
  padding-bottom: 0;
}

/* Ajuste rápido — FILTROS DE ESTADO EN ALERTAS: `.alert-chips` se
   movió DENTRO de `.param-panel__toolbar.dash-filtros` (mismo panel
   horizontal de Año/Mes/Asesor/Auditor/Nivel/Limpiar filtros, ya no una
   fila separada debajo) — el `margin-top` que antes separaba esa fila
   ya no aplica; el `gap` del propio `.param-panel__toolbar` deja el
   espacio entre "Nivel" y los chips. `.dash-filtros` alinea el resto
   de la fila (selects/botón) por su base (`align-items:flex-end`) —
   ajuste rápido posterior: los chips (24px, más bajos que los selects
   de 38px) se centran verticalmente respecto a esos vecinos con
   `align-self:center`, sólo para este ítem del flex row, sin tocar la
   alineación de los demás controles. */
.alert-chips {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
  align-self: center;
}

/* Chips de filtro (instrucción 10/12) — misma altura/radius/peso que
   `.badge`, nunca un estilo aparte. */
.alert-chip {
  display: inline-flex;
  align-items: center;
  height: 24px;
  border: 1.5px solid var(--border-subtle);
  background: var(--bg-elevated);
  color: var(--text-secondary);
  border-radius: var(--radius-pill);
  padding: 0 var(--space-3);
  font-size: 0.8rem;
  font-weight: 600;
  cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
}

.alert-chip:hover {
  background: var(--bg-secondary);
  border-color: var(--border-strong);
}

.alert-chip--active {
  background: var(--brand-primary);
  border-color: var(--brand-primary);
  color: #fff;
}

.alert-resumen-wrap {
  margin-bottom: 14px; /* instrucción 5: KPIs → tabla, 12-16px */
}

.alert-resumen-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-3);
}

/* Fase 11.2.1 — bug real corregido: usaban `--bg-app`, el MISMO tono
   que el canvas detrás — por eso "los KPIs se pierden sobre el fondo".
   Card elevada real, como el resto del sistema (NIVEL 2).
   Ajuste puntual — ALERTAS STICKY + COMPACTACIÓN, instrucción 4:
   padding reducido (24px→14px/16px) para bajar la altura a ~70-78px —
   mismo diseño/colores/valores, sin tocar tipografía. */
.alert-resumen-card {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px 16px;
  background: var(--bg-elevated);
  border: 1px solid var(--border-subtle);
  border-left: 4px solid var(--border-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-subtle);
  transition: box-shadow var(--transition-fast), transform var(--transition-fast);
}

.alert-resumen-card:hover {
  box-shadow: var(--shadow-card);
  transform: translateY(-1px);
}

.alert-resumen-card--critical {
  border-left-color: var(--danger);
}

.alert-resumen-card--watch {
  border-left-color: var(--warning);
}

.alert-resumen-card--normal {
  border-left-color: var(--success);
}

.alert-resumen-card__valor {
  font-size: 1.6rem;
  font-weight: 800;
  line-height: 1.2;
}

.alert-resumen-card__label {
  font-size: 0.75rem;
  color: var(--text-secondary);
  font-weight: 600;
}

/* Fase 11.2.1 — Alertas construye su tabla con HTML crudo (no pasa por
   dataTable.js), así que necesita su propia regla de superficie NIVEL 2
   — mismo tratamiento que `.data-table-wrapper` (instrucción 18: "la
   tabla/listado carece de una superficie suficientemente clara"). */
/* Ajuste puntual — ALERTAS STICKY + COMPACTACIÓN SUPERIOR, instrucción
   6/7: en vez de dejar que la tabla crezca sin límite y depender de que
   `#module-outlet` (la página completa) se desplace, `.alert-tabla-scroll`
   pasa a ser su propio contenedor de scroll ACOTADO (mismo patrón ya
   probado en Evaluaciones/Base de Datos — `.table-scroll-area`): el
   header de la tabla usa `position:sticky;top:0` relativo a ESTE
   contenedor (nunca a la página), así que nunca necesita conocer la
   altura del bloque superior — sólo las FILAS se desplazan "por
   debajo" (instrucción 7), un único scroll real (nunca doble). El
   alto disponible se resta dinámicamente de la altura ya medida de
   `.alert-sticky-top` (`--alert-sticky-offset`) más el header/footer
   globales — se encoge solo si el panel de ayuda está abierto, sin
   volver a medir nada aparte. Piso de 180px (`max(...)` exterior): con
   el panel de ayuda abierto en una ventana baja, el cálculo puede dar
   negativo — un `max-height` negativo se recorta a 0 y la tabla
   desaparecería por completo; el piso deja siempre el header de
   columnas + un par de filas utilizables. */
.alert-tabla-scroll {
  overflow: auto;
  max-height: max(180px, min(620px, calc(100vh - var(--alert-sticky-offset, 260px) - 188px)));
  background: var(--bg-elevated);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-subtle);
}

.alert-tabla__row {
  cursor: pointer;
}

.alert-tabla__row:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

/* Ajuste rápido — HOVER/SELECCIÓN EN ALERTAS: mismo patrón exacto ya
   usado en Evaluaciones/Base de Datos (`.eval-list-table`/`.bd-table`,
   ver arriba) — gris neutro suave + barra vertical dorada de 4px en la
   primera celda, en vez del tinte navy genérico que sigue usando el
   resto de las tablas (Usuarios y roles/Parametrizables, sin tocar).
   Mismos valores exactos, ningún color nuevo. */
.alert-tabla tbody tr {
  transition: background-color 180ms ease;
}

.alert-tabla tbody tr:hover {
  background: var(--row-hover-bg);
  box-shadow: none;
}

.alert-tabla tbody tr:hover > td:first-child {
  box-shadow: inset 4px 0 0 #fcc509;
}

/* ---------- Insignias de nivel de alerta / tendencia — Fase 11.2.1:
   misma altura/padding que `.badge` (instrucción 10/11), nunca un
   tamaño aparte. ---------- */

.alert-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 22px;
  padding: 0 var(--space-2) 0 10px;
  border-radius: var(--radius-pill);
  font-size: 0.72rem;
  font-weight: 700;
  line-height: 1;
  white-space: nowrap;
}

.alert-badge__dot {
  font-size: 0.6rem;
}

.alert-badge--critical {
  background: var(--danger-soft);
  color: var(--danger);
}

.alert-badge--watch {
  background: var(--warning-soft, rgba(184, 134, 11, 0.14));
  color: var(--warning);
}

.alert-badge--normal {
  background: rgba(46, 125, 85, 0.12);
  color: var(--success);
}

.tendencia-badge {
  font-size: 0.78rem;
  font-weight: 600;
  white-space: nowrap;
}

.tendencia--mejora {
  color: var(--success);
}

.tendencia--deterioro {
  color: var(--danger);
}

.tendencia--estable {
  color: var(--text-secondary);
}

.tendencia--insuficiente {
  color: var(--text-secondary);
  font-style: italic;
}

.alert-brecha {
  display: block;
  font-size: 0.7rem;
  font-weight: 600;
}

.alert-brecha--ok {
  color: var(--success);
}

.alert-brecha--neg {
  color: var(--danger);
}

.alert-muted {
  color: var(--text-secondary);
  font-size: 0.76rem;
}

.alert-chip-mini {
  display: inline-block;
  background: var(--bg-secondary);
  color: var(--text-secondary);
  border-radius: var(--radius-sm);
  padding: 3px var(--space-2);
  font-size: 0.72rem;
  font-weight: 600;
  margin: 2px;
}

/* ---------- Modal de detalle de agente ---------- */

.alert-detalle__header {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}

.row-avatar--lg,
.row-avatar--lg .row-avatar__img,
.row-avatar--lg .row-avatar__placeholder {
  width: 56px;
  height: 56px;
  font-size: 1.3rem;
}

/* Fase 11.2.5 — variante compacta, avatar inline junto a texto (p.ej.
   auditor dentro de `.eval-detail-meta`). */
.row-avatar--sm,
.row-avatar--sm .row-avatar__img,
.row-avatar--sm .row-avatar__placeholder {
  width: 22px;
  height: 22px;
  font-size: 0.68rem;
}

.alert-detalle__nombre {
  margin: 0 0 4px;
}

.alert-detalle__subtitulo {
  margin: var(--space-3) 0 var(--space-2);
}

.alert-detalle__incumplimientos {
  margin: 0;
  padding-left: 1.2em;
}

.alert-detalle__incumplimientos li {
  margin-bottom: var(--space-1);
  font-size: 0.85rem;
}

.alert-detalle__acciones {
  margin-top: var(--space-3);
  flex-wrap: wrap;
}

@media (max-width: 900px) {
  .alert-resumen-grid {
    grid-template-columns: 1fr 1fr;
  }
}

@media (max-width: 640px) {
  .alert-resumen-grid {
    grid-template-columns: 1fr;
  }
  .alert-tabla th,
  .alert-tabla td {
    white-space: nowrap;
  }
}

/* ==========================================================
   Fase 10 — Módulos Reportes / Exportar
   ========================================================== */

/* Ajuste rápido — SCROLL/STICKY REPORTES: mismo patrón ya corregido
   en Dashboard — 2 bloques reales (`flex:0 0 auto` + `flex:1 1 auto`
   con `overflow-y:auto`), nunca `position:fixed` ni sticky individual
   sobre el contenido. `.reporte-header-fixed` (título+subtítulo+
   filtros+botón) reserva físicamente su alto en el layout — nunca
   flota ni se superpone. `.reporte-content-scroll` es el ÚNICO
   contenedor con scroll del módulo (el resultado del reporte, que
   puede crecer mucho una vez generado, vive ahí dentro).
   `scrollbar-gutter:stable` reserva el ancho real del scrollbar como
   espacio del layout, igual que en Dashboard — nunca se dibuja
   encima del contenido. */
.reporte-shell {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
}

.reporte-header-fixed {
  flex: 0 0 auto;
}

.reporte-content-scroll {
  flex: 1 1 auto;
  min-height: 220px;
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-gutter: stable;
  padding-right: var(--space-2);
}

.reporte-filtros-wrap {
  margin-bottom: var(--space-3);
}

.reporte-preview {
  background: var(--bg-primary);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-4);
}

.reporte-preview__header {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
  flex-wrap: wrap;
}

.reporte-preview__header .row-avatar {
  flex-shrink: 0;
}

.reporte-preview__nombre {
  margin: 0 0 4px;
}

.reporte-preview__header > div:nth-child(2) {
  flex: 1;
  min-width: 160px;
}

.reporte-kpi-grid {
  margin-bottom: var(--space-3);
}

.reporte-resumen-texto {
  background: var(--bg-secondary);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  font-size: 0.9rem;
  line-height: 1.5;
  margin: 0 0 var(--space-3);
}

.reporte-columnas {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}

.reporte-columnas h3,
.reporte-preview > h3 {
  font-size: 0.95rem;
  margin: 0 0 var(--space-2);
}

.reporte-columnas ul {
  margin: 0;
  padding-left: 1.2em;
  font-size: 0.85rem;
}

.reporte-columnas li {
  margin-bottom: var(--space-1);
}

.reporte-categorias {
  list-style: none;
  margin: 0 0 var(--space-3);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.reporte-categorias li {
  display: flex;
  justify-content: space-between;
  padding: 6px var(--space-2);
  background: var(--bg-secondary);
  border-radius: var(--radius-sm);
  font-size: 0.85rem;
}

.reporte-meta-nota {
  color: var(--text-secondary);
  font-size: 0.76rem;
  margin: 0;
}

/* Fase 10.4 — alcance de la selección + capítulo "Cumplimiento por criterio" (acordeón nativo <details>, sin rediseñar). */
.reporte-alcance {
  margin: 2px 0 0;
  color: var(--text-secondary);
  font-size: 0.82rem;
}

.reporte-reincidencia {
  margin: var(--space-3) 0;
}

.reporte-reincidencia ul {
  margin: 4px 0 0;
  padding-left: 18px;
  font-size: 0.85rem;
  color: var(--danger);
}

.reporte-criterio-wrap {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-bottom: var(--space-3);
}

.reporte-criterio-categoria {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
}

.reporte-criterio-categoria summary {
  display: flex;
  justify-content: space-between;
  cursor: pointer;
  font-weight: 600;
  list-style: none;
}

.reporte-criterio-categoria summary::-webkit-details-marker {
  display: none;
}

.reporte-criterio-categoria summary::before {
  content: "▸ ";
  color: var(--text-secondary);
}

.reporte-criterio-categoria[open] summary::before {
  content: "▾ ";
}

.reporte-criterio-categoria__pct {
  color: var(--accent-text);
}

.reporte-criterio-lista {
  list-style: none;
  margin: var(--space-2) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.reporte-criterio-lista li {
  display: flex;
  justify-content: space-between;
  gap: var(--space-2);
  padding: 4px 6px;
  font-size: 0.82rem;
  border-radius: var(--radius-sm);
}

.reporte-criterio-lista li:hover {
  background: var(--bg-secondary);
}

.exportar-resultado-wrap {
  margin-top: var(--space-2);
}

.exportar-preview {
  background: var(--bg-elevated);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-subtle);
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.exportar-aviso-parcial {
  background: var(--warning-soft, rgba(184, 134, 11, 0.14));
  color: var(--warning);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  font-size: 0.82rem;
  margin: 0;
}

.exportar-hojas-nota {
  color: var(--text-secondary);
  font-size: 0.82rem;
  margin: 0;
}

@media (max-width: 700px) {
  .reporte-columnas {
    grid-template-columns: 1fr;
  }
}

/* ==========================================================
   Fase 10.1 — Módulo Conexión
   ========================================================== */

.conexion-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px var(--space-3) 4px 8px;
  border-radius: var(--radius-pill);
  font-size: 0.76rem;
  font-weight: 700;
  margin-left: var(--space-1);
}

.conexion-badge::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
}

.conexion-badge--ok {
  background: var(--success-soft);
  color: var(--success);
}

.conexion-badge--error {
  background: var(--danger-soft);
  color: var(--danger);
}

/* Ajuste visual puntual — MÓDULO CONEXIÓN: reemplaza la lista de
   bullets (`.status-list`, ya no usada por este módulo — se conserva
   la regla por si algo más la referencia) por el mismo lenguaje visual
   de cards ya aprobado en el resto de la app. Cero dato/lógica nueva:
   sólo reorganiza los mismos 6 campos que ya devolvía
   `conexionApi.get()`. */

/* Instrucción 1 — acercar el contenido: `.eval-list-header` es una
   regla COMPARTIDA (Base de Datos/Exportar/Conexión) — nunca se toca
   directamente; se reduce sólo dentro de `.conexion-shell`. */
.conexion-shell .eval-list-header {
  margin-bottom: var(--space-3);
}

/* Ajuste puntual — MÓDULO CONEXIÓN (distribución + limpieza),
   instrucción 5: el ancho fijo (1180px) dejaba las cards concentradas
   en la mitad izquierda en pantallas anchas — quitado para que los 4
   KPIs y la card de estado aprovechen TODO el ancho útil del módulo,
   igual que Evaluaciones/Dashboard (ninguno de los dos limita el
   ancho de su contenido). */
.conexion-kpi-grid {
  margin-bottom: var(--space-4);
}

/* Instrucción 3 — una única card "Estado de la conexión", a todo el
   ancho disponible (antes compartía fila con "Integraciones
   retiradas", eliminada — instrucción 4). */
.conexion-card {
  background: var(--bg-elevated);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-subtle);
  padding: var(--space-4);
}

.conexion-card__titulo {
  margin: 0 0 var(--space-3);
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--text-primary);
}

/* Instrucción 3 — información organizada en una fila horizontal
   compacta (antes una lista vertical de filas label/valor), con
   divisores finos entre segmentos; el segmento de mensaje toma el
   espacio restante y se envuelve si el texto es largo. */
.conexion-estado-fila {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4);
}

.conexion-estado-item {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.85rem;
}

.conexion-estado-item--mensaje {
  flex: 1 1 260px;
  min-width: 0;
}

.conexion-estado-item__nombre {
  font-size: 0.95rem;
  color: var(--text-primary);
}

.conexion-estado-item__label {
  font-weight: 600;
  color: var(--text-secondary);
  white-space: nowrap;
}

.conexion-estado-item__valor {
  color: var(--text-primary);
  font-weight: 500;
  overflow-wrap: anywhere;
}

.conexion-row__valor--code {
  font-family: "SFMono-Regular", Consolas, monospace;
  font-size: 0.8rem;
  color: var(--brand-primary);
  background: var(--accent-soft);
  padding: 2px 8px;
  border-radius: var(--radius-sm);
}

.conexion-estado-divisor {
  width: 1px;
  align-self: stretch;
  min-height: 20px;
  background: var(--border-subtle);
}

.conexion-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--border-strong);
  flex-shrink: 0;
}

.conexion-dot--ok {
  background: var(--success);
}

.conexion-dot--error {
  background: var(--danger);
}

@media (max-width: 700px) {
  .conexion-estado-fila {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-3);
  }
  .conexion-estado-divisor {
    display: none;
  }
}

/* ==========================================================
   Fase 10.4 — Multiselección (control funcional, no premium —
   reutiliza los mismos tokens de color/radio/espaciado que el resto
   de la aplicación; el refinamiento visual queda para Fase 11)
   ========================================================== */

.multiselect {
  position: relative;
  display: inline-block;
  min-width: 160px;
  max-width: 100%;
}

.multiselect__button {
  width: 100%;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-primary);
  color: var(--text-primary);
  font: inherit;
  text-align: left;
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.multiselect__button:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

.multiselect__panel {
  position: absolute;
  z-index: 50;
  top: calc(100% + 4px);
  left: 0;
  min-width: 240px;
  max-width: min(320px, 90vw);
  max-height: 320px;
  overflow-y: auto;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: 0 8px 24px rgba(20, 22, 26, 0.12);
  padding: var(--space-2);
}

.multiselect__toolbar {
  display: flex;
  justify-content: space-between;
  gap: var(--space-2);
  padding-bottom: var(--space-2);
  margin-bottom: var(--space-2);
  border-bottom: 1px solid var(--border);
}

.multiselect__link {
  border: none;
  background: none;
  color: var(--accent-text);
  font: inherit;
  font-size: 0.85em;
  cursor: pointer;
  padding: 0;
}

.multiselect__link:hover,
.multiselect__link:focus-visible {
  text-decoration: underline;
}

.multiselect__list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.multiselect__option {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: 0.9em;
}

.multiselect__option:hover {
  background: var(--bg-secondary);
}

.multiselect__option input[type="checkbox"] {
  flex-shrink: 0;
}

.multiselect__option span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

@media (max-width: 480px) {
  .multiselect__panel {
    max-width: 92vw;
  }
}

/* ---------------------------------------------------------------------
   Reestructuración — Parametrizables → Importación histórica (reemplaza
   el placeholder "Migración inicial" de Fase 4). Reutiliza el lenguaje
   visual existente (.param-panel, .btn--primary/--ghost, .data-table,
   .badge--*, .form-field, .table-loading/-error/-empty) — sólo añade lo
   estrictamente nuevo: el indicador de 4 pasos, la zona de
   arrastrar/soltar y las tarjetas de resumen de validación.
   --------------------------------------------------------------------- */
.importacion-shell__header h2 {
  margin: 0 0 var(--space-1);
}

.importacion-shell__header p {
  margin: 0 0 var(--space-4);
  color: var(--text-secondary);
}

.form-field__hint {
  margin: 0;
  font-size: 0.8rem;
  color: var(--text-secondary);
}

/* Indicador "[1 Archivo] → [2 Mapeo] → [3 Validación] → [4 Importar]" — instrucción 12. */
.importacion-steps {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  list-style: none;
  margin: 0 0 var(--space-4);
  padding: 0;
  flex-wrap: wrap;
}

.importacion-step {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--bg-elevated);
  border: 1.5px solid var(--border-subtle);
  color: var(--text-secondary);
  font-weight: 600;
  font-size: 0.85rem;
}

.importacion-step:not(:last-child)::after {
  content: "→";
  margin-left: var(--space-2);
  color: var(--border-subtle);
}

.importacion-step__num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--bg-secondary);
  color: var(--text-secondary);
  font-size: 0.75rem;
}

.importacion-step--activo {
  border-color: var(--accent-text);
  color: var(--accent-text);
}

.importacion-step--activo .importacion-step__num {
  background: var(--accent);
  color: var(--text-inverse);
}

.importacion-step--completo {
  color: var(--success);
}

.importacion-step--completo .importacion-step__num {
  background: var(--success-soft);
  color: var(--success);
}

.importacion-step-panel {
  background: var(--bg-elevated);
  border: 1.5px solid var(--border-brand);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  box-shadow: var(--shadow-subtle);
  margin-bottom: var(--space-4);
}

.importacion-step-actions {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: var(--space-4);
}

/* Paso 1 — zona de arrastrar/soltar. */
.importacion-dropzone {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-6, 3rem) var(--space-4);
  border: 2px dashed var(--border-subtle);
  border-radius: var(--radius-lg);
  text-align: center;
  cursor: pointer;
  transition: border-color var(--transition-fast), background-color var(--transition-fast);
}

.importacion-dropzone:hover,
.importacion-dropzone:focus-visible {
  border-color: var(--accent-text);
  outline: none;
}

.importacion-dropzone--arrastrando {
  border-color: var(--accent-text);
  background: var(--accent-soft);
}

.importacion-dropzone--cargando {
  opacity: 0.6;
  pointer-events: none;
}

.importacion-dropzone__texto {
  margin: 0;
  color: var(--text-secondary);
}

.importacion-archivo-info {
  margin-top: var(--space-4);
}

.importacion-preview-scroll,
.importacion-mapeo-scroll,
.importacion-detalle-scroll {
  max-height: 360px;
  overflow-y: auto;
}

.importacion-preview-nota {
  margin: var(--space-2) 0 0;
  font-size: 0.8rem;
  color: var(--text-secondary);
}

/* Paso 2 — tabla de mapeo. */
.importacion-mapeo-tabla td:first-child {
  white-space: nowrap;
}

.importacion-mapeo-tabla select {
  width: 100%;
  min-width: 220px;
  padding: var(--space-2) var(--space-3);
  border: 1.5px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--bg-app);
  color: var(--text-primary);
  font: inherit;
}

.importacion-obligatorio {
  color: var(--danger);
}

.importacion-criterio-cat,
.importacion-referencia-nota {
  color: var(--text-secondary);
  font-size: 0.8em;
  font-weight: 400;
}

/* Paso 3 — resumen de validación. */
.importacion-resumen-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

.importacion-resumen-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-3);
  border-radius: var(--radius-lg);
  background: var(--bg-secondary);
  border: 1.5px solid var(--border-subtle);
}

.importacion-resumen-card__valor {
  font-size: 1.6rem;
  font-weight: 800;
}

.importacion-resumen-card--ok {
  border-color: var(--success);
}

.importacion-resumen-card--ok .importacion-resumen-card__valor {
  color: var(--success);
}

.importacion-resumen-card--warn {
  border-color: var(--warning);
}

.importacion-resumen-card--warn .importacion-resumen-card__valor {
  color: var(--warning);
}

.importacion-resumen-card--bad {
  border-color: var(--danger);
}

.importacion-resumen-card--bad .importacion-resumen-card__valor {
  color: var(--danger);
}

/* Paso 4 — confirmación e importación real. */
.importacion-confirmacion {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  align-items: flex-start;
}

.importacion-omitidas {
  margin: 0;
  color: var(--text-secondary);
  font-size: 0.9rem;
}

.importacion-resultado-final {
  margin-top: var(--space-3);
}

.importacion-resultado-ok {
  padding: var(--space-3);
  border-radius: var(--radius-md);
  background: var(--success-soft);
  color: var(--success);
}

/* Trazabilidad — "Últimas importaciones". */
.importacion-historial h3 {
  margin: 0 0 var(--space-3);
}

/* ==========================================================================
   Fase 11.4B — Dark Mode: correcciones de contraste puntuales
   ==========================================================================
   `--brand-primary` (navy #20294b) es intencionalmente el MISMO en
   Light y Dark (regla del tema: navy/dorado son marca, no cambian) —
   pero varios selectores lo usaban como color de TEXTO/ICONO sobre un
   fondo que SÍ cambia con el tema (`--bg-elevated`/`--accent-soft`),
   dando texto navy sobre superficie ya oscura = invisible en Dark
   (detectado navegando real, instrucción 11 — nunca sólo por existir
   la clase). Cada caso se corrige aquí, sólo en Dark, sin tocar Light
   ni la superficie/layout que lo contiene. Dorado (`--accent`) se usa
   para elementos de énfasis (pestaña activa, iconos, ranking); texto
   plano pasa a `--text-primary` — el dorado sigue siendo acento, nunca
   protagonista (instrucción 12). */
:root[data-theme="dark"] {
  --accent-text: var(--brand-accent);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --accent-text: var(--brand-accent);
  }
}

:root[data-theme="dark"] .login-brand__mark,
:root[data-theme="dark"] .param-tab--active,
:root[data-theme="dark"] .dash-kpi-card__icon,
:root[data-theme="dark"] .dash-chart-card__info:hover,
:root[data-theme="dark"] .dash-chart-card__info:focus-visible,
:root[data-theme="dark"] .dash-ranking__valor,
:root[data-theme="dark"] .dash-ranking__item:nth-child(1) .dash-ranking__pos {
  color: var(--brand-accent);
}

:root[data-theme="dark"] .eval-form__resumen,
:root[data-theme="dark"] .alert-help__bloque h3,
:root[data-theme="dark"] .conexion-row__valor--code {
  color: var(--text-primary);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .login-brand__mark,
  :root:not([data-theme="light"]) .param-tab--active,
  :root:not([data-theme="light"]) .dash-kpi-card__icon,
  :root:not([data-theme="light"]) .dash-chart-card__info:hover,
  :root:not([data-theme="light"]) .dash-chart-card__info:focus-visible,
  :root:not([data-theme="light"]) .dash-ranking__valor,
  :root:not([data-theme="light"]) .dash-ranking__item:nth-child(1) .dash-ranking__pos {
    color: var(--brand-accent);
  }

  :root:not([data-theme="light"]) .eval-form__resumen,
  :root:not([data-theme="light"]) .alert-help__bloque h3,
  :root:not([data-theme="light"]) .conexion-row__valor--code {
    color: var(--text-primary);
  }
}

/* ============================================================
   Fase 13A.2A — Dataset Demo seguro y reutilizable
   ============================================================ */

.demo-indicator {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.3rem 0.75rem;
  border-radius: 999px;
  background: var(--warning-soft, rgba(202, 138, 4, 0.12));
  color: var(--warning);
  border: 1px solid var(--warning);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: min(60vw, 320px);
  flex: 0 1 auto;
  min-width: 0;
}

/* Bug confirmado — Microfase 13A.2V.1: `display: inline-flex` de arriba
   pisaba el `display: none` nativo de `[hidden]` (misma especificidad,
   `.demo-indicator` cae después en la cascada) — el pill quedaba
   visible y vacío en Modo REAL. Mismo patrón ya usado en este archivo
   para `.user-menu__panel[hidden]`/`.sidebar-scrim[hidden]`. */
.demo-indicator[hidden] {
  display: none;
}

/* Microfase 13A.2V.1 — texto completo vs. abreviado, decidido por CSS
   según el ancho (nunca JS por breakpoint) — mismo criterio que
   `.user-menu__info` más arriba en este archivo. El recorte con
   ellipsis (si el texto completo no cupiera) ya lo aplica
   `.demo-indicator` como contenedor. */
.demo-indicator__short {
  display: none;
}

@media (max-width: 480px) {
  .demo-indicator__full {
    display: none;
  }

  .demo-indicator__short {
    display: inline;
  }
}

.demo-dataset-contexto {
  padding: 0.6rem 1rem;
  border-radius: var(--radius-md, 8px);
  background: var(--bg-elevated);
  border: 1px solid var(--border-subtle);
  margin-block: 1rem;
}

.demo-dataset-contexto--activo {
  background: var(--warning-soft, rgba(202, 138, 4, 0.12));
  border-color: var(--warning);
  color: var(--warning);
}

.demo-dataset-batch {
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md, 8px);
  padding: 1rem;
  margin-block-end: 1rem;
  background: var(--bg-elevated);
}

.demo-dataset-batch__header h3 {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0;
}

.demo-dataset-batch__conteos {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.25rem;
  list-style: none;
  padding: 0;
  margin: 0.5rem 0;
  font-size: 0.9rem;
  color: var(--text-secondary);
}

.demo-dataset-batch__acciones {
  margin-top: 0.75rem;
}

.demo-dataset-activate-form {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  align-items: center;
}

/* Post-deploy — mismo patrón visual que .demo-dataset-activate-form,
   sólo para el formulario "Cargar Dataset Demo" que faltaba (bug
   confirmado: el backend ya soportaba action:"load" desde Fase
   13A.2V, pero nunca existió un botón real en la UI). */
.demo-dataset-load-form {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  align-items: center;
  margin-top: 0.75rem;
}

.demo-dataset-ops__advanced {
  margin-top: 1.5rem;
  border-top: 1px solid var(--border-subtle);
  padding-top: 1rem;
}

.demo-dataset-ops__advanced summary {
  cursor: pointer;
  font-weight: 700;
  font-size: 1.05rem;
  color: var(--text-primary);
}

/* Microfase UI — Operaciones avanzadas Dataset Demo: descripción breve
   bajo el encabezado, reutilizando literalmente `.param-shell__subtitle`
   (mismo criterio tipográfico que el resto de Parametrizables — nunca
   un estilo de texto nuevo). */
.demo-dataset-ops__desc {
  margin-top: var(--space-2);
}

/* Primera fila — "Generar paquete" / "Probar restauración": misma card
   base que `.dash-chart-card` (Dashboard) — reutiliza el Design System
   ya existente en vez de inventar un lenguaje visual nuevo. `auto-fit`
   da 2 columnas en desktop/tablet cuando caben cómodamente y colapsa a
   1 columna por fila en mobile, sin overflow horizontal y sin lógica
   de breakpoint en JS. */
.demo-dataset-ops__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-4);
  margin-top: var(--space-4);
}

.demo-dataset-ops__card {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  background: var(--bg-elevated);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  padding: var(--space-4);
}

.demo-dataset-ops__card h4 {
  margin: 0;
  font-size: 1rem;
  font-weight: 700;
  color: var(--text-primary);
}

.demo-dataset-ops__card p {
  margin: 0;
  color: var(--text-secondary);
  font-size: 0.88rem;
  flex: 1 0 auto;
}

.demo-dataset-ops__card form {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: auto;
}

.demo-dataset-ops__card form button {
  align-self: flex-start;
}

/* Segunda fila — zona de eliminación: separada de las cards normales,
   con el tratamiento destructivo ya usado en la app (`--danger`/
   `--danger-soft`, ver `.alert-badge--critical`) — fondo sutil, nunca
   rojo saturado completo. Fila horizontal completa (texto a la
   izquierda, formulario a la derecha) que colapsa a columna en mobile;
   el botón nunca ocupa el ancho completo de pantalla. */
.demo-dataset-ops__danger {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3) var(--space-4);
  margin-top: var(--space-4);
  padding: var(--space-4);
  background: var(--danger-soft);
  border: 1px solid var(--danger);
  border-radius: var(--radius-card);
}

.demo-dataset-ops__danger-text {
  flex: 1 1 260px;
  min-width: 0;
}

.demo-dataset-ops__danger-text h4 {
  margin: 0 0 var(--space-1);
  font-size: 1rem;
  font-weight: 700;
  color: var(--danger);
}

.demo-dataset-ops__danger-text p {
  margin: 0;
  color: var(--text-secondary);
  font-size: 0.88rem;
}

.demo-dataset-ops__danger-form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  flex: 0 1 auto;
}

.demo-dataset-ops__danger-form input {
  min-width: 200px;
}

@media (max-width: 480px) {
  .demo-dataset-ops__danger {
    flex-direction: column;
    align-items: stretch;
  }

  .demo-dataset-ops__danger-text {
    flex: 0 0 auto;
  }

  .demo-dataset-ops__danger-form {
    flex-direction: column;
    align-items: stretch;
  }

  .demo-dataset-ops__danger-form button {
    align-self: flex-start;
  }
}

.demo-dataset-ops__result {
  margin-top: 1rem;
  padding: 0.75rem;
  border-radius: var(--radius-md, 8px);
  background: var(--bg-elevated);
  border: 1px solid var(--border-subtle);
  font-family: monospace;
  font-size: 0.8rem;
  white-space: pre-wrap;
  word-break: break-word;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .demo-indicator {
    background: var(--warning-soft, rgba(229, 138, 43, 0.16));
  }
}

:root[data-theme="dark"] .demo-indicator {
  background: var(--warning-soft, rgba(229, 138, 43, 0.16));
}

/* ============================================================
   Fase 13B.1B — Administrador principal protegido + recuperación
   administrativa
   ============================================================ */

/* Microfase UI — enlace discreto dentro de `.login-recovery` (nunca
   desplegado junto al login): jerarquía baja a propósito, texto
   pequeño, sin fondo/borde de botón — nunca compite con el CTA
   principal "Ingresar". Abre el formulario existente dentro del modal
   reutilizable (components/modal.js). */
.login-recovery__admin-link {
  display: inline-block;
  margin-top: var(--space-2);
  padding: 0;
  border: none;
  background: none;
  font: inherit;
  font-size: 0.78rem;
  color: var(--text-secondary);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

.login-recovery__admin-link:hover,
.login-recovery__admin-link:focus-visible {
  /* `--accent-text` (nunca `--accent`/`--brand-primary` como color de
     TEXTO) — mismo criterio ya establecido en la sección "Dark Mode:
     correcciones de contraste puntuales" más abajo en este archivo:
     `--brand-primary` es intencionalmente el mismo navy en Light y
     Dark, así que usarlo como texto sobre una superficie que sí
     oscurece (la card de login) lo vuelve invisible en Dark. */
  color: var(--accent-text);
}

.admin-recovery-login-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.admin-recovery-login-form .form-hint {
  margin: 0 0 var(--space-2);
}

.form-success {
  color: var(--success);
  font-size: 0.85rem;
  font-weight: 600;
}

/* Panel de gestión (menú de usuario → "Recuperación administrativa") —
   el párrafo introductorio reutiliza `.form-hint` (ya definida, mismo
   criterio tipográfico que el resto de formularios) en vez de una
   clase propia. */
.admin-recovery-panel__state {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  margin: 0 0 var(--space-3);
  background: var(--bg-elevated);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md, 8px);
}

.admin-recovery-panel__state-label {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-primary);
}

/* "Al generar uno nuevo, el código anterior dejará de funcionar." —
   tono de advertencia suave (`--warning`, no `--danger`): informa una
   consecuencia esperada, no un error. */
.admin-recovery-panel__regen-warning {
  color: var(--warning);
  font-size: 0.82rem;
  margin: 0 0 var(--space-3);
}

.admin-recovery-panel__warning {
  color: var(--danger);
  font-size: 0.85rem;
  font-weight: 600;
  margin: 0 0 var(--space-2);
}

/* Badge "Administrador principal protegido" en Usuarios y roles —
   reutiliza el tono `--warning` ya usado por `.demo-indicator`/
   `.demo-dataset-contexto--activo` para "estado especial, no crítico"
   (nunca `--danger`: esto es protección, no una alerta). */
.badge--admin-principal {
  background: var(--warning-soft, rgba(202, 138, 4, 0.12));
  color: var(--warning);
  border: 1px solid var(--warning);
  font-size: 0.72rem;
}
