/* ==========================================================================
   Centro de Control de Marketing — Dentaláser
   Design system. Herramienta de trabajo diaria: densidad + honestidad de
   datos por encima de espectáculo. Sin build step — CSS plano.
   Ver ARQUITECTURA.md §6 para el contrato de colores y tipografía.
   ========================================================================== */

/* ---------- Fuentes (src:url, NUNCA local — bug conocido de la web) ------ */
@font-face { font-family:'CottorwayPro'; src:url('../fonts/CottorwayPro-Light.woff2') format('woff2'); font-weight:300; font-display:swap; }
@font-face { font-family:'CottorwayPro'; src:url('../fonts/CottorwayPro-Regular.woff2') format('woff2'); font-weight:400; font-display:swap; }
@font-face { font-family:'CottorwayPro'; src:url('../fonts/CottorwayPro-Medium.woff2') format('woff2'); font-weight:500; font-display:swap; }
@font-face { font-family:'CottorwayPro'; src:url('../fonts/CottorwayPro-SemiBold.woff2') format('woff2'); font-weight:600; font-display:swap; }
@font-face { font-family:'CottorwayPro'; src:url('../fonts/CottorwayPro-Bold.woff2') format('woff2'); font-weight:700; font-display:swap; }
@font-face { font-family:'CottorwayPro'; src:url('../fonts/CottorwayPro-ExtraBold.woff2') format('woff2'); font-weight:800; font-display:swap; }
@font-face { font-family:'CottorwayPro'; src:url('../fonts/CottorwayPro-Black.woff2') format('woff2'); font-weight:900; font-display:swap; }
@font-face { font-family:'Polly'; src:url('../fonts/Polly-Thin.woff2') format('woff2'); font-weight:100; font-display:swap; }
@font-face { font-family:'Polly'; src:url('../fonts/Polly-Light.woff2') format('woff2'); font-weight:300; font-display:swap; }
@font-face { font-family:'Polly'; src:url('../fonts/Polly-Regular.woff2') format('woff2'); font-weight:400; font-display:swap; }
@font-face { font-family:'Polly'; src:url('../fonts/Polly-Bold.woff2') format('woff2'); font-weight:700; font-display:swap; }

/* ---------- Reset mínimo ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body { margin: 0; }
img, svg { display: block; max-width: 100%; }
button, input, select, textarea { font: inherit; color: inherit; background: none; border: none; }
a { color: inherit; text-decoration: none; }
ul, ol { list-style: none; margin: 0; padding: 0; }
table { border-collapse: collapse; width: 100%; }
fieldset { border: none; margin: 0; padding: 0; }
button { cursor: pointer; }
button:disabled { cursor: not-allowed; }

/* ==========================================================================
   TOKENS — tema oscuro (por defecto)
   ========================================================================== */
:root {
  /* marca — los 5 colores, ninguno más */
  --navy:#184566; --navy-dark:#0d2a3d; --navy-mid:#10344f;
  --cyan:#44D4E4; --cyan-light:#6BEDFB; --steel:#59879E; --white:#FDFFFF;

  /* superficies */
  --bg:#0a1f2e;
  --surface:#0d2a3d;
  --surface-2:#10344f;
  --surface-3:#153c59;
  --border:rgba(89,135,158,.22);
  --border-strong:rgba(89,135,158,.42);

  /* texto */
  --text:#FDFFFF;
  --text-2:#9fc0d1;
  --text-3:#59879E;

  /* acento */
  --accent:var(--cyan);
  --accent-light:var(--cyan-light);
  --accent-contrast:#052430; /* texto sobre fondo cyan solido */

  /* semánticos */
  --pos:#3ddc97; --neg:#ff6b6b; --warn:#ffc857; --neutro:#59879E;
  --pos-bg:rgba(61,220,151,.14); --neg-bg:rgba(255,107,107,.14);
  --warn-bg:rgba(255,200,87,.14); --neutro-bg:rgba(89,135,158,.16);

  /* tipografía */
  --f-d:'CottorwayPro','DM Sans',system-ui,sans-serif;
  --f-b:'Polly','DM Sans',system-ui,sans-serif;

  /* espaciado — escala 4px */
  --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:20px;
  --sp-6:24px; --sp-7:28px; --sp-8:32px; --sp-10:40px; --sp-12:48px; --sp-16:64px;

  --radius-sm:8px; --radius-md:12px; --radius-lg:16px; --radius-full:999px;

  --shadow-sm:0 2px 8px rgba(3,10,16,.28);
  --shadow-md:0 8px 28px rgba(3,10,16,.36);
  --shadow-lg:0 20px 56px rgba(3,10,16,.46);

  --ease-expo:cubic-bezier(.16,1,.3,1);
  --dur-fast:150ms; --dur-base:200ms; --dur-slow:250ms;

  --sidebar-w:264px; --sidebar-w-collapsed:76px; --topbar-h:64px;
  --z-sidebar:60; --z-topbar:50; --z-overlay:70; --z-panel:80; --z-cmdk:90; --z-toast:100;

  color-scheme: dark;
}

/* Tema claro — MISMAS variables redefinidas (§6: ambos con las mismas) */
:root[data-theme="light"] {
  --bg:#eef4f8;
  --surface:#ffffff;
  --surface-2:#f4f9fb;
  --surface-3:#e8f1f5;
  --border:rgba(24,69,102,.14);
  --border-strong:rgba(24,69,102,.26);

  --text:#0d2a3d;
  --text-2:#3d6376;
  --text-3:#6b93a5;

  --accent-contrast:#052430;

  --pos:#18a76d; --neg:#d8383e; --warn:#a56c17; --neutro:#3d6376;
  --pos-bg:rgba(24,167,109,.1); --neg-bg:rgba(216,56,62,.1);
  --warn-bg:rgba(165,108,23,.12); --neutro-bg:rgba(61,99,118,.08);

  --shadow-sm:0 2px 8px rgba(24,69,102,.08);
  --shadow-md:0 8px 28px rgba(24,69,102,.1);
  --shadow-lg:0 20px 56px rgba(24,69,102,.14);

  color-scheme: light;
}

/* ==========================================================================
   BASE
   ========================================================================== */
html, body {
  height: 100%;
  background: var(--bg);
  color: var(--text);
}
body {
  font-family: var(--f-b);
  font-weight: 400;
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  transition: background-color var(--dur-slow) var(--ease-expo), color var(--dur-slow) var(--ease-expo);
}
h1, h2, h3, h4, .num {
  font-family: var(--f-d);
  font-weight: 700;
  letter-spacing: -0.01em;
  margin: 0;
}
p { margin: 0; }

/* Números tabulares en TODO KPI y toda tabla, siempre */
.num, .kpi-card__valor, .kpi-card__delta, .tabla td[data-num], .cmdk-hint {
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
}

/* Foco visible siempre — accesibilidad no negociable */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}
:focus:not(:focus-visible) { outline: none; }

::selection { background: var(--accent); color: var(--accent-contrast); }

/* Scrollbar discreta */
* { scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; }
*::-webkit-scrollbar { width: 8px; height: 8px; }
*::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: var(--radius-full); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

.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 {
  position: absolute; left: var(--sp-3); top: -48px;
  background: var(--accent); color: var(--accent-contrast);
  padding: var(--sp-2) var(--sp-4); border-radius: var(--radius-sm);
  font-family: var(--f-d); font-weight: 700; z-index: var(--z-cmdk);
  transition: top var(--dur-base) var(--ease-expo);
}
.skip-link:focus { top: var(--sp-3); }

/* Envoltura de icono SVG inline (creada por componentes.js → icono()).
   Por defecto escala con el texto (1em); las clases más específicas de cada
   componente (estado-vacio__icono, btn-icon svg...) declaradas más abajo
   ganan la cascada por orden de aparición y fijan su tamaño en píxeles. */
.icono { display: inline-flex; align-items: center; justify-content: center; width: 1em; height: 1em; flex: none; }
.icono svg { width: 100%; height: 100%; display: block; }

/* ==========================================================================
   SUPERFICIES / TARJETAS
   ========================================================================== */
.surface {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--sp-6);
  box-shadow: var(--shadow-sm);
}

/* ==========================================================================
   BOTONES
   ========================================================================== */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  font-family: var(--f-d); font-weight: 600; font-size: 14px;
  padding: var(--sp-3) var(--sp-5);
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  transition: transform var(--dur-fast) var(--ease-expo),
              background-color var(--dur-fast) var(--ease-expo),
              border-color var(--dur-fast) var(--ease-expo),
              box-shadow var(--dur-fast) var(--ease-expo);
  white-space: nowrap;
}
.btn:active { transform: scale(.97); }
.btn:disabled { opacity: .5; pointer-events: none; }

.btn--primary { background: var(--accent); color: var(--accent-contrast); }
.btn--primary:hover { background: var(--accent-light); box-shadow: 0 0 0 4px rgba(68,212,228,.18); }

.btn--secondary { background: var(--surface-2); color: var(--text); border-color: var(--border); }
.btn--secondary:hover { border-color: var(--border-strong); background: var(--surface-3); }

.btn--ghost { background: transparent; color: var(--text-2); }
.btn--ghost:hover { background: var(--surface-2); color: var(--text); }

.btn--danger { background: var(--neg-bg); color: var(--neg); }
.btn--danger:hover { background: var(--neg); color: #180505; }

.btn--sm { padding: var(--sp-2) var(--sp-3); font-size: 13px; }
.btn--block { width: 100%; }

.btn-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; border-radius: var(--radius-sm);
  color: var(--text-2); position: relative;
  transition: background-color var(--dur-fast) var(--ease-expo), color var(--dur-fast) var(--ease-expo);
}
.btn-icon:hover { background: var(--surface-2); color: var(--text); }
.btn-icon svg { width: 20px; height: 20px; }
/* Issue Ronda 1 #6: 36×36 visual se queda corto del mínimo táctil de 44×44
   pedido por Joni. En vez de agrandar la caja visible (que apretaría/podría
   solapar los iconos ya ajustados en la topbar), se amplía solo el ÁREA DE
   PULSACIÓN con un ::before invisible centrado — mismo patrón que usa Apple/
   Google HIG. El hueco entre iconos en la topbar es --sp-2 (8px): +4px por
   lado a cada botón hace que las zonas táctiles se toquen sin solaparse. */
@media (max-width: 899px), (pointer: coarse) {
  .btn-icon::before {
    content: ''; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
    width: 44px; height: 44px;
  }
}

/* ==========================================================================
   FORMULARIOS
   ========================================================================== */
.campo { display: flex; flex-direction: column; gap: var(--sp-2); margin-bottom: var(--sp-4); }
.campo label {
  font-family: var(--f-d); font-weight: 600; font-size: 13px; color: var(--text-2);
  text-transform: uppercase; letter-spacing: .04em;
}
.input, .select {
  width: 100%;
  padding: var(--sp-3) var(--sp-4);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-size: 15px;
  transition: border-color var(--dur-fast) var(--ease-expo), box-shadow var(--dur-fast) var(--ease-expo);
}
.input::placeholder { color: var(--text-2); }
.input:hover { border-color: var(--border-strong); }
.input:focus-visible, .select:focus-visible {
  border-color: var(--accent); box-shadow: 0 0 0 3px rgba(68,212,228,.16); outline: none;
}
.input--error { border-color: var(--neg); }
.campo__error {
  display: flex; align-items: center; gap: var(--sp-1);
  color: var(--neg); font-size: 13px;
}
.campo__ayuda { color: var(--text-2); font-size: 13px; }

.input-wrap { position: relative; }
.input-wrap .input { padding-right: 44px; }
.input-wrap__accion {
  position: absolute; right: var(--sp-1); top: 50%; transform: translateY(-50%);
}

/* Medidor de fortaleza de contraseña */
.fortaleza { display: flex; gap: var(--sp-1); margin-top: var(--sp-2); }
.fortaleza__barra {
  flex: 1; height: 4px; border-radius: var(--radius-full);
  background: var(--surface-3);
  transition: background-color var(--dur-base) var(--ease-expo);
}
.fortaleza__barra.activa[data-nivel="debil"] { background: var(--neg); }
.fortaleza__barra.activa[data-nivel="media"] { background: var(--warn); }
.fortaleza__barra.activa[data-nivel="fuerte"] { background: var(--pos); }
.fortaleza__texto { font-size: 13px; color: var(--text-2); margin-top: var(--sp-1); }

/* Input segmentado TOTP — 6 casillas */
.otp {
  display: flex; gap: var(--sp-2); justify-content: center;
}
.otp__casilla {
  width: 46px; height: 56px;
  text-align: center;
  font-family: var(--f-d); font-size: 22px; font-weight: 700;
  background: var(--surface-2);
  border: 1.5px solid var(--border);
  border-radius: var(--radius-sm);
  transition: border-color var(--dur-fast) var(--ease-expo), box-shadow var(--dur-fast) var(--ease-expo), transform var(--dur-fast) var(--ease-expo);
}
.otp__casilla:focus-visible {
  border-color: var(--accent); box-shadow: 0 0 0 3px rgba(68,212,228,.16); outline: none;
}
.otp__casilla.llena { border-color: var(--border-strong); }
.otp__casilla.error { border-color: var(--neg); animation: otp-shake .3s var(--ease-expo); }
@keyframes otp-shake {
  0%,100% { transform: translateX(0); } 25% { transform: translateX(-4px); } 75% { transform: translateX(4px); }
}

/* ==========================================================================
   BADGES / ORIGEN DEL DATO — regla de honestidad central
   ========================================================================== */
.badge {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  padding: 2px var(--sp-2);
  border-radius: var(--radius-full);
  font-family: var(--f-d); font-size: 11px; font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase;
  white-space: nowrap;
}
.badge--pos { background: var(--pos-bg); color: var(--pos); }
.badge--neg { background: var(--neg-bg); color: var(--neg); }
.badge--warn { background: var(--warn-bg); color: var(--warn); }
.badge--neutro { background: var(--neutro-bg); color: var(--text-2); }

.badge-origen { cursor: default; }
.badge-origen[data-origen="MEDIDO"] { background: var(--pos-bg); color: var(--pos); }
.badge-origen[data-origen="ESTIMADO"] { background: var(--warn-bg); color: var(--warn); }
.badge-origen[data-origen="NO_CONECTADO"] { background: var(--neutro-bg); color: var(--text-2); }
.badge-origen__punto {
  width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none;
}

/* Semáforo simple (frecuencia de anuncio, ocupación...) */
.semaforo { display: inline-flex; align-items: center; gap: var(--sp-2); }
.semaforo__luz { width: 10px; height: 10px; border-radius: 50%; flex: none; }
.semaforo__luz[data-nivel="verde"] { background: var(--pos); box-shadow: 0 0 8px rgba(61,220,151,.5); }
.semaforo__luz[data-nivel="ambar"] { background: var(--warn); box-shadow: 0 0 8px rgba(255,200,87,.5); }
.semaforo__luz[data-nivel="rojo"] { background: var(--neg); box-shadow: 0 0 8px rgba(255,107,107,.5); }

/* ==========================================================================
   KPI CARD — el componente más importante de la app
   ========================================================================== */
.kpi-card {
  display: flex; flex-direction: column; gap: var(--sp-3);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--sp-5);
  text-align: left;
  width: 100%;
  transition: transform var(--dur-base) var(--ease-expo),
              border-color var(--dur-base) var(--ease-expo),
              box-shadow var(--dur-base) var(--ease-expo);
}
.kpi-card[data-clicable="true"] { cursor: pointer; }
.kpi-card[data-clicable="true"]:hover {
  border-color: var(--border-strong);
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}
.kpi-card[data-clicable="true"]:active { transform: translateY(0) scale(.995); }

.kpi-card__header {
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-2);
}
.kpi-card__titulo {
  font-family: var(--f-d); font-weight: 600; font-size: 13px;
  color: var(--text-2); text-transform: uppercase; letter-spacing: .04em;
  display: flex; align-items: center; gap: var(--sp-1);
}
.kpi-card__cuerpo { display: flex; align-items: baseline; gap: var(--sp-2); flex-wrap: wrap; }
.kpi-card__valor {
  font-family: var(--f-d); font-weight: 800; font-size: clamp(28px, 4vw, 36px);
  letter-spacing: -0.02em; line-height: 1;
}
.kpi-card__unidad { font-family: var(--f-d); font-size: 15px; color: var(--text-2); font-weight: 600; }

.kpi-card__pie { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.kpi-card__delta {
  display: inline-flex; align-items: center; gap: 3px;
  font-family: var(--f-d); font-weight: 700; font-size: 14px;
  padding: 2px var(--sp-2); border-radius: var(--radius-full);
}
.kpi-card__delta[data-sentido="pos"] { color: var(--pos); background: var(--pos-bg); }
.kpi-card__delta[data-sentido="neg"] { color: var(--neg); background: var(--neg-bg); }
.kpi-card__delta[data-sentido="neutro"] { color: var(--text-2); background: var(--neutro-bg); }
.kpi-card__delta svg { width: 13px; height: 13px; }

.kpi-card__abs {
  font-size: 13px; color: var(--text-2);
  max-height: 0; opacity: 0; overflow: hidden;
  transition: max-height var(--dur-base) var(--ease-expo), opacity var(--dur-base) var(--ease-expo), margin-top var(--dur-base) var(--ease-expo);
}
.kpi-card.abs-visible .kpi-card__abs { max-height: 40px; opacity: 1; margin-top: var(--sp-1); }

.kpi-card__sparkline { width: 100%; height: 36px; margin-top: var(--sp-1); }

/* Estado muestra pequeña (§8 motor de insights) */
.kpi-card__confianza {
  font-size: 12px; color: var(--text-2); display: flex; align-items: center; gap: 4px;
}
.kpi-card__confianza svg { width: 13px; height: 13px; }

/* ==========================================================================
   ESTADOS VACÍOS — tres, distintos y NUNCA intercambiables (§9)
   ========================================================================== */
.estado-vacio {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--sp-2); text-align: center; padding: var(--sp-8) var(--sp-4);
  color: var(--text-2); min-height: 120px;
}
.estado-vacio__icono {
  width: 40px; height: 40px; color: var(--text-3);
}
.estado-vacio__titulo { font-family: var(--f-d); font-weight: 700; font-size: 15px; color: var(--text); }
.estado-vacio__detalle { font-size: 13px; color: var(--text-2); max-width: 36ch; }

/* CERO_REAL: no es un estado vacío decorado, es un 0 normal dentro del propio KPI */
.kpi-card__valor[data-cero-real="true"] { color: var(--text); }

/* NO_CONECTADO: placeholder + qué falta + botón conectar */
.estado-vacio--no-conectado .estado-vacio__icono { color: var(--warn); }
.estado-vacio--no-conectado { border: 1px dashed var(--border-strong); border-radius: var(--radius-md); }

/* SIN_HISTORICO: conectado, pero sin datos en el periodo */
.estado-vacio--sin-historico .estado-vacio__icono { color: var(--text-3); }

/* Dentro de una kpi-card, versión compacta de los tres estados */
.kpi-card--vacio .kpi-card__valor { font-size: 22px; color: var(--text-2); font-weight: 700; }
.kpi-card--no-conectado { border-style: dashed; }
.kpi-card__conectar { margin-top: var(--sp-1); align-self: flex-start; }

/* ==========================================================================
   SKELETON LOADERS — forma exacta del contenido final
   ========================================================================== */
.skeleton {
  position: relative; overflow: hidden;
  background: var(--surface-3);
  border-radius: var(--radius-sm);
}
.skeleton::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(253,255,255,.06), transparent);
  transform: translateX(-100%);
  animation: skeleton-shimmer 1.4s var(--ease-expo) infinite;
}
:root[data-theme="light"] .skeleton::after {
  background: linear-gradient(90deg, transparent, rgba(13,42,61,.06), transparent);
}
@keyframes skeleton-shimmer { 100% { transform: translateX(100%); } }

.skeleton--linea { height: 14px; margin-bottom: var(--sp-2); }
.skeleton--titulo { height: 20px; width: 60%; margin-bottom: var(--sp-3); }
.skeleton--kpi-valor { height: 34px; width: 45%; margin: var(--sp-2) 0; }
.skeleton--circulo { border-radius: 50%; }

/* ==========================================================================
   TABLA AVANZADA
   ========================================================================== */
.tabla-wrap { overflow-x: auto; }
.tabla { font-size: 14px; }
.tabla thead th {
  text-align: left; padding: var(--sp-3) var(--sp-4);
  font-family: var(--f-d); font-weight: 600; font-size: 12px;
  color: var(--text-2); text-transform: uppercase; letter-spacing: .04em;
  border-bottom: 1px solid var(--border);
  white-space: nowrap; position: sticky; top: 0; background: var(--surface); z-index: 1;
}
.tabla th[data-ordenable="true"] { cursor: pointer; user-select: none; }
.tabla th[data-ordenable="true"]:hover { color: var(--text); }
.tabla th .th-orden { display: inline-flex; align-items: center; gap: 4px; }
.tabla th .th-orden svg { width: 12px; height: 12px; opacity: .5; transition: opacity var(--dur-fast) var(--ease-expo), transform var(--dur-fast) var(--ease-expo); }
.tabla th[data-orden="asc"] .th-orden svg { opacity: 1; }
.tabla th[data-orden="desc"] .th-orden svg { opacity: 1; transform: rotate(180deg); }

.tabla tbody td { padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--border); }
.tabla tbody td[data-num] { text-align: right; }
.tabla tbody tr { transition: background-color var(--dur-fast) var(--ease-expo); }
.tabla tbody tr:hover { background: var(--surface-2); }
.tabla tbody tr:last-child td { border-bottom: none; }

.tabla__toolbar {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  padding-bottom: var(--sp-3); flex-wrap: wrap;
}
.tabla__buscador { max-width: 280px; }
.tabla__paginacion {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  padding-top: var(--sp-3); flex-wrap: wrap; font-size: 13px; color: var(--text-2);
}
.tabla__paginacion-botones { display: flex; gap: var(--sp-1); }

/* ==========================================================================
   SIDEBAR
   ========================================================================== */
.sidebar {
  position: fixed; inset: 0 auto 0 0; width: var(--sidebar-w);
  background: var(--surface);
  border-right: 1px solid var(--border);
  display: flex; flex-direction: column;
  z-index: var(--z-sidebar);
  transition: width var(--dur-slow) var(--ease-expo), transform var(--dur-slow) var(--ease-expo);
  padding-top: env(safe-area-inset-top, 0px);
}
.sidebar[data-colapsado="true"] { width: var(--sidebar-w-collapsed); }

.sidebar__marca {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-5) var(--sp-4); height: var(--topbar-h);
  border-bottom: 1px solid var(--border);
}
.sidebar__marca-logo {
  width: 32px; height: 32px; flex: none; border-radius: 50%; object-fit: cover;
}
.sidebar__marca-texto {
  font-family: var(--f-d); font-weight: 800; font-size: 15px; white-space: nowrap;
  overflow: hidden; opacity: 1; transition: opacity var(--dur-fast) var(--ease-expo);
}
.sidebar[data-colapsado="true"] .sidebar__marca-texto { opacity: 0; width: 0; }

.sidebar__nav { flex: 1; overflow-y: auto; padding: var(--sp-3); display: flex; flex-direction: column; gap: 2px; }
.sidebar__item {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3); border-radius: var(--radius-sm);
  color: var(--text-2); position: relative; white-space: nowrap;
  transition: background-color var(--dur-fast) var(--ease-expo), color var(--dur-fast) var(--ease-expo);
}
.sidebar__item:hover { background: var(--surface-2); color: var(--text); }
/* El acento MARCA, no escribe: el cyan queda para el borde y el icono; la
   etiqueta usa --text, que sí contrasta en ambos temas (issue Ronda 1 #1:
   --accent como color de texto sobre --surface-2 medía 1.68:1 en claro). */
.sidebar__item[aria-current="page"] { background: var(--surface-2); color: var(--text); }
.sidebar__item[aria-current="page"] .sidebar__item-texto { font-weight: 700; }
.sidebar__item[aria-current="page"] .sidebar__item-icono { color: var(--accent); }
.sidebar__item[aria-current="page"]::before {
  content: ''; position: absolute; left: -12px; top: 50%; transform: translateY(-50%);
  width: 3px; height: 60%; background: var(--accent); border-radius: var(--radius-full);
}
.sidebar__item-icono { width: 20px; height: 20px; flex: none; }
.sidebar__item-texto {
  overflow: hidden; opacity: 1; transition: opacity var(--dur-fast) var(--ease-expo);
  font-size: 14px; font-weight: 500;
}
/* Sidebar colapsada: el texto no se recorta a width:0, se convierte en un
   tooltip flotante accesible con teclado y ratón (issue Ronda 1 #4). El nodo
   de texto sigue en el DOM tal cual (nunca aria-hidden), así que un lector de
   pantalla lo sigue anunciando como parte del nombre accesible del enlace en
   cualquier estado — esto es solo la capa visual. */
.sidebar[data-colapsado="true"] .sidebar__item { position: relative; }
/* CSS no permite "scroll en Y, visible en X" en la MISMA caja (la
   especificación fuerza el eje "visible" a "auto" en cuanto el otro no lo
   es) — con overflow-y:auto en .sidebar__nav, el tooltip de abajo quedaba
   con opacity:1 (verificado con getComputedStyle) pero recortado en el
   borde derecho del rail (verificado con elementFromPoint: devolvía el
   contenido de detrás, no el tooltip). Solución con el menor alcance
   posible: solo en escritorio (≥900px, donde vive el rail de iconos) y solo
   colapsada, el propio nav pasa a overflow visible para dejar salir el
   tooltip. Con 13 secciones esto cabe de sobra en cualquier alto de
   viewport realista (verificado hasta 800px); en un escritorio
   anormalmente bajo el límite sería que el último icono quede sin scroll —
   el drawer móvil y el sidebar expandido conservan su overflow-y:auto tal
   cual, sin este cambio. */
@media (min-width: 900px) {
  .sidebar[data-colapsado="true"] .sidebar__nav { overflow: visible; }
}
.sidebar[data-colapsado="true"] .sidebar__item-texto {
  position: absolute; left: calc(100% + var(--sp-3)); top: 50%; width: auto; max-width: none;
  transform: translateY(-50%) translateX(-4px);
  background: var(--navy-dark); color: var(--white);
  padding: var(--sp-2) var(--sp-3); border-radius: var(--radius-sm);
  font-size: 13px; font-weight: 600; white-space: nowrap;
  box-shadow: var(--shadow-md); z-index: var(--z-overlay);
  opacity: 0; pointer-events: none;
  transition: opacity var(--dur-fast) var(--ease-expo), transform var(--dur-fast) var(--ease-expo);
}
.sidebar[data-colapsado="true"] .sidebar__item:hover .sidebar__item-texto,
.sidebar[data-colapsado="true"] .sidebar__item:focus-visible .sidebar__item-texto,
.sidebar[data-colapsado="true"] .sidebar__item:focus-within .sidebar__item-texto {
  opacity: 1; transform: translateY(-50%) translateX(0);
}
.sidebar__item-badge {
  margin-left: auto; font-size: 11px; font-family: var(--f-d); font-weight: 700;
  color: var(--warn); background: var(--warn-bg); padding: 1px 6px; border-radius: var(--radius-full);
}
.sidebar[data-colapsado="true"] .sidebar__item-badge { display: none; }

.sidebar__pie { padding: var(--sp-3); border-top: 1px solid var(--border); }
.sidebar__colapsar {
  display: flex; align-items: center; justify-content: center; gap: var(--sp-2);
  width: 100%; padding: var(--sp-2); border-radius: var(--radius-sm); color: var(--text-2);
}
.sidebar__colapsar:hover { background: var(--surface-2); color: var(--text); }
.sidebar__colapsar svg { transition: transform var(--dur-base) var(--ease-expo); }
.sidebar[data-colapsado="true"] .sidebar__colapsar svg { transform: rotate(180deg); }

/* Drawer en móvil */
.sidebar__overlay {
  display: none; position: fixed; inset: 0; background: rgba(3,10,16,.6);
  z-index: calc(var(--z-sidebar) - 1); opacity: 0; transition: opacity var(--dur-base) var(--ease-expo);
}
@media (max-width: 899px) {
  .sidebar { transform: translateX(-100%); width: min(var(--sidebar-w), 82vw); }
  .sidebar[data-abierto="true"] { transform: translateX(0); box-shadow: var(--shadow-lg); }
  .sidebar[data-colapsado="true"] { width: min(var(--sidebar-w), 82vw); }
  .sidebar[data-colapsado="true"] .sidebar__item-texto,
  .sidebar[data-colapsado="true"] .sidebar__marca-texto { opacity: 1; width: auto; }
  /* En el drawer móvil "colapsado" sigue siendo texto normal en línea, nunca
     el tooltip flotante de escritorio (issue Ronda 1 #4). */
  .sidebar[data-colapsado="true"] .sidebar__item-texto {
    position: static; transform: none; background: none; color: inherit;
    padding: 0; box-shadow: none;
  }
  .sidebar__overlay[data-abierto="true"] { display: block; opacity: 1; }
}

/* ==========================================================================
   TOPBAR
   ========================================================================== */
.app-shell {
  margin-left: var(--sidebar-w);
  min-height: 100%;
  transition: margin-left var(--dur-slow) var(--ease-expo);
}
.app-shell[data-sidebar-colapsada="true"] { margin-left: var(--sidebar-w-collapsed); }
@media (max-width: 899px) { .app-shell, .app-shell[data-sidebar-colapsada="true"] { margin-left: 0; } }

.topbar {
  position: sticky; top: env(safe-area-inset-top, 0px); z-index: var(--z-topbar);
  height: var(--topbar-h);
  display: flex; align-items: center; gap: var(--sp-3);
  padding: 0 var(--sp-5);
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border);
}
.topbar__menu-movil { display: none; }
@media (max-width: 899px) { .topbar__menu-movil { display: inline-flex; } }

.topbar__grupo { display: flex; align-items: center; gap: var(--sp-2); }
.topbar__grupo--principal { flex: 1; min-width: 0; overflow-x: auto; }
.topbar__separador { width: 1px; height: 24px; background: var(--border); margin: 0 var(--sp-1); }

@media (max-width: 640px) {
  .topbar { padding: 0 var(--sp-3); gap: var(--sp-2); }
  .topbar__texto-oculto-movil { display: none; }
}
@media (max-width: 899px) {
  /* Los filtros (periodo/comparativa/clínica) no caben todos en una fila
     estrecha: se dejan desplazables en horizontal en vez de romper la
     altura fija de la topbar o esconderlos. */
  .topbar__grupo:not(.topbar__grupo--principal) {
    overflow-x: auto; max-width: 46vw; scrollbar-width: none;
  }
  .topbar__grupo:not(.topbar__grupo--principal)::-webkit-scrollbar { display: none; }

  /* #topbar-filtros es el ÚNICO elemento de la topbar pensado para encoger/
     hacer scroll horizontal — todo lo demás (menú, buscador-icono, línea
     separadora, acciones) tiene que conservar su tamaño utilizable siempre.
     Sin esto, flexbox reparte la falta de espacio de forma impredecible
     entre los hermanos y puede aplastar, por ejemplo, el botón de tema a
     unos pocos píxeles — regresión real detectada al verificar en 375px
     tras convertir el buscador en icono (issue #3: antes con display:none
     no competía por ancho; ahora sí, y necesita este resguardo). */
  .topbar__menu-movil, .topbar__separador, #topbar-acciones {
    flex-shrink: 0;
  }

  /* Issue Ronda 1 #4/#6: el filtro de CLÍNICA es "de primera clase" (Joni) y
     antes quedaba detrás del selector de periodo + comparativa, fuera de la
     ventana visible de 46vw, sin ninguna pista de que había más contenido.
     Dos arreglos combinados, ambos solo CSS (sin tocar app.js):
     1) se reordena visualmente #topbar-filtros para que la clínica (3er
        hijo que inserta app.js) sea SIEMPRE el primero en pantalla — no hace
        falta scroll para verla ni para tocarla.
     2) el resto del grupo (periodo · comparativa), que sigue desplazable,
        lleva un degradado de desvanecido en el borde derecho como pista
        visual de que hay más contenido — patrón estándar de tabla/carrusel
        con overflow. */
  #topbar-filtros > *:nth-child(3) { order: -1; margin-right: var(--sp-2); }
  #topbar-filtros {
    -webkit-mask-image: linear-gradient(to right, black calc(100% - 28px), transparent 100%);
    mask-image: linear-gradient(to right, black calc(100% - 28px), transparent 100%);
  }
}

/* Selector de periodo / comparativa / clínica — segmented + dropdown */
.selector {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
  font-size: 13px; font-weight: 600; font-family: var(--f-d); color: var(--text);
  white-space: nowrap;
  transition: border-color var(--dur-fast) var(--ease-expo);
}
.selector:hover { border-color: var(--border-strong); }
.selector svg { width: 15px; height: 15px; color: var(--text-3); }

.segmentado {
  display: inline-flex; background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 2px; gap: 2px;
}
.segmentado__opcion {
  padding: 6px var(--sp-3); border-radius: calc(var(--radius-sm) - 2px);
  font-size: 13px; font-weight: 600; font-family: var(--f-d); color: var(--text-2);
  transition: background-color var(--dur-fast) var(--ease-expo), color var(--dur-fast) var(--ease-expo);
  white-space: nowrap;
}
.segmentado__opcion:hover { color: var(--text); }
.segmentado__opcion[aria-pressed="true"] { background: var(--accent); color: var(--accent-contrast); }
/* Issue Ronda 1 #6: 31.5px de alto medido, por debajo del mínimo táctil —
   aquí sí se sube el tamaño real (no solo el área de pulsación) porque es
   una superficie con estado visual propio (fondo al pulsar) que debe
   percibirse tan grande como se toca. */
@media (max-width: 899px), (pointer: coarse) {
  .segmentado__opcion { padding: 13px var(--sp-3); }
}

/* Buscador topbar (también reutilizado como .tabla__buscador dentro de
   tabla() en componentes.js — las reglas específicas de topbar de abajo van
   siempre con el prefijo #topbar-buscador para no afectar al de las tablas). */
.buscador { position: relative; flex: 1; max-width: 320px; min-width: 120px; }
.buscador svg {
  position: absolute; left: var(--sp-3); top: 50%; transform: translateY(-50%);
  width: 16px; height: 16px; color: var(--text-3); pointer-events: none;
}
.buscador input {
  width: 100%; padding: var(--sp-2) var(--sp-3) var(--sp-2) 38px;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
  font-size: 13px; color: var(--text);
}
.buscador input:focus-visible { border-color: var(--accent); outline: none; box-shadow: 0 0 0 3px rgba(68,212,228,.16); }
.buscador__atajo {
  position: absolute; right: var(--sp-2); top: 50%; transform: translateY(-50%);
  font-size: 11px; color: var(--text-2); font-family: var(--f-d);
  border: 1px solid var(--border); border-radius: 4px; padding: 1px 5px;
}
/* El botón de la topbar usa la utilidad .text-3 para su etiqueta ("Buscar o
   ir a…"), fijada en app.js — aquí se sube a --text-2 solo dentro de este
   componente para que siga siendo texto legible (issue Ronda 1 #2). */
.buscador .text-3 { color: var(--text-2); }

/* Issue Ronda 1 #3: por debajo de 640px el buscador de la topbar dejaba de
   existir sin ningún sustituto — la búsqueda universal quedaba inalcanzable
   en táctil. Se colapsa a botón-icono (44×44, mismo target táctil que el
   resto — issue #6) que abre la MISMA paleta de comandos; el buscador de
   tablas (.tabla__buscador) no se toca. */
@media (max-width: 640px) {
  /* El contenedor (#topbar-buscador, .topbar__grupo--principal) hereda
     flex:1 con flex-basis:0% para el <input> de escritorio: con basis 0 y
     sin espacio libre sobrante que "crecer" (topbar llena entera en móvil),
     el botón-icono de 44px quedaba con caja de ancho 0 y su contenido
     recortado por el overflow-x:auto heredado — regresión detectada al
     verificar en 375px. Tamaño fijo por contenido, como el resto de
     acciones de la topbar. */
  #topbar-buscador { flex: 0 0 auto; overflow: visible; }
  #topbar-buscador .buscador {
    flex: none; width: 44px; height: 44px; min-width: 44px; max-width: 44px;
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
  }
  #topbar-buscador .buscador svg { position: static; }
  #topbar-buscador .buscador .text-3,
  #topbar-buscador .buscador__atajo { display: none; }
}

/* Indicador de estado de sincronización */
.sync {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  font-size: 12px; color: var(--text-2); padding: var(--sp-2) var(--sp-3);
  border-radius: var(--radius-sm);
}
.sync:hover { background: var(--surface-2); }
.sync__punto { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.sync__punto[data-estado="CONECTADA"] { background: var(--pos); box-shadow: 0 0 6px rgba(61,220,151,.6); }
.sync__punto[data-estado="PARCIAL"] { background: var(--warn); box-shadow: 0 0 6px rgba(255,200,87,.6); }
.sync__punto[data-estado="NO_CONECTADA"] { background: var(--text-3); }
.sync__punto[data-estado="ERROR"] { background: var(--neg); box-shadow: 0 0 6px rgba(255,107,107,.6); }

/* Menú de usuario */
.usuario-menu { position: relative; }
.usuario-menu__boton {
  display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-1) var(--sp-2);
  border-radius: var(--radius-sm); position: relative;
}
.usuario-menu__boton:hover { background: var(--surface-2); }
.usuario-menu__avatar {
  width: 32px; height: 32px; border-radius: 50%; flex: none;
  background: var(--accent); color: var(--accent-contrast);
  display: flex; align-items: center; justify-content: center;
  font-family: var(--f-d); font-weight: 800; font-size: 13px;
}
/* Issue Ronda 1 #6: el avatar mide 32×32 — mismo patrón de área de pulsación
   ampliada que .btn-icon, sobre el botón entero (no solo el círculo). */
@media (max-width: 899px), (pointer: coarse) {
  .usuario-menu__boton::before {
    content: ''; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
    width: 44px; height: 44px;
  }
}
.usuario-menu__dropdown {
  position: absolute; right: 0; top: calc(100% + var(--sp-2));
  min-width: 220px; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-md); box-shadow: var(--shadow-lg); padding: var(--sp-2);
  z-index: var(--z-overlay);
  opacity: 0; visibility: hidden; transform: translateY(-6px);
  transition: opacity var(--dur-base) var(--ease-expo), transform var(--dur-base) var(--ease-expo), visibility var(--dur-base);
}
.usuario-menu__dropdown[data-abierto="true"] { opacity: 1; visibility: visible; transform: translateY(0); }
.usuario-menu__item {
  display: flex; align-items: center; gap: var(--sp-2); width: 100%;
  padding: var(--sp-2) var(--sp-3); border-radius: var(--radius-sm); font-size: 14px; color: var(--text);
}
.usuario-menu__item:hover { background: var(--surface-2); }
.usuario-menu__item svg { width: 16px; height: 16px; color: var(--text-3); }
.usuario-menu__rol {
  padding: var(--sp-2) var(--sp-3); font-size: 12px; color: var(--text-2);
  border-bottom: 1px solid var(--border); margin-bottom: var(--sp-1);
}

/* ==========================================================================
   CONTENIDO
   ========================================================================== */
.contenido { padding: var(--sp-6); max-width: 1440px; }
@media (max-width: 640px) { .contenido { padding: var(--sp-4); } }

/* Issue Ronda 1 #3 (numerada como #7 en el encargo): app.js enfoca
   #contenido por script en cada cambio de ruta (buena práctica de
   accesibilidad para quien navega por teclado o lector de pantalla). El
   :focus-visible por defecto SÍ hace match en foco programático de un
   landmark con tabindex="-1" (heurística estándar del navegador), así que
   el anillo aparecía también para usuarios de ratón, con outline-offset
   positivo asomando por fuera del <main> y leyéndose como un elemento roto.
   Solución: el anillo pasa a inset (nunca sobresale del propio <main>, así
   que no puede confundirse con topbar/sidebar) y se desvanece solo a los
   900ms — sigue siendo perfectamente visible al llegar por teclado, pero no
   se queda flotando indefinidamente para quien solo mira con el ratón. */
#contenido:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  border-radius: 0;
  animation: foco-contenido-desvanece 900ms var(--ease-expo) forwards;
}
@keyframes foco-contenido-desvanece {
  0%, 55% { outline-color: var(--accent); }
  100% { outline-color: transparent; }
}

.pagina__cabecera {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--sp-4); margin-bottom: var(--sp-6); flex-wrap: wrap;
}
.pagina__titulo { font-size: clamp(20px, 3vw, 26px); }
.pagina__subtitulo { color: var(--text-2); font-size: 14px; margin-top: var(--sp-1); }

.grid-kpis {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: var(--sp-4); margin-bottom: var(--sp-6);
}

.seccion { margin-bottom: var(--sp-8); }
.seccion__titulo {
  font-size: 17px; margin-bottom: var(--sp-4);
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
}

/* Estado "en construcción" — honesto, nunca mockup con datos falsos */
.en-construccion {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--sp-3); text-align: center; padding: var(--sp-16) var(--sp-4);
  color: var(--text-2); border: 1px dashed var(--border-strong); border-radius: var(--radius-lg);
}
.en-construccion__icono { width: 48px; height: 48px; color: var(--text-3); }
.en-construccion__titulo { font-family: var(--f-d); font-weight: 700; font-size: 17px; color: var(--text); }
.en-construccion__detalle { font-size: 14px; color: var(--text-2); max-width: 42ch; }

/* ==========================================================================
   TOOLTIP + FÓRMULA
   ========================================================================== */
.tooltip-ancla { position: relative; display: inline-flex; }
.tooltip {
  position: absolute; z-index: var(--z-overlay); bottom: calc(100% + var(--sp-2)); left: 50%;
  transform: translateX(-50%) translateY(4px);
  background: var(--navy-dark); color: var(--white);
  border: 1px solid var(--border-strong);
  padding: var(--sp-2) var(--sp-3); border-radius: var(--radius-sm);
  font-size: 12px; line-height: 1.4; white-space: normal; width: max-content; max-width: 240px;
  box-shadow: var(--shadow-md);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity var(--dur-fast) var(--ease-expo), transform var(--dur-fast) var(--ease-expo), visibility var(--dur-fast);
}
:root[data-theme="light"] .tooltip { background: var(--navy-dark); color: var(--white); }
.tooltip-ancla:hover .tooltip, .tooltip-ancla:focus-within .tooltip, .tooltip[data-visible="true"] {
  opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0);
}
.tooltip-formula__boton {
  width: 15px; height: 15px; border-radius: 50%; color: var(--text-3);
  display: inline-flex; align-items: center; justify-content: center;
}
.tooltip-formula__boton:hover { color: var(--accent); }
.tooltip__formula { font-family: var(--f-d); color: var(--accent-light); display: block; margin-top: 2px; }

/* ==========================================================================
   TOASTS
   ========================================================================== */
.toast-container {
  position: fixed; right: var(--sp-4); bottom: calc(var(--sp-4) + env(safe-area-inset-bottom, 0px));
  z-index: var(--z-toast); display: flex; flex-direction: column; gap: var(--sp-2);
  width: min(360px, calc(100vw - var(--sp-8)));
}
.toast {
  display: flex; align-items: flex-start; gap: var(--sp-3);
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--radius-md); padding: var(--sp-3) var(--sp-4);
  box-shadow: var(--shadow-lg);
  animation: toast-in var(--dur-slow) var(--ease-expo);
}
.toast[data-saliendo="true"] { animation: toast-out var(--dur-base) var(--ease-expo) forwards; }
@keyframes toast-in { from { opacity: 0; transform: translateY(12px) scale(.98); } to { opacity: 1; transform: translateY(0) scale(1); } }
@keyframes toast-out { to { opacity: 0; transform: translateX(24px); } }
.toast__icono { width: 20px; height: 20px; flex: none; margin-top: 1px; }
.toast[data-tipo="exito"] .toast__icono { color: var(--pos); }
.toast[data-tipo="error"] .toast__icono { color: var(--neg); }
.toast[data-tipo="aviso"] .toast__icono { color: var(--warn); }
.toast[data-tipo="info"] .toast__icono { color: var(--accent); }
.toast__texto { flex: 1; font-size: 14px; }
.toast__cerrar { flex: none; color: var(--text-3); }
.toast__cerrar:hover { color: var(--text); }

/* ==========================================================================
   PANEL LATERAL DE DRILL-DOWN
   ========================================================================== */
.panel-lateral__overlay {
  position: fixed; inset: 0; background: rgba(3,10,16,.55); z-index: var(--z-panel);
  opacity: 0; visibility: hidden; transition: opacity var(--dur-base) var(--ease-expo), visibility var(--dur-base);
}
.panel-lateral__overlay[data-abierto="true"] { opacity: 1; visibility: visible; }
.panel-lateral {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: calc(var(--z-panel) + 1);
  width: min(480px, 100vw);
  background: var(--surface); border-left: 1px solid var(--border);
  box-shadow: var(--shadow-lg);
  transform: translateX(100%);
  transition: transform var(--dur-slow) var(--ease-expo);
  display: flex; flex-direction: column;
  padding-top: env(safe-area-inset-top, 0px);
  padding-bottom: env(safe-area-inset-bottom, 0px);
}
.panel-lateral[data-abierto="true"] { transform: translateX(0); }
.panel-lateral__cabecera {
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-3);
  padding: var(--sp-5); border-bottom: 1px solid var(--border);
}
.panel-lateral__cuerpo { flex: 1; overflow-y: auto; padding: var(--sp-5); }

/* ==========================================================================
   COMMAND PALETTE — Cmd/Ctrl+K
   ========================================================================== */
.cmdk-overlay {
  position: fixed; inset: 0; z-index: var(--z-cmdk);
  background: rgba(3,10,16,.6); backdrop-filter: blur(2px);
  display: flex; align-items: flex-start; justify-content: center;
  padding: 12vh var(--sp-4) var(--sp-4);
  opacity: 0; visibility: hidden;
  transition: opacity var(--dur-base) var(--ease-expo), visibility var(--dur-base);
}
.cmdk-overlay[data-abierto="true"] { opacity: 1; visibility: visible; }
.cmdk {
  width: min(560px, 100%); background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); overflow: hidden;
  transform: translateY(-12px) scale(.98);
  transition: transform var(--dur-base) var(--ease-expo);
}
.cmdk-overlay[data-abierto="true"] .cmdk { transform: translateY(0) scale(1); }
.cmdk__input-wrap { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-4); border-bottom: 1px solid var(--border); }
.cmdk__input-wrap svg { width: 18px; height: 18px; color: var(--text-3); flex: none; }
.cmdk__input { flex: 1; background: none; font-size: 16px; color: var(--text); }
.cmdk__resultados { max-height: 46vh; overflow-y: auto; padding: var(--sp-2); }
.cmdk__grupo-titulo { padding: var(--sp-2) var(--sp-3); font-size: 11px; color: var(--text-2); text-transform: uppercase; letter-spacing: .06em; font-family: var(--f-d); }
.cmdk__item {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3); border-radius: var(--radius-sm); font-size: 14px;
}
.cmdk__item svg { width: 17px; height: 17px; color: var(--text-3); flex: none; }
.cmdk__item[data-activo="true"] { background: var(--accent); color: var(--accent-contrast); }
.cmdk__item[data-activo="true"] svg { color: var(--accent-contrast); }
.cmdk__item mark { background: none; color: inherit; font-weight: 800; text-decoration: underline; text-decoration-color: currentColor; }
.cmdk__vacio { padding: var(--sp-6); text-align: center; color: var(--text-2); font-size: 14px; }
.cmdk__pie {
  display: flex; align-items: center; gap: var(--sp-4); padding: var(--sp-2) var(--sp-4);
  border-top: 1px solid var(--border); font-size: 11px; color: var(--text-2);
}
.cmdk__pie kbd {
  font-family: var(--f-d); border: 1px solid var(--border-strong); border-radius: 4px;
  padding: 1px 5px; font-size: 11px;
}

/* Issue Ronda 1 #3: en móvil la paleta debe ocupar la pantalla cómodamente y
   los resultados no pueden quedar tapados por el teclado. dvh (viewport
   dinámico) se ajusta cuando el teclado abre/cierra en navegadores móviles
   modernos (iOS Safari ≥15.4, Chrome Android ≥108) — a diferencia de vh, que
   se queda con la altura del viewport SIN teclado. Cabecera (input) y pie
   (atajos) fijos, resultados con el resto del alto disponible siempre
   visible por encima del teclado. */
@media (max-width: 640px) {
  .cmdk-overlay { padding: 0; align-items: stretch; }
  .cmdk {
    width: 100%; height: 100dvh; max-height: 100dvh; border-radius: 0;
    display: flex; flex-direction: column;
    padding-top: env(safe-area-inset-top, 0px);
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  .cmdk-overlay[data-abierto="true"] .cmdk { transform: none; }
  .cmdk__resultados { flex: 1; max-height: none; }
}

/* ==========================================================================
   ESTADO DE FUENTES (integraciones, panel/tarjeta)
   ========================================================================== */
.fuente-fila {
  display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-3) 0;
  border-bottom: 1px solid var(--border);
}
.fuente-fila:last-child { border-bottom: none; }
.fuente-fila__info { flex: 1; min-width: 0; }
.fuente-fila__nombre { font-family: var(--f-d); font-weight: 600; font-size: 14px; }
.fuente-fila__detalle { font-size: 12px; color: var(--text-2); }

/* ==========================================================================
   AUTENTICACIÓN — login / invitación (pantallas standalone)
   ========================================================================== */
.auth-pagina {
  min-height: 100dvh; display: flex; align-items: center; justify-content: center;
  padding: var(--sp-5); position: relative; overflow: hidden;
  /* La trama de puntos (issue "espacio muerto" del informe) rellena de
     textura el fondo en pantallas anchas, donde antes solo quedaba un
     degradado sutil casi plano a los lados de la caja de 420px — mismo
     --border ya usado en el resto del sistema, sin ningún hue nuevo. */
  background:
    radial-gradient(ellipse at 20% -10%, rgba(68,212,228,.16) 0%, transparent 55%),
    radial-gradient(ellipse at 100% 100%, rgba(68,212,228,.08) 0%, transparent 50%),
    radial-gradient(circle, var(--border) 1px, transparent 1px) 0 0/28px 28px,
    var(--bg);
}
.auth-caja {
  width: min(420px, 100%); background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); padding: var(--sp-8);
  position: relative; z-index: 1;
}
.auth-marca { display: flex; align-items: center; gap: var(--sp-3); margin-bottom: var(--sp-6); }
.auth-marca__logo { width: 36px; height: 36px; flex: none; border-radius: 50%; object-fit: cover; }
.auth-marca__texto { font-family: var(--f-d); font-weight: 800; font-size: 16px; }
.auth-marca__sub { font-size: 12px; color: var(--text-2); }
.auth-titulo { font-size: 22px; margin-bottom: var(--sp-2); }
.auth-subtitulo { color: var(--text-2); font-size: 14px; margin-bottom: var(--sp-6); }
.auth-pie { text-align: center; margin-top: var(--sp-5); font-size: 13px; color: var(--text-2); }
.auth-pie a { color: var(--accent); font-weight: 600; }
.auth-pie a:hover { text-decoration: underline; }

.auth-alerta {
  display: flex; gap: var(--sp-2); align-items: flex-start;
  padding: var(--sp-3); border-radius: var(--radius-sm); font-size: 13px; margin-bottom: var(--sp-4);
}
.auth-alerta[data-tipo="error"] { background: var(--neg-bg); color: var(--neg); }
.auth-alerta[data-tipo="aviso"] { background: var(--warn-bg); color: var(--warn); }
.auth-alerta[data-tipo="info"] { background: var(--neutro-bg); color: var(--text-2); }
.auth-alerta svg { width: 17px; height: 17px; flex: none; margin-top: 1px; }

.tema-toggle {
  position: fixed; top: var(--sp-4); right: var(--sp-4); z-index: 5;
}

/* Códigos de recuperación */
.codigos-recuperacion {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--sp-2);
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-md);
  padding: var(--sp-4); margin: var(--sp-4) 0;
}
.codigos-recuperacion__codigo {
  font-family: var(--f-d); font-weight: 700; font-size: 14px; letter-spacing: .03em;
  padding: var(--sp-2); text-align: center; background: var(--surface); border-radius: var(--radius-sm);
}

.qr-caja {
  display: flex; justify-content: center; padding: var(--sp-4);
  background: #fff; border-radius: var(--radius-md); margin: var(--sp-4) 0;
}

/* ==========================================================================
   UTILIDADES
   ========================================================================== */
.flex { display: flex; }
.flex-col { flex-direction: column; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }
.gap-1 { gap: var(--sp-1); } .gap-2 { gap: var(--sp-2); } .gap-3 { gap: var(--sp-3); } .gap-4 { gap: var(--sp-4); }
.w-full { width: 100%; }
/* .text-3 (issue Ronda 1 #2): en la práctica los módulos (mod-dashboard.js,
   mod-geo.js, mod-integraciones.js...) usan esta utilidad como "texto
   secundario apagado" para prosa real que el usuario tiene que leer
   (mensajes de error, "falta conectar esta fuente", confianza de la
   muestra...), no como decoración pura — y --text-3 nunca pasa 4.5:1 para
   texto. En vez de perseguir cada uno de esos usos en ficheros que no son
   míos en esta ronda, la utilidad .text-3 se alinea aquí al color legible de
   --text-2; la variable --text-3 en sí sigue disponible sin tocar para los
   usos puramente decorativos (iconos, puntos de estado) que si siguen
   declarados con var(--text-3) directamente en sus selectores. Ver informe
   de Ronda 1 para la lista de líneas en mod-*.js que deberían migrar a la
   clase .text-2 cuando se toquen esos ficheros. */
.text-2 { color: var(--text-2); } .text-3 { color: var(--text-2); }
.text-center { text-align: center; }
.mt-2 { margin-top: var(--sp-2); } .mt-3 { margin-top: var(--sp-3); } .mt-4 { margin-top: var(--sp-4); }
.campo--centrado { align-items: center; }
.oculto { display: none !important; }
[hidden] { display: none !important; }

@media (max-width: 375px) {
  .auth-caja { padding: var(--sp-5); }
}

/* Gráfica + su tabla accesible equivalente (graficas.js → conTablaAccesible) */
.grafica-envoltura { display: flex; flex-direction: column; gap: var(--sp-3); }
.grafica-tabla-accesible summary { cursor: pointer; list-style: none; width: fit-content; }
.grafica-tabla-accesible summary::-webkit-details-marker { display: none; }
.grafica-tabla-accesible[open] summary { margin-bottom: var(--sp-2); }
